NavigationFrame.Avalonia
2.0.9
dotnet add package NavigationFrame.Avalonia --version 2.0.9
NuGet\Install-Package NavigationFrame.Avalonia -Version 2.0.9
<PackageReference Include="NavigationFrame.Avalonia" Version="2.0.9" />
<PackageVersion Include="NavigationFrame.Avalonia" Version="2.0.9" />
<PackageReference Include="NavigationFrame.Avalonia" />
paket add NavigationFrame.Avalonia --version 2.0.9
#r "nuget: NavigationFrame.Avalonia, 2.0.9"
#:package NavigationFrame.Avalonia@2.0.9
#addin nuget:?package=NavigationFrame.Avalonia&version=2.0.9
#tool nuget:?package=NavigationFrame.Avalonia&version=2.0.9
NavigationFrame.Avalonia
A modern, flexible, and Source Generator powered navigation framework for Avalonia applications. Inspired by Blazor and ASP.NET Core, it brings strongly-typed routing, powerful layout systems, automatic ViewModel wiring, and a robust authorization system to your desktop apps.
✨ Key Features
- 🚀 Zero Runtime Reflection: Routes and View factories are generated at compile-time for maximum performance and type safety.
- 🛡️ Strongly Typed Navigation: No more magic strings. Navigate using objects like
new ProductRoute(123). - 🧩 Advanced Layout System: First-class support for nested layouts (e.g.,
Master -> Settings -> Profile). - 🧠 Smart ViewModel Inference: Automatically locates and wires up ViewModels based on naming conventions.
- 🔐 Built-in Authorization: ASP.NET Core-style authorization with Policies, Roles, and UI helpers (
AuthorizeView). - ⚡ Rich Lifecycle: Granular control over page events (
IPreloadable,INavigatingFrom,INavigatedTo). - 🎨 Built-in Transitions: Slide, Fade, Zoom, and custom animations supported out of the box.
- 🛠️ Roslyn Analyzers: Catch common errors (deadlocks, missing ViewModels) at compile time.
📑 Table of Contents
- Getting Started
- Routing & Navigation
- Layout System
- ViewModel & Lifecycle
- Authorization System
- Advanced Features
- Diagnostics
🚀 Getting Started
1. Setup Dependency Injection
Register the core services in your App.axaml.cs or DI configuration:
using NavigationFrame.Avalonia;
using Microsoft.Extensions.DependencyInjection;
public void ConfigureServices(IServiceCollection services)
{
// 1. Register the ViewFactory (Generated)
services.AddSingleton<IViewFactory, ViewFactory>();
// 2. Register NavigationService
// Standard service: Global navigation
services.AddSingleton<INavigationService, NavigationService>();
// OR Scoped service: Treats pages without layouts as scopes
// services.AddSingleton<INavigationService, ScopedNavigationService>();
// 3. Register your ViewModels
services.AddTransient<HomeViewModel>();
services.AddTransient<MainLayoutViewModel>();
// 4. (Optional) Register Authorization
// services.AddSingleton<IAuthenticationStateProvider, ...>();
}
2. Implement ViewFactory
Create a partial class marked with [ViewFactory]. The Source Generator will fill in the ResolveView logic automatically.
using NavigationFrame.Avalonia;
namespace MyApp.Services;
[ViewFactory]
public partial class ViewFactory : IViewFactory;
3. Initialize Navigation Host
In your main window, bind the NavigationService.Content to a content presenter.
MainWindow.axaml:
<Window ...>
<Panel>
<ContentControl Content="{Binding NavigationService.Content}" />
<ProgressBar IsIndeterminate="True"
IsVisible="{Binding NavigationService.IsNavigating}"
VerticalAlignment="Top"/>
</Panel>
</Window>
MainWindowViewModel.cs:
public class MainWindowViewModel : ViewModelBase
{
public INavigationService NavigationService { get; }
public MainWindowViewModel(INavigationService navService)
{
NavigationService = navService;
// Setup default behaviors
navService.HandleBackwardInput = true; // Alt+Left or MouseBack
navService.HandleRefreshInput = true; // F5
// Navigate to start page
_ = navService.NavigateAsync(new HomeRoute());
}
}
🧭 Routing & Navigation
Defining Routes
Define a record for each page and mark it with [Route<TPage>].
// Simple Route
[Route<HomePage>]
public partial record HomeRoute;
// Route with Layout
[Route<SettingsPage, MainLayout>]
public partial record SettingsRoute;
Navigation Methods
Inject INavigationService into your ViewModels:
// Push a new page
await navigator.NavigateAsync(new SettingsRoute());
// Go back
await navigator.GoBackAsync();
// Go forward
await navigator.GoForwardAsync();
// Refresh (triggers IPreloadable again)
await navigator.RefreshAsync();
// Go to an existing page in the stack (or create if not found)
await navigator.GoToAsync(new HomeRoute());
Passing Parameters
Since routes are just objects, pass parameters via the constructor.
[Route<ProductPage>]
public partial record ProductRoute(int ProductId, string Category);
// Usage
await navigator.NavigateAsync(new ProductRoute(42, "Electronics"));
In your ProductViewModel:
public async Task PreloadAsync(IRoute route, NavigationMode mode, CancellationToken token)
{
if (route is ProductRoute p)
{
await LoadProduct(p.ProductId);
}
}
🧩 Layout System
Layouts are wrapper controls that provide a consistent UI structure (headers, sidebars, etc.).
Creating Layouts
- Create a UserControl.
- Implement
IMountableControl. - Mark with
[Layout].
[Layout]
public partial class MainLayout : UserControl, IMountableControl
{
// Return the container where the page content should be placed
public Control GetMountPoint() => this.FindControl<ContentControl>("Body");
}
Nested Layouts
Layouts can be nested! Just specify the parent layout in the attribute.
// AuthLayout will be rendered INSIDE MainLayout
[Layout<MainLayout>]
public partial class AuthLayout : UserControl, IMountableControl
{
public Control GetMountPoint() => this.FindControl<ContentControl>("InnerBody");
}
// Page uses AuthLayout (which uses MainLayout)
[Route<LoginPage, AuthLayout>]
public partial record LoginRoute;
🧠 ViewModel & Lifecycle
ViewModel Inference
The framework automatically finds ViewModels for your Views using these strategies (in order):
Namespace.HomePage→Namespace.HomeViewModelNamespace.Views.HomePage→Namespace.ViewModels.HomeViewModelNamespace.HomePage→Namespace.ViewModels.HomeViewModel- Global Search: Any class named
HomeViewModelin the assembly.
Manual Override:
[Route<MyPage>(DataContext = typeof(CustomViewModel))]
public partial record MyRoute;
Inherit Parent Context:
[Route<MyPage>(InheritContext = true)]
public partial record MyRoute;
Lifecycle Interfaces
Implement these interfaces in your ViewModel to hook into navigation events.
| Interface | Method | Usage |
|---|---|---|
IRequireNavigator |
SetNavigator |
Get the navigator instance. |
INavigatingFrom |
OnNavigatingFromAsync |
Guard. Return false to cancel navigation (e.g., "Unsaved Changes"). |
IPreloadable |
PreloadAsync |
Background Thread. Load data before the page is shown. |
INavigatedTo |
OnNavigatedToAsync |
Called when page becomes active. |
INavigatedFrom |
OnNavigatedFromAsync |
Called when page is no longer active (paused). |
IReleaseAware |
OnReleased |
Cleanup resources when page is removed from stack. |
Data Loading Patterns
Use IPreloadable for heavy lifting to keep the UI responsive.
public class ProductViewModel : ViewModelBase, IPreloadable, INavigatedTo
{
private ProductData? _data;
// Runs in parallel with the exit animation of the previous page
public async Task PreloadAsync(IRoute route, NavigationMode mode, CancellationToken token)
{
_data = await _service.FetchDataAsync(token);
}
public async Task OnNavigatedToAsync(IRoute route, Task preload, NavigationMode mode)
{
await preload; // Ensure loading finished (and propagate exceptions)
Products = _data; // Update UI properties
}
}
🔐 Authorization System
NavigationFrame includes a full-featured, policy-based authorization system inspired by ASP.NET Core.
Setup Authorization
// 1. Implement IAuthenticationStateProvider
services.AddSingleton<AppAuthenticationStateProvider>();
services.AddSingleton<IAuthenticationStateProvider>(sp => sp.GetRequiredService<AppAuthenticationStateProvider>());
// 2. Configure Options
var authOptions = new AuthorizationOptions();
authOptions.AddPolicy("CanEdit", policy => policy.RequireRole("Admin"));
services.AddSingleton(authOptions);
// 3. Register Service
services.AddSingleton<IAuthorizationService, AuthorizationService>();
Route Protection
Decorate your route records with [Authorize].
[Route<AdminPage>]
[Authorize(Roles = "Admin", Policy = "CanEdit")]
public partial record AdminRoute;
If a user tries to navigate here without permission, UnauthorizedNavigationException is thrown (catch this globally to redirect to Login).
UI Protection
Use AuthorizeView in your XAML to show/hide content based on permissions.
<nav:AuthorizeView Policy="CanEdit">
<nav:AuthorizeView.Authorized>
<Button Content="Edit Item" />
</nav:AuthorizeView.Authorized>
<nav:AuthorizeView.NotAuthorized>
<TextBlock Text="Read Only Mode" />
</nav:AuthorizeView.NotAuthorized>
</nav:AuthorizeView>
⚡ Advanced Features
Transitions
Control animations via NavOptions.
var options = new NavOptions
{
Animation = PageAnimations.SlideLeft, // Built-in: Slide, Fade, Zoom, Dissolve
// OR custom: new CompositeTransition(...)
};
await navigator.NavigateAsync(new DetailsRoute(), options);
Stack Manipulation
StackBehavior.Push: Standard history.StackBehavior.Clear: Wipes history (e.g., after Login).StackBehavior.Replace: Replaces current page.IsEphemeral: Page won't be saved in history (skip over it when going back).
Smart Progress Indicator
The NavigationService.IsNavigating property is smart. It respects IndicatorShowDelay (default 500ms).
- Fast navigation (<500ms): Indicator never shows (no flicker).
- Slow navigation: Indicator shows and stays for at least
IndicatorHideDelayto prevent jarring "flash".
🛠️ Diagnostics
We ship with Roslyn Analyzers to keep your code healthy:
- NAV001: Ensure Layouts inherit from
Control. - NAV002/003: ViewModel inference status (missing or inferred).
- NAV005: Critical. Detects deadlocks if you
awaitnavigation inside lifecycle callbacks. - NAV008: Ensures Route classes are
partialand notabstract.
💡 Best Practices
- Avoid Deadlocks: Never
awaitnavigation methods insideOnNavigatingFromAsync. If you need to redirect, usenavigator.Post(NavigationMode.GoTo, ...)or returnfalseand handle it separately. - Use Records: Always use
recordfor Routes. They provide value-based equality which is crucial forGoToAsyncand stack matching. - DI Lifecycle: The framework relies on your DI container. If you use
AddTransient, a new ViewModel is created for each page visit. IfAddSingleton, state persists.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Avalonia (>= 11.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
-
net8.0
- Avalonia (>= 11.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
-
net9.0
- Avalonia (>= 11.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 2.0.7 | 120 | 12/31/2025 | |
| 2.0.6 | 116 | 12/31/2025 | |
| 2.0.5 | 121 | 12/30/2025 | |
| 2.0.4 | 114 | 12/29/2025 | |
| 2.0.3 | 116 | 12/28/2025 | |
| 2.0.2 | 119 | 12/28/2025 | |
| 2.0.0 | 131 | 12/27/2025 | |
| 1.9.9 | 205 | 12/24/2025 | |
| 1.9.8.1 | 191 | 12/22/2025 | |
| 1.9.8 | 270 | 12/22/2025 | |
| 1.9.6 | 151 | 12/20/2025 | |
| 1.9.0 | 246 | 12/14/2025 | |
| 1.7.0 | 229 | 12/12/2025 | |
| 1.6.0 | 534 | 12/10/2025 | |
| 1.5.1 | 450 | 12/8/2025 | |
| 1.5.0 | 441 | 12/8/2025 | |
| 1.4.0 | 565 | 12/7/2025 |