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 |
| Platform | Status | Package / Sample |
|---|---|---|
| Avalonia | Stable | AsyncNavigation.Avalonia |
| WPF | Stable | AsyncNavigation.Wpf |
| WinUI 3 | In development | Sample.WinUI |
Warning
WinUI 3 support is under active development. It includes content, items, tab, NavigationView, dialog, window, and indicator support, but its APIs and behavior may still change. Test it carefully before using it in production.
# Avalonia
dotnet add package AsyncNavigation.Avalonia
# WPF
dotnet add package AsyncNavigation.WpfWinUI 3 is currently available from source and through the repository sample while development continues.
services.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