From 1e4fe2ab07f230b58eaab49337b43200d69dc32f Mon Sep 17 00:00:00 2001 From: Elliot Gawthrop <41838907+elliot-gawthrop@users.noreply.github.com> Date: Sat, 1 Feb 2025 22:05:10 +0000 Subject: [PATCH] Invoke INavigationAware callbacks on UI thread (#1307) --- .../Wpf.Ui.Demo.Mvvm/ViewModels/ViewModel.cs | 28 ++++--------------- src/Wpf.Ui.Gallery/ViewModels/ViewModel.cs | 28 ++++--------------- .../NavigationViewContentPresenter.cs | 13 ++++++--- 3 files changed, 21 insertions(+), 48 deletions(-) diff --git a/samples/Wpf.Ui.Demo.Mvvm/ViewModels/ViewModel.cs b/samples/Wpf.Ui.Demo.Mvvm/ViewModels/ViewModel.cs index 3fbc133ab..b96b71e2f 100644 --- a/samples/Wpf.Ui.Demo.Mvvm/ViewModels/ViewModel.cs +++ b/samples/Wpf.Ui.Demo.Mvvm/ViewModels/ViewModel.cs @@ -10,11 +10,11 @@ namespace Wpf.Ui.Demo.Mvvm.ViewModels; public abstract class ViewModel : ObservableObject, INavigationAware { /// - public virtual async Task OnNavigatedToAsync() + public virtual Task OnNavigatedToAsync() { - using CancellationTokenSource cts = new(); + OnNavigatedTo(); - await DispatchAsync(OnNavigatedTo, cts.Token); + return Task.CompletedTask; } /// @@ -24,11 +24,11 @@ public virtual async Task OnNavigatedToAsync() public virtual void OnNavigatedTo() { } /// - public virtual async Task OnNavigatedFromAsync() + public virtual Task OnNavigatedFromAsync() { - using CancellationTokenSource cts = new(); + OnNavigatedFrom(); - await DispatchAsync(OnNavigatedFrom, cts.Token); + return Task.CompletedTask; } /// @@ -36,20 +36,4 @@ public virtual async Task OnNavigatedFromAsync() /// // ReSharper disable once MemberCanBeProtected.Global public virtual void OnNavigatedFrom() { } - - /// - /// Dispatches the specified action on the UI thread. - /// - /// The action to be dispatched. - /// A cancellation token that can be used to cancel the operation. - /// A task that represents the asynchronous operation. - protected static async Task DispatchAsync(Action action, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - await Application.Current.Dispatcher.InvokeAsync(action); - } } diff --git a/src/Wpf.Ui.Gallery/ViewModels/ViewModel.cs b/src/Wpf.Ui.Gallery/ViewModels/ViewModel.cs index dcb66a09f..d82d5a5e3 100644 --- a/src/Wpf.Ui.Gallery/ViewModels/ViewModel.cs +++ b/src/Wpf.Ui.Gallery/ViewModels/ViewModel.cs @@ -8,11 +8,11 @@ namespace Wpf.Ui.Gallery.ViewModels; public abstract partial class ViewModel : ObservableObject, INavigationAware { /// - public virtual async Task OnNavigatedToAsync() + public virtual Task OnNavigatedToAsync() { - using CancellationTokenSource cts = new(); + OnNavigatedTo(); - await DispatchAsync(OnNavigatedTo, cts.Token); + return Task.CompletedTask; } /// @@ -22,11 +22,11 @@ public virtual async Task OnNavigatedToAsync() public virtual void OnNavigatedTo() { } /// - public virtual async Task OnNavigatedFromAsync() + public virtual Task OnNavigatedFromAsync() { - using CancellationTokenSource cts = new(); + OnNavigatedFrom(); - await DispatchAsync(OnNavigatedFrom, cts.Token); + return Task.CompletedTask; } /// @@ -34,20 +34,4 @@ public virtual async Task OnNavigatedFromAsync() /// // ReSharper disable once MemberCanBeProtected.Global public virtual void OnNavigatedFrom() { } - - /// - /// Dispatches the specified action on the UI thread. - /// - /// The action to be dispatched. - /// A cancellation token that can be used to cancel the operation. - /// A task that represents the asynchronous operation. - protected static async Task DispatchAsync(Action action, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - await Application.Current.Dispatcher.InvokeAsync(action); - } } diff --git a/src/Wpf.Ui/Controls/NavigationView/NavigationViewContentPresenter.cs b/src/Wpf.Ui/Controls/NavigationView/NavigationViewContentPresenter.cs index 23595cfaf..3d4b2a1ac 100644 --- a/src/Wpf.Ui/Controls/NavigationView/NavigationViewContentPresenter.cs +++ b/src/Wpf.Ui/Controls/NavigationView/NavigationViewContentPresenter.cs @@ -211,27 +211,32 @@ private static void NotifyContentAboutNavigatingFrom(object content) )] private static void NotifyContentAboutNavigating(object content, Func function) { + async void PerformNotify(INavigationAware navigationAware) + { + await function(navigationAware).ConfigureAwait(false); + } + switch (content) { // The order in which the OnNavigatedToAsync/OnNavigatedFromAsync methods of View and ViewModel are called // is not guaranteed case INavigationAware navigationAwareNavigationContent: - _ = Task.Run(() => function(navigationAwareNavigationContent)).ConfigureAwait(false); + PerformNotify(navigationAwareNavigationContent); if ( navigationAwareNavigationContent is FrameworkElement { DataContext: INavigationAware viewModel } && !ReferenceEquals(viewModel, navigationAwareNavigationContent) ) { - _ = Task.Run(() => function(viewModel)).ConfigureAwait(false); + PerformNotify(viewModel); } break; case INavigableView { ViewModel: INavigationAware navigationAwareNavigableViewViewModel }: - _ = Task.Run(() => function(navigationAwareNavigableViewViewModel)).ConfigureAwait(false); + PerformNotify(navigationAwareNavigableViewViewModel); break; case FrameworkElement { DataContext: INavigationAware navigationAwareCurrentContent }: - _ = Task.Run(() => function(navigationAwareCurrentContent)).ConfigureAwait(false); + PerformNotify(navigationAwareCurrentContent); break; } }