diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs index abefd47bcb4..ac36a02ae81 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs @@ -5,6 +5,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Documents; +using Flow.Launcher.Core.Resource; using Flow.Launcher.Infrastructure.Storage; using Flow.Launcher.Plugin; @@ -99,14 +100,14 @@ public void UpdateSettings(IReadOnlyDictionary settings) { case TextBox textBox: var text = value as string ?? string.Empty; - textBox.Dispatcher.Invoke(() => textBox.Text = text); + DispatcherHelper.Invoke(() => textBox.Text = text); break; case PasswordBox passwordBox: var password = value as string ?? string.Empty; - passwordBox.Dispatcher.Invoke(() => passwordBox.Password = password); + DispatcherHelper.Invoke(() => passwordBox.Password = password); break; case ComboBox comboBox: - comboBox.Dispatcher.Invoke(() => comboBox.SelectedItem = value); + DispatcherHelper.Invoke(() => comboBox.SelectedItem = value); break; case CheckBox checkBox: var isChecked = value is bool boolValue @@ -114,7 +115,7 @@ public void UpdateSettings(IReadOnlyDictionary settings) // If can parse the default value to bool, use it, otherwise use false : value is string stringValue && bool.TryParse(stringValue, out var boolValueFromString) && boolValueFromString; - checkBox.Dispatcher.Invoke(() => checkBox.IsChecked = isChecked); + DispatcherHelper.Invoke(() => checkBox.IsChecked = isChecked); break; } } diff --git a/Flow.Launcher.Core/Resource/DispatcherHelper.cs b/Flow.Launcher.Core/Resource/DispatcherHelper.cs new file mode 100644 index 00000000000..8b213035185 --- /dev/null +++ b/Flow.Launcher.Core/Resource/DispatcherHelper.cs @@ -0,0 +1,102 @@ +using System; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Threading; + +namespace Flow.Launcher.Core.Resource; + +#pragma warning disable VSTHRD001 // Avoid legacy thread switching APIs + +public static class DispatcherHelper +{ + public static void Invoke(Action action, DispatcherPriority priority = DispatcherPriority.Normal) + { + Invoke(Application.Current?.Dispatcher, action, priority); + } + + public static T Invoke(Func func, DispatcherPriority priority = DispatcherPriority.Normal) + { + return Invoke(Application.Current?.Dispatcher, func, priority); + } + + public static void Invoke(Dispatcher dispatcher, Action action, DispatcherPriority priority = DispatcherPriority.Normal) + { + if (dispatcher == null) return; + if (dispatcher.CheckAccess()) + { + action(); + } + else + { + dispatcher.Invoke(action, priority); + } + } + + public static T Invoke(Dispatcher dispatcher, Func func, DispatcherPriority priority = DispatcherPriority.Normal) + { + if (dispatcher == null) return default; + if (dispatcher.CheckAccess()) + { + return func(); + } + else + { + return dispatcher.Invoke(func, priority); + } + } + + public static async Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal) + { + await InvokeAsync(Application.Current?.Dispatcher, action, priority); + } + + public static async Task InvokeAsync(Func func, DispatcherPriority priority = DispatcherPriority.Normal) + { + return await InvokeAsync(Application.Current?.Dispatcher, func, priority); + } + + public static async Task InvokeAsync(Func func, DispatcherPriority priority = DispatcherPriority.Normal) + { + await InvokeAsync(Application.Current?.Dispatcher, func, priority); + } + + public static async Task InvokeAsync(Dispatcher dispatcher, Action action, DispatcherPriority priority = DispatcherPriority.Normal) + { + if (dispatcher == null) return; + if (dispatcher.CheckAccess()) + { + action(); + } + else + { + await dispatcher.InvokeAsync(action, priority); + } + } + + public static async Task InvokeAsync(Dispatcher dispatcher, Func func, DispatcherPriority priority = DispatcherPriority.Normal) + { + if (dispatcher == null) return default; + if (dispatcher.CheckAccess()) + { + return func(); + } + else + { + return await dispatcher.InvokeAsync(func, priority); + } + } + + public static async Task InvokeAsync(Dispatcher dispatcher, Func func, DispatcherPriority priority = DispatcherPriority.Normal) + { + if (dispatcher == null) return; + if (dispatcher.CheckAccess()) + { + await func(); + } + else + { + var task = await dispatcher.InvokeAsync(func, priority); + await task; + } + } +} diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 241631f7c54..9fbf9ad00d4 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -592,7 +592,7 @@ private void SetResizeBoarderThickness(Thickness? effectMargin) /// public async Task RefreshFrameAsync() { - await Application.Current.Dispatcher.InvokeAsync(() => + await DispatcherHelper.InvokeAsync(() => { // Get the actual backdrop type and drop shadow effect settings var (backdropType, useDropShadowEffect) = GetActualValue(); @@ -619,7 +619,7 @@ await Application.Current.Dispatcher.InvokeAsync(() => /// public async Task SetBlurForWindowAsync() { - await Application.Current.Dispatcher.InvokeAsync(() => + await DispatcherHelper.InvokeAsync(() => { // Get the actual backdrop type and drop shadow effect settings var (backdropType, _) = GetActualValue(); diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index da11380b861..314a751b052 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -334,7 +334,7 @@ private static void AutoPluginUpdates() var timer = new PeriodicTimer(TimeSpan.FromHours(5)); await PluginInstaller.CheckForPluginUpdatesAsync((plugins) => { - Current.Dispatcher.Invoke(() => + DispatcherHelper.Invoke(() => { var pluginUpdateWindow = new PluginUpdateWindow(plugins); pluginUpdateWindow.ShowDialog(); @@ -345,7 +345,7 @@ await PluginInstaller.CheckForPluginUpdatesAsync((plugins) => // check updates on startup await PluginInstaller.CheckForPluginUpdatesAsync((plugins) => { - Current.Dispatcher.Invoke(() => + DispatcherHelper.Invoke(() => { var pluginUpdateWindow = new PluginUpdateWindow(plugins); pluginUpdateWindow.ShowDialog(); @@ -444,7 +444,7 @@ protected virtual void Dispose(bool disposing) { // Dispose needs to be called on the main Windows thread, // since some resources owned by the thread need to be disposed. - _mainWindow?.Dispatcher.Invoke(_mainWindow.Dispose); + DispatcherHelper.Invoke(_mainWindow?.Dispatcher, _mainWindow.Dispose); _mainVM?.Dispose(); DialogJump.Dispose(); _internationalization.Dispose(); diff --git a/Flow.Launcher/Helper/SingleInstance.cs b/Flow.Launcher/Helper/SingleInstance.cs index de2579b6290..45d76491df1 100644 --- a/Flow.Launcher/Helper/SingleInstance.cs +++ b/Flow.Launcher/Helper/SingleInstance.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using System.Windows; +using Flow.Launcher.Core.Resource; // http://blogs.microsoft.co.il/arik/2010/05/28/wpf-single-instance-application/ // modified to allow single instace restart @@ -100,7 +101,9 @@ private static async Task CreateRemoteServiceAsync(string channelName) await pipeServer.WaitForConnectionAsync(); // Do an asynchronous call to ActivateFirstInstance function - Application.Current?.Dispatcher.Invoke(ActivateFirstInstance); +#pragma warning disable VSTHRD103 // Call async methods when in an async method + DispatcherHelper.Invoke(ActivateFirstInstance); +#pragma warning restore VSTHRD103 // Call async methods when in an async method // Disconect client pipeServer.Disconnect(); diff --git a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs index fd04b3e88fc..32c5f4aea41 100644 --- a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs +++ b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs @@ -3,9 +3,9 @@ using System.IO; using System.Linq; using System.Threading; -using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; +using Flow.Launcher.Core.Resource; using Flow.Launcher.Infrastructure; using Microsoft.Win32; @@ -22,11 +22,11 @@ public static class WallpaperPathRetrieval public static Brush GetWallpaperBrush() { // Invoke the method on the UI thread - if (!Application.Current.Dispatcher.CheckAccess()) - { - return Application.Current.Dispatcher.Invoke(GetWallpaperBrush); - } + return DispatcherHelper.Invoke(GetWallpaperBrushCore); + } + private static Brush GetWallpaperBrushCore() + { try { var wallpaperPath = Win32Helper.GetWallpaperPath(); diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 60517db9736..9aa9724a08c 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -153,7 +153,7 @@ private void OnLoaded(object sender, RoutedEventArgs e) Localize.appUpdateButtonContent(), () => { - Application.Current.Dispatcher.Invoke(() => + DispatcherHelper.Invoke(() => { var releaseNotesWindow = new ReleaseNotesWindow(); releaseNotesWindow.Show(); @@ -225,7 +225,7 @@ private void OnLoaded(object sender, RoutedEventArgs e) { case nameof(MainViewModel.MainWindowVisibilityStatus): { - Dispatcher.Invoke(() => + DispatcherHelper.Invoke(() => { if (_viewModel.MainWindowVisibilityStatus) { @@ -269,7 +269,7 @@ private void OnLoaded(object sender, RoutedEventArgs e) { // QueryTextBox seems to be update with a DispatcherPriority as low as ContextIdle. // To ensure QueryTextBox is up to date with QueryText from the View, we need to Dispatch with such a priority - Dispatcher.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length); + DispatcherHelper.Invoke(() => QueryTextBox.CaretIndex = QueryTextBox.Text.Length); _viewModel.QueryTextCursorMovedToEnd = false; } break; @@ -542,7 +542,7 @@ private void OnMouseDown(object sender, MouseButtonEventArgs e) // Switch to Normal state WindowState = WindowState.Normal; - Application.Current?.Dispatcher.Invoke(new Action(() => + DispatcherHelper.Invoke(() => { double normalWidth = Width; double normalHeight = Height; @@ -555,7 +555,7 @@ private void OnMouseDown(object sender, MouseButtonEventArgs e) { DragMove(); } - }), DispatcherPriority.ApplicationIdle); + }, DispatcherPriority.ApplicationIdle); } else { @@ -726,19 +726,8 @@ private void RegisterSoundEffectsEvent() { Win32Helper.RegisterSleepModeListener(() => { - if (Application.Current == null) - { - return; - } - // We must run InitSoundEffects on UI thread because MediaPlayer is a DispatcherObject - if (!Application.Current.Dispatcher.CheckAccess()) - { - Application.Current.Dispatcher.Invoke(InitSoundEffects); - return; - } - - InitSoundEffects(); + DispatcherHelper.Invoke(InitSoundEffects); }); } catch (Exception e) diff --git a/Flow.Launcher/MessageBoxEx.xaml.cs b/Flow.Launcher/MessageBoxEx.xaml.cs index 907bfb926d5..14bda4c0ff8 100644 --- a/Flow.Launcher/MessageBoxEx.xaml.cs +++ b/Flow.Launcher/MessageBoxEx.xaml.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Input; +using Flow.Launcher.Core.Resource; using Flow.Launcher.Infrastructure; namespace Flow.Launcher @@ -29,11 +30,16 @@ public static MessageBoxResult Show( MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.OK) { - if (!Application.Current.Dispatcher.CheckAccess()) - { - return Application.Current.Dispatcher.Invoke(() => Show(messageBoxText, caption, button, icon, defaultResult)); - } + return DispatcherHelper.Invoke(() => ShowCore(messageBoxText, caption, button, icon, defaultResult)); + } + private static MessageBoxResult ShowCore( + string messageBoxText, + string caption = "", + MessageBoxButton button = MessageBoxButton.OK, + MessageBoxImage icon = MessageBoxImage.None, + MessageBoxResult defaultResult = MessageBoxResult.OK) + { try { msgBox = new MessageBoxEx(button); diff --git a/Flow.Launcher/Msg.xaml.cs b/Flow.Launcher/Msg.xaml.cs index dd7d4495c5b..5e47d01a175 100644 --- a/Flow.Launcher/Msg.xaml.cs +++ b/Flow.Launcher/Msg.xaml.cs @@ -3,6 +3,7 @@ using System.Windows; using System.Windows.Input; using System.Windows.Media.Animation; +using Flow.Launcher.Core.Resource; using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin.SharedModels; @@ -80,12 +81,12 @@ public async void Show(string title, string subTitle, string iconPath) Show(); - await Dispatcher.InvokeAsync(async () => + await DispatcherHelper.InvokeAsync(async () => { if (!closing) { closing = true; - await Dispatcher.InvokeAsync(fadeOutStoryboard.Begin); + fadeOutStoryboard.Begin(); } }); } diff --git a/Flow.Launcher/MsgWithButton.xaml.cs b/Flow.Launcher/MsgWithButton.xaml.cs index 7ae53e6c599..8ee95bc579c 100644 --- a/Flow.Launcher/MsgWithButton.xaml.cs +++ b/Flow.Launcher/MsgWithButton.xaml.cs @@ -3,6 +3,7 @@ using System.Windows; using System.Windows.Input; using System.Windows.Media.Animation; +using Flow.Launcher.Core.Resource; using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin.SharedModels; @@ -82,12 +83,12 @@ public async void Show(string title, string buttonText, Action buttonAction, str Show(); - await Dispatcher.InvokeAsync(async () => + await DispatcherHelper.InvokeAsync(async () => { if (!closing) { closing = true; - await Dispatcher.InvokeAsync(fadeOutStoryboard.Begin); + fadeOutStoryboard.Begin(); } }); } diff --git a/Flow.Launcher/Notification.cs b/Flow.Launcher/Notification.cs index deb5442a4b1..aeac09579d4 100644 --- a/Flow.Launcher/Notification.cs +++ b/Flow.Launcher/Notification.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Concurrent; using System.IO; -using System.Windows; +using Flow.Launcher.Core.Resource; using Flow.Launcher.Infrastructure; using Microsoft.Toolkit.Uwp.Notifications; @@ -41,7 +41,7 @@ internal static void Uninstall() public static void Show(string title, string subTitle, string iconPath = null) { - Application.Current.Dispatcher.Invoke(() => + DispatcherHelper.Invoke(() => { ShowInternal(title, subTitle, iconPath); }); @@ -91,7 +91,7 @@ private static void LegacyShow(string title, string subTitle, string iconPath) public static void ShowWithButton(string title, string buttonText, Action buttonAction, string subTitle, string iconPath = null) { - Application.Current.Dispatcher.Invoke(() => + DispatcherHelper.Invoke(() => { ShowInternalWithButton(title, buttonText, buttonAction, subTitle, iconPath); }); diff --git a/Flow.Launcher/ProgressBoxEx.xaml.cs b/Flow.Launcher/ProgressBoxEx.xaml.cs index 11946334869..cc75b0e2636 100644 --- a/Flow.Launcher/ProgressBoxEx.xaml.cs +++ b/Flow.Launcher/ProgressBoxEx.xaml.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Input; +using Flow.Launcher.Core.Resource; namespace Flow.Launcher { @@ -22,19 +23,7 @@ public static async Task ShowAsync(string caption, Func, Task> re ProgressBoxEx progressBox = null; try { - if (!Application.Current.Dispatcher.CheckAccess()) - { - await Application.Current.Dispatcher.InvokeAsync(() => - { - progressBox = new ProgressBoxEx(cancelProgress) - { - Title = caption - }; - progressBox.TitleTextBlock.Text = caption; - progressBox.Show(); - }); - } - else + await DispatcherHelper.InvokeAsync(() => { progressBox = new ProgressBoxEx(cancelProgress) { @@ -42,7 +31,7 @@ await Application.Current.Dispatcher.InvokeAsync(() => }; progressBox.TitleTextBlock.Text = caption; progressBox.Show(); - } + }); await reportProgressAsync(progressBox.ReportProgress).ConfigureAwait(false); } @@ -54,28 +43,20 @@ await Application.Current.Dispatcher.InvokeAsync(() => } finally { - if (!Application.Current.Dispatcher.CheckAccess()) - { - await Application.Current.Dispatcher.InvokeAsync(() => - { - progressBox?.Close(); - }); - } - else + await DispatcherHelper.InvokeAsync(() => { progressBox?.Close(); - } + }); } } private void ReportProgress(double progress) { - if (!Application.Current.Dispatcher.CheckAccess()) - { - Application.Current.Dispatcher.Invoke(() => ReportProgress(progress)); - return; - } + DispatcherHelper.Invoke(() => ReportProgressCore(progress)); + } + private void ReportProgressCore(double progress) + { if (progress < 0) { ProgressBar.Value = 0; diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index b939cfb5a5e..969bb485d7d 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -142,7 +142,7 @@ public void ShowMsgWithButton(string title, string buttonText, Action buttonActi public void OpenSettingDialog() { - Application.Current.Dispatcher.Invoke(() => + DispatcherHelper.Invoke(() => { SettingWindow sw = SingletonWindowOpener.Open(); }); @@ -150,7 +150,7 @@ public void OpenSettingDialog() public bool OpenPluginSettingsWindow(string pluginId) { - return Application.Current.Dispatcher.Invoke(() => + return DispatcherHelper.Invoke(() => { try { diff --git a/Flow.Launcher/ReleaseNotesWindow.xaml.cs b/Flow.Launcher/ReleaseNotesWindow.xaml.cs index 12e0aa1d8c6..5d5f73abaec 100644 --- a/Flow.Launcher/ReleaseNotesWindow.xaml.cs +++ b/Flow.Launcher/ReleaseNotesWindow.xaml.cs @@ -9,6 +9,7 @@ using System.Windows; using System.Windows.Input; using System.Windows.Media; +using Flow.Launcher.Core.Resource; using Flow.Launcher.Infrastructure.Http; using iNKORE.UI.WPF.Modern; @@ -28,7 +29,7 @@ public ReleaseNotesWindow() private void ThemeManager_ActualApplicationThemeChanged(ThemeManager sender, object args) { - Application.Current.Dispatcher.Invoke(() => + DispatcherHelper.Invoke(() => { if (ThemeManager.Current.ActualApplicationTheme == ApplicationTheme.Light) { @@ -124,7 +125,7 @@ private async void RefreshMarkdownViewer() { var output = await GetReleaseNotesMarkdownAsync().ConfigureAwait(false); - Application.Current.Dispatcher.Invoke(() => + DispatcherHelper.Invoke(() => { RefreshProgressRing.Visibility = Visibility.Collapsed; if (string.IsNullOrEmpty(output)) diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs index 752d6890338..d4b596c11a8 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs @@ -5,6 +5,7 @@ using System.Windows; using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Core.Plugin; +using Flow.Launcher.Core.Resource; using Flow.Launcher.Plugin; using Flow.Launcher.ViewModel; @@ -115,7 +116,7 @@ private async Task CheckPluginUpdatesAsync() { await PluginInstaller.CheckForPluginUpdatesAsync((plugins) => { - Application.Current.Dispatcher.Invoke(() => + DispatcherHelper.Invoke(() => { var pluginUpdateWindow = new PluginUpdateWindow(plugins); pluginUpdateWindow.ShowDialog(); diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 057fb4610a1..342acc2dd96 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -16,6 +16,7 @@ using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.Input; using Flow.Launcher.Core.Plugin; +using Flow.Launcher.Core.Resource; using Flow.Launcher.Helper; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.DialogJump; @@ -770,12 +771,11 @@ private void DecreaseMaxResult() public void ChangeQueryText(string queryText, bool isReQuery = false) { // Must check access so that we will not block the UI thread which causes window visibility issue - if (!Application.Current.Dispatcher.CheckAccess()) - { - Application.Current.Dispatcher.Invoke(() => ChangeQueryText(queryText, isReQuery)); - return; - } + DispatcherHelper.Invoke(() => ChangeQueryTextCore(queryText, isReQuery)); + } + private void ChangeQueryTextCore(string queryText, bool isReQuery = false) + { if (QueryText != queryText) { // Change query text first @@ -802,12 +802,11 @@ public void ChangeQueryText(string queryText, bool isReQuery = false) private async Task ChangeQueryTextAsync(string queryText, bool isReQuery = false) { // Must check access so that we will not block the UI thread which causes window visibility issue - if (!Application.Current.Dispatcher.CheckAccess()) - { - await Application.Current.Dispatcher.InvokeAsync(() => ChangeQueryTextAsync(queryText, isReQuery)); - return; - } + await DispatcherHelper.InvokeAsync(() => ChangeQueryTextCoreAsync(queryText, isReQuery)); + } + private async Task ChangeQueryTextCoreAsync(string queryText, bool isReQuery = false) + { if (QueryText != queryText) { // Change query text first @@ -1979,10 +1978,12 @@ public async Task SetupDialogJumpAsync(nint handle) if (dialogWindowHandleChanged) { // Only update the position - Application.Current?.Dispatcher.Invoke(() => +#pragma warning disable VSTHRD103 // Call async methods when in an async method + DispatcherHelper.Invoke(() => { (Application.Current?.MainWindow as MainWindow)?.UpdatePosition(); }); +#pragma warning restore VSTHRD103 // Call async methods when in an async method _ = ResetWindowAsync(); } @@ -2077,7 +2078,7 @@ public async void ResetDialogJump() if (_previousMainWindowVisibilityStatus) { // Only update the position - Application.Current?.Dispatcher.Invoke(() => + DispatcherHelper.Invoke(() => { (Application.Current?.MainWindow as MainWindow)?.UpdatePosition(); }); @@ -2140,7 +2141,7 @@ public void Show() if (App.LoadingOrExiting) return; // When application is exiting, the Application.Current will be null - Application.Current?.Dispatcher.Invoke(() => + DispatcherHelper.Invoke(() => { // When application is exiting, the Application.Current will be null if (Application.Current?.MainWindow is MainWindow mainWindow) @@ -2222,7 +2223,7 @@ public async void Hide(bool reset = true) } // When application is exiting, the Application.Current will be null - Application.Current?.Dispatcher.Invoke(() => + DispatcherHelper.Invoke(() => { // When application is exiting, the Application.Current will be null if (Application.Current?.MainWindow is MainWindow mainWindow) @@ -2356,7 +2357,7 @@ public void UpdateResultView(ICollection resultsForUpdates) public void FocusQueryTextBox() { // When application is exiting, the Application.Current will be null - Application.Current?.Dispatcher.Invoke(() => + DispatcherHelper.Invoke(() => { // When application is exiting, the Application.Current will be null if (Application.Current?.MainWindow is MainWindow window)