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