Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invoke INavigationAware callbacks on UI thread #1307

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 6 additions & 22 deletions samples/Wpf.Ui.Demo.Mvvm/ViewModels/ViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ namespace Wpf.Ui.Demo.Mvvm.ViewModels;
public abstract class ViewModel : ObservableObject, INavigationAware
{
/// <inheritdoc />
public virtual async Task OnNavigatedToAsync()
public virtual Task OnNavigatedToAsync()
{
using CancellationTokenSource cts = new();
OnNavigatedTo();

await DispatchAsync(OnNavigatedTo, cts.Token);
return Task.CompletedTask;
}

/// <summary>
Expand All @@ -24,32 +24,16 @@ public virtual async Task OnNavigatedToAsync()
public virtual void OnNavigatedTo() { }

/// <inheritdoc />
public virtual async Task OnNavigatedFromAsync()
public virtual Task OnNavigatedFromAsync()
{
using CancellationTokenSource cts = new();
OnNavigatedFrom();

await DispatchAsync(OnNavigatedFrom, cts.Token);
return Task.CompletedTask;
}

/// <summary>
/// Handles the event that is fired before the component is navigated from.
/// </summary>
// ReSharper disable once MemberCanBeProtected.Global
public virtual void OnNavigatedFrom() { }

/// <summary>
/// Dispatches the specified action on the UI thread.
/// </summary>
/// <param name="action">The action to be dispatched.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
protected static async Task DispatchAsync(Action action, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return;
}

await Application.Current.Dispatcher.InvokeAsync(action);
}
}
28 changes: 6 additions & 22 deletions src/Wpf.Ui.Gallery/ViewModels/ViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ namespace Wpf.Ui.Gallery.ViewModels;
public abstract partial class ViewModel : ObservableObject, INavigationAware
{
/// <inheritdoc />
public virtual async Task OnNavigatedToAsync()
public virtual Task OnNavigatedToAsync()
{
using CancellationTokenSource cts = new();
OnNavigatedTo();

await DispatchAsync(OnNavigatedTo, cts.Token);
return Task.CompletedTask;
}

/// <summary>
Expand All @@ -22,32 +22,16 @@ public virtual async Task OnNavigatedToAsync()
public virtual void OnNavigatedTo() { }

/// <inheritdoc />
public virtual async Task OnNavigatedFromAsync()
public virtual Task OnNavigatedFromAsync()
{
using CancellationTokenSource cts = new();
OnNavigatedFrom();

await DispatchAsync(OnNavigatedFrom, cts.Token);
return Task.CompletedTask;
}

/// <summary>
/// Handles the event that is fired before the component is navigated from.
/// </summary>
// ReSharper disable once MemberCanBeProtected.Global
public virtual void OnNavigatedFrom() { }

/// <summary>
/// Dispatches the specified action on the UI thread.
/// </summary>
/// <param name="action">The action to be dispatched.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
protected static async Task DispatchAsync(Action action, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return;
}

await Application.Current.Dispatcher.InvokeAsync(action);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -211,27 +211,32 @@ private static void NotifyContentAboutNavigatingFrom(object content)
)]
private static void NotifyContentAboutNavigating(object content, Func<INavigationAware, Task> 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<object> { 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;
}
}
Expand Down
Loading