Shell | Navigation
All navigation in Shiny Shell goes through the INavigator interface. Inject it into your ViewModels via constructor injection.
public class MyViewModel(INavigator navigator){ // navigator is ready to use}NavigateTo (Route)
Section titled “NavigateTo (Route)”Navigate to a registered route by name, optionally passing arguments as key-value tuples.
// Simple route navigationawait navigator.NavigateTo("details");
// With argumentsawait navigator.NavigateTo("details", ("Id", 42), ("Name", "Allan"));Arguments are received on the target ViewModel via IQueryAttributable.ApplyQueryAttributes.
NavigateTo (ViewModel)
Section titled “NavigateTo (ViewModel)”Navigate by ViewModel type with an optional configuration action for strongly-typed argument passing.
// Navigate to a ViewModelawait navigator.NavigateTo<DetailViewModel>();
// With strongly-typed property setupawait navigator.NavigateTo<DetailViewModel>(vm => vm.Id = 42);
// With additional route argumentsawait navigator.NavigateTo<DetailViewModel>( vm => vm.Id = 42, ("ExtraArg", "value"));Relative Navigation
Section titled “Relative Navigation”Both NavigateTo overloads accept a relativeNavigation parameter (default true). When set to false, the URI is prefixed with // to navigate from the root, replacing the navigation stack.
// Relative navigation (default) — pushes onto the stackawait navigator.NavigateTo("details");await navigator.NavigateTo<DetailViewModel>();
// Root navigation — resets to root and navigatesawait navigator.NavigateTo("dashboard", relativeNavigation: false);await navigator.NavigateTo<DashboardViewModel>(relativeNavigation: false);
// With configurationawait navigator.NavigateTo<DashboardViewModel>( vm => vm.WelcomeMessage = "Hello!", relativeNavigation: false);Navigation Builder
Section titled “Navigation Builder”The INavigationBuilder provides a fluent API for constructing multi-segment navigation URIs in a single call. Create one via INavigator.CreateBuilder().
// Push three pages in one navigation: ChainPage/DetailPage/ChainPageawait navigator .CreateBuilder() .Add<ChainViewModel>(x => x.Text = "Page1") .Add<DetailViewModel>(x => x.Id = 42) .Add<ChainViewModel>(x => x.Text = "Page3") .Navigate();
// Pop back 2 pages, then push a new page: ../../ChainPageawait navigator .CreateBuilder() .PopBack(2) .Add<ChainViewModel>(x => x.Text = "After Pop") .Navigate();
// Navigate from root: //ChainPage/DetailPageawait navigator .CreateBuilder(fromRoot: true) .Add<ChainViewModel>(x => x.Text = "Root Start") .Add<DetailViewModel>(x => x.Id = 1) .Navigate();
// Mix string routes and ViewModel-based segmentsawait navigator .CreateBuilder() .Add("settings") .Add<DetailViewModel>(x => x.Id = 99) .Navigate();| Method | Description |
|---|---|
CreateBuilder(bool fromRoot) | Creates a builder. fromRoot: true prefixes the URI with // |
PopBack(int count) | Adds .. pop segments. Must be called before any Add calls. Not valid with fromRoot: true |
Add<TViewModel>() | Adds a route segment for the ViewModel type |
Add<TViewModel>(configure) | Adds a route segment with a configure callback invoked when the page is created |
Add(string routeName) | Adds a raw route string segment |
Navigate() | Builds the URI and executes the navigation |
GoBack
Section titled “GoBack”Navigate back one or more pages, optionally passing arguments to the previous ViewModel.
// Simple go backawait navigator.GoBack();
// Go back with argumentsawait navigator.GoBack(("Result", "saved"), ("Timestamp", DateTime.UtcNow));
// Go back multiple pagesawait navigator.GoBack(2);
// Go back multiple pages with argumentsawait navigator.GoBack(2, ("Result", "saved"));PopToRoot
Section titled “PopToRoot”Pop the entire navigation stack back to the root page.
await navigator.PopToRoot();
// With argumentsawait navigator.PopToRoot(("Status", "complete"));SwitchShell
Section titled “SwitchShell”Replace the entire active Shell at runtime. Useful for switching between different app experiences (e.g. tabbed layout, flyout menu, onboarding flow).
// Switch to a new Shell instanceawait navigator.SwitchShell(new AdminShell());
// Switch to a Shell resolved from DIawait navigator.SwitchShell<AdminShell>();The current Shell’s ViewModel receives OnNavigatingFrom before the switch, and the Navigating event fires with NavigationType.SwitchShell.
Tab Badges
Section titled “Tab Badges”INavigator can set and clear numeric badges on tabs that already exist in the active Shell.
// By routeawait navigator.SetTabBadge("Inbox", 3);await navigator.ClearTabBadge("Inbox");
// By ViewModel mappingawait navigator.SetTabBadge<InboxViewModel>(7);await navigator.ClearTabBadge<InboxViewModel>();XAML Navigation
Section titled “XAML Navigation”For route-based navigation from XAML, use the Navigate attached properties instead of a ViewModel command.
<Button Text="Open Detail" shiny:Navigate.Route="Detail" shiny:Navigate.ParameterKey="ItemId" shiny:Navigate.ParameterValue="{Binding SelectedId}" />RelativeNavigation defaults to true. Set it to false for root navigation:
<ToolbarItem Text="Home" shiny:Navigate.Route="MainPage" shiny:Navigate.RelativeNavigation="False" />For multiple parameters:
<Button Text="Open Modal" shiny:Navigate.Route="modal"> <shiny:Navigate.Parameters> <shiny:NavigationParameters> <shiny:NavigationParameter Key="Arg1" Value="{Binding NavArg}" /> <shiny:NavigationParameter Key="Arg2" Value="5" /> </shiny:NavigationParameters> </shiny:Navigate.Parameters></Button>Navigation Events
Section titled “Navigation Events”INavigator exposes two events for observing navigation lifecycle:
Navigating (Pre-Navigation)
Section titled “Navigating (Pre-Navigation)”Fires before navigation occurs. Provides the source ViewModel instance and destination route.
navigator.Navigating += (sender, args) =>{ // args.FromUri — current location URI // args.FromViewModel — source ViewModel instance (object?) // args.ToUri — destination route URI // args.NavigationType — Push, SetRoot, GoBack, PopToRoot, or SwitchShell // args.Parameters — navigation parameters};| Property | Type | Description |
|---|---|---|
FromUri | string? | The current Shell location URI |
FromViewModel | object? | The source page’s ViewModel instance |
ToUri | string | The destination route URI |
NavigationType | NavigationType | Push, SetRoot, GoBack, PopToRoot, or SwitchShell |
Parameters | IReadOnlyDictionary<string, object> | Navigation parameters |
Navigated (Post-Navigation)
Section titled “Navigated (Post-Navigation)”Fires after navigation completes and the destination page’s ViewModel is resolved.
navigator.Navigated += (sender, args) =>{ // args.ToUri — destination route URI // args.ToViewModel — destination ViewModel instance (object?) // args.NavigationType — Push, SetRoot, GoBack, PopToRoot, or SwitchShell // args.Parameters — navigation parameters};| Property | Type | Description |
|---|---|---|
ToUri | string | The destination route URI |
ToViewModel | object? | The destination page’s ViewModel instance |
NavigationType | NavigationType | Push, SetRoot, GoBack, PopToRoot, or SwitchShell |
Parameters | IReadOnlyDictionary<string, object> | Navigation parameters |
Example: Navigation Logger
Section titled “Example: Navigation Logger”Use IMauiInitializeService to hook events for cross-cutting concerns like logging or analytics:
public class NavigationLogger( ILogger<NavigationLogger> logger, INavigator navigator) : IMauiInitializeService{ public void Initialize(IServiceProvider services) { navigator.Navigating += (_, args) => { logger.LogInformation( "Navigating from '{FromUri}' to '{ToUri}' | Type: {NavigationType} | FromViewModel: {FromViewModel}", args.FromUri, args.ToUri, args.NavigationType, args.FromViewModel?.GetType().Name ); };
navigator.Navigated += (_, args) => { logger.LogInformation( "Navigated to '{ToUri}' | Type: {NavigationType} | ToViewModel: {ToViewModel}", args.ToUri, args.NavigationType, args.ToViewModel?.GetType().Name ); }; }}
// Register in MauiProgram.csbuilder.Services.AddSingleton<IMauiInitializeService, NavigationLogger>();Passing Arguments
Section titled “Passing Arguments”Arguments passed during navigation are delivered via the MAUI built-in IQueryAttributable interface.
public class DetailViewModel : IQueryAttributable{ public int Id { get; set; }
public void ApplyQueryAttributes(IDictionary<string, object> query) { if (query.TryGetValue("Id", out var id)) Id = (int)id; }}This works for arguments passed via NavigateTo, GoBack, and PopToRoot.