A lightweight async navigation framework for .NET desktop apps, built on
Microsoft.Extensions.DependencyInjection.
| Async/Await Native | Navigation is fully async end-to-end with CancellationToken support |
| DI-First | Views and view models are resolved from the DI container |
| Navigation Guard | Block navigation away with INavigationGuard (e.g. unsaved changes) |
| Interceptors | Run cross-cutting logic (auth, analytics) via INavigationInterceptor |
| Dialog Service | Async dialog and window management built-in |
| Multiple Region Types | ContentControl, ItemsControl, and TabControl regions |
| History Navigation | GoForwardAsync / GoBackAsync out of the box |
| Lifecycle Management | Automatic view caching, eviction, and disposal — no memory leaks |
| Native AOT | Full Avalonia AOT / trimming support, zero extra config |
| Framework-Agnostic | Works with any MVVM framework |
| Minimal Deps | Only Microsoft.Extensions.DependencyInjection.Abstractions >= 8.0 |
# Avalonia
dotnet add package AsyncNavigation.Avalonia
# WPF
dotnet add package AsyncNavigation.Wpfservices.AddNavigationSupport()
.RegisterView<HomeView, HomeViewModel>("Home")
.RegisterView<SettingsView, SettingsViewModel>("Settings")
.RegisterDialog<ConfirmView, ConfirmViewModel>("Confirm");xmlns:an="https://github.com/NeverMorewd/AsyncNavigation"
<ContentControl an:RegionManager.RegionName="MainRegion" />// Navigate
await _regionManager.RequestNavigateAsync("MainRegion", "Home");
// History
await _regionManager.GoBackAsync("MainRegion");
await _regionManager.GoForwardAsync("MainRegion");
// Dialog
var result = await _dialogService.ShowViewDialogAsync("Confirm");// Derive from NavigationAwareBase and override only what you need.
public class HomeViewModel : NavigationAwareBase
{
public override async Task OnNavigatedToAsync(NavigationContext context)
{
await LoadDataAsync(context.CancellationToken);
}
}public class EditViewModel : NavigationAwareBase, INavigationGuard
{
public async Task<bool> CanNavigateAsync(NavigationContext context, CancellationToken ct)
{
// Return false to cancel — show a confirmation dialog here if needed.
return !HasUnsavedChanges;
}
}public class AuthInterceptor : INavigationInterceptor
{
public Task OnNavigatingAsync(NavigationContext context)
{
if (!_auth.IsLoggedIn)
throw new OperationCanceledException("Not authenticated.");
return Task.CompletedTask;
}
public Task OnNavigatedAsync(NavigationContext context) => Task.CompletedTask;
}
// Register
services.AddNavigationSupport()
.RegisterNavigationInterceptor<AuthInterceptor>();MIT