Plugin.Maui.NavigationAware 1.0.4

dotnet add package Plugin.Maui.NavigationAware --version 1.0.4
                    
NuGet\Install-Package Plugin.Maui.NavigationAware -Version 1.0.4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Plugin.Maui.NavigationAware" Version="1.0.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Plugin.Maui.NavigationAware" Version="1.0.4" />
                    
Directory.Packages.props
<PackageReference Include="Plugin.Maui.NavigationAware" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Plugin.Maui.NavigationAware --version 1.0.4
                    
#r "nuget: Plugin.Maui.NavigationAware, 1.0.4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Plugin.Maui.NavigationAware@1.0.4
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Plugin.Maui.NavigationAware&version=1.0.4
                    
Install as a Cake Addin
#tool nuget:?package=Plugin.Maui.NavigationAware&version=1.0.4
                    
Install as a Cake Tool

Plugin.Maui.NavigationAware

NuGet License: MIT

Plugin.Maui.NavigationAware provides navigation awareness for .NET MAUI applications, similar to Prism's INavigationAware interface. This plugin allows your pages to be notified when navigation events occur, enabling you to handle incoming and outgoing navigation with parameters.

Features

  • Navigation Awareness: Receive notifications when navigating to or from a page
  • Parameter Passing: Pass and receive strongly-typed parameters during navigation
  • ViewModel Locator: Automatic View-ViewModel binding with convention-based or explicit registration
  • String-Based Navigation: Navigate using page names/keys like Prism (e.g., NavigateToAsync("PageName"))
  • URI-Based Navigation: Navigate using URI paths (e.g., NavigateAsync("/NavigationPage/DetailPage"))
  • Instance-Based Navigation: Navigate using page instances (e.g., NavigateToAsync(new MyPage()))
  • Navigation Stack Control: Navigate back to specific pages or root with GoBackToAsync() and GoBackToRootAsync()
  • Prism-like API: Familiar interface for developers coming from Prism
  • Easy Integration: Simple base class or interface implementation
  • Cross-platform: Works on all .NET MAUI supported platforms (iOS, Android, macOS, Windows)
  • Zero Dependencies: No external dependencies beyond .NET MAUI

Installation

Install the package via NuGet:

dotnet add package Plugin.Maui.NavigationAware

Or via the NuGet Package Manager:

Install-Package Plugin.Maui.NavigationAware

Getting Started

Basic Usage

There are two ways to use this plugin:

Option 1: Inherit from NavigationAwarePage

The easiest way to use navigation awareness is to inherit from NavigationAwarePage:

using Plugin.Maui.NavigationAware;

public partial class MyPage : NavigationAwarePage
{
    public MyPage()
    {
        InitializeComponent();
    }

    public override void OnNavigatedTo(INavigationParameters parameters)
    {
        base.OnNavigatedTo(parameters);
        // Handle incoming navigation
        
        // Access parameters
        if (parameters.TryGetValue<string>("message", out var message))
        {
            // Use the message
            Console.WriteLine($"Received: {message}");
        }
    }

    public override void OnNavigatedFrom(INavigationParameters parameters)
    {
        base.OnNavigatedFrom(parameters);
        // Handle outgoing navigation
    }
}

XAML File:

<?xml version="1.0" encoding="utf-8" ?>
<local:NavigationAwarePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:Plugin.Maui.NavigationAware;assembly=Plugin.Maui.NavigationAware"
             x:Class="YourNamespace.MyPage"
             Title="My Page">
    
</local:NavigationAwarePage>
Option 2: Implement INavigationAware

Alternatively, you can implement the INavigationAware interface on any ContentPage:

using Plugin.Maui.NavigationAware;

public partial class MyPage : ContentPage, INavigationAware
{
    public MyPage()
    {
        InitializeComponent();
    }

    public void OnNavigatedTo(INavigationParameters parameters)
    {
        // Handle incoming navigation
    }

    public void OnNavigatedFrom(INavigationParameters parameters)
    {
        // Handle outgoing navigation
    }
}

Use the NavigationService to navigate with parameters:

private async void OnNavigateClicked(object sender, EventArgs e)
{
    // Get the navigation service from the current page
    var navigationService = this.GetNavigationService();
    
    // Create navigation parameters
    var parameters = new NavigationParameters
    {
        { "userId", 123 },
        { "message", "Hello from previous page!" },
        { "timestamp", DateTime.Now }
    };
    
    // Option 1: Navigate using page instance
    await navigationService.NavigateToAsync(new DetailsPage(), parameters);
    
    // Option 2: Navigate using string (requires page registration - see below)
    await navigationService.NavigateToAsync("DetailsPage", parameters);
    // or using nameof for type safety:
    await navigationService.NavigateToAsync(nameof(DetailsPage), parameters);
}

String-Based Navigation (Optional)

For developers migrating from Prism or preferring string-based navigation, you can register pages in your MauiProgram.cs:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder.UseMauiApp<App>();

        // Register pages for string-based navigation
        builder.Services.RegisterPage<MainPage>();
        builder.Services.RegisterPage<DetailsPage>();
        
        // You can also use custom keys:
        builder.Services.RegisterPage<DetailsPage>("CustomDetailsKey");

        return builder.Build();
    }
}

Now you can navigate using strings:

await navigationService.NavigateToAsync("DetailsPage", parameters);
await navigationService.NavigateToAsync(nameof(DetailsPage), parameters);

Receiving Parameters

On the destination page, access the parameters in the OnNavigatedTo method:

public override void OnNavigatedTo(INavigationParameters parameters)
{
    base.OnNavigatedTo(parameters);
    
    // Access strongly-typed parameters
    if (parameters.TryGetValue<int>("userId", out var userId))
    {
        LoadUserData(userId);
    }
    
    if (parameters.TryGetValue<string>("message", out var message))
    {
        DisplayMessage(message);
    }
    
    // Or use GetValue with default
    var timestamp = parameters.GetValue<DateTime>("timestamp");
}

Going Back with Parameters

You can also pass parameters when navigating back:

private async void OnGoBackClicked(object sender, EventArgs e)
{
    var navigationService = this.GetNavigationService();
    
    var parameters = new NavigationParameters
    {
        { "result", "Success" },
        { "data", someData }
    };
    
    await navigationService.GoBackAsync(parameters);
}

ViewModel Locator

The plugin includes a powerful ViewModel Locator that automates the binding between Views and ViewModels, reducing boilerplate code and making it easier to maintain the MVVM pattern.

Automatic ViewModel Binding in XAML

Use the AutoWireViewModel attached property to automatically bind a ViewModel to your View:

<?xml version="1.0" encoding="utf-8" ?>
<local:NavigationAwarePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:Plugin.Maui.NavigationAware;assembly=Plugin.Maui.NavigationAware"
             xmlns:nav="clr-namespace:Plugin.Maui.NavigationAware;assembly=Plugin.Maui.NavigationAware"
             x:Class="MyApp.MainPage"
             nav:ViewModelLocator.AutoWireViewModel="True"
             Title="Main Page">
    
</local:NavigationAwarePage>

The ViewModel Locator will automatically find and create a ViewModel based on naming conventions:

  • MainPageMainPageViewModel
  • DetailsPageDetailsPageViewModel
  • MainViewMainViewModel
Convention-Based ViewModel Resolution

By default, the ViewModel Locator uses convention-based naming to find ViewModels. For a page named MainPage, it will look for:

  1. MainPageViewModel in the same namespace
  2. MainViewModel (if page ends with "Page")

You can customize the convention by setting the DefaultViewTypeToViewModelTypeResolver:

ViewModelLocationProvider.DefaultViewTypeToViewModelTypeResolver = viewType =>
{
    // Your custom logic to resolve ViewModel type from View type
    var viewName = viewType.Name;
    var viewModelTypeName = $"{viewType.Namespace}.ViewModels.{viewName}ViewModel";
    return viewType.Assembly.GetType(viewModelTypeName);
};
Explicit ViewModel Registration

You can explicitly register ViewModels for specific Views in your MauiProgram.cs:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder.UseMauiApp<App>();

        // Register navigation service
        builder.Services.AddNavigationAware();
        
        // Register pages and ViewModels
        builder.Services.AddTransient<MainPage>();
        builder.Services.AddTransient<MainPageViewModel>();
        
        // Explicitly register ViewModel for a View
        builder.Services.RegisterViewModel<MainPage, MainPageViewModel>();
        
        // Or use a factory method
        builder.Services.RegisterViewModel<DetailsPage>(sp => 
            new DetailsPageViewModel(sp.GetRequiredService<IDataService>()));

        return builder.Build();
    }
}
Programmatic ViewModel Binding

You can also wire up ViewModels programmatically:

public partial class MyPage : NavigationAwarePage
{
    public MyPage()
    {
        InitializeComponent();
        
        // Automatically wire up ViewModel based on convention
        ViewModelLocationProvider.AutoWireViewModel(this);
        
        // Or specify a specific ViewModel type
        // ViewModelLocationProvider.AutoWireViewModel(this, typeof(MyCustomViewModel));
    }
}
Using ViewModels with Navigation

When using ViewModels, you can update them in the navigation lifecycle methods:

public partial class MyPage : NavigationAwarePage
{
    public override void OnNavigatedTo(INavigationParameters parameters)
    {
        base.OnNavigatedTo(parameters);
        
        if (BindingContext is MyPageViewModel viewModel)
        {
            // Access parameters and update ViewModel
            if (parameters.TryGetValue<int>("userId", out var userId))
            {
                viewModel.LoadUser(userId);
            }
        }
    }
}
ViewModel Locator Setup Requirements

For the ViewModel Locator to work properly, ensure you follow these steps:

  1. Register Navigation Services: Call AddNavigationAware() in your MauiProgram.cs:
builder.Services.AddNavigationAware();
  1. Register ViewModels in DI (if using dependency injection):
builder.Services.AddTransient<MainPageViewModel>();
builder.Services.AddTransient<DetailsPageViewModel>();
  1. Use the attached property in XAML or call AutoWireViewModel programmatically:
<local:NavigationAwarePage 
    xmlns:nav="clr-namespace:Plugin.Maui.NavigationAware;assembly=Plugin.Maui.NavigationAware"
    nav:ViewModelLocator.AutoWireViewModel="True">
ViewModel Locator Troubleshooting

Problem: ViewModel is not being set (BindingContext is null)

Solutions:

  1. Check naming conventions: Ensure your ViewModel follows the naming convention:

    • MainPageMainPageViewModel
    • DetailsViewDetailsViewModel
  2. Check namespace: The ViewModel must be in the same namespace as the View, or you need to customize the resolver.

  3. Register explicitly if conventions don't work:

    builder.Services.RegisterViewModel<MainPage, MainPageViewModel>();
    
  4. Enable debug output: Check the Debug output window for messages from ViewModelLocationProvider that explain what's happening.

  5. Verify ViewModel constructor: Ensure your ViewModel has a parameterless constructor, OR is registered in the DI container with all its dependencies.

Problem: ViewModel constructor dependencies not resolved

Solution: Register the ViewModel and its dependencies in DI:

// Register dependencies
builder.Services.AddSingleton<IDataService, DataService>();

// Register ViewModel
builder.Services.AddTransient<MainPageViewModel>();

Problem: Custom namespace for ViewModels

Solution: Customize the ViewModel resolver in App.xaml.cs or MauiProgram.cs:

ViewModelLocationProvider.DefaultViewTypeToViewModelTypeResolver = viewType =>
{
    var viewName = viewType.Name;
    // Look in a separate ViewModels namespace
    var viewModelTypeName = $"{viewType.Namespace}.ViewModels.{viewName}ViewModel";
    var viewModelType = viewType.Assembly.GetType(viewModelTypeName);
    
    // Fallback to default namespace
    if (viewModelType == null)
    {
        viewModelTypeName = $"{viewType.Namespace}.{viewName}ViewModel";
        viewModelType = viewType.Assembly.GetType(viewModelTypeName);
    }
    
    return viewModelType;
};

API Reference

Core Interfaces

INavigationAware

Interface for pages that need to be notified of navigation events.

public interface INavigationAware
{
    void OnNavigatedTo(INavigationParameters parameters);
    void OnNavigatedFrom(INavigationParameters parameters);
}
INavigationParameters

Represents navigation parameters passed during navigation.

public interface INavigationParameters : IDictionary<string, object>
{
    T? GetValue<T>(string key);
    bool TryGetValue<T>(string key, out T? value);
}
INavigationService

Service for performing navigation operations.

public interface INavigationService
{
    Task NavigateToAsync(Page page, INavigationParameters? parameters = null);
    Task NavigateToAsync(string pageKey, INavigationParameters? parameters = null);
    Task GoBackAsync(INavigationParameters? parameters = null);
}

Base Classes

Base ContentPage implementation with navigation awareness built-in.

public abstract class NavigationAwarePage : ContentPage, INavigationAware
{
    public virtual void OnNavigatedTo(INavigationParameters parameters);
    public virtual void OnNavigatedFrom(INavigationParameters parameters);
}

Extension Methods

public static class NavigationExtensions
{
    // Get navigation service from a page
    public static INavigationService GetNavigationService(this Page page);
    
    // Register navigation service with DI (optional)
    public static IServiceCollection AddNavigationAware(this IServiceCollection services);
    
    // Register a page type for string-based navigation
    public static IServiceCollection RegisterPage<TPage>(this IServiceCollection services, string? key = null) 
        where TPage : Page;
    
    // Register a ViewModel for a specific View type
    public static IServiceCollection RegisterViewModel<TView, TViewModel>(this IServiceCollection services)
        where TView : BindableObject;
    
    // Register a ViewModel using a factory method
    public static IServiceCollection RegisterViewModel<TView>(this IServiceCollection services, 
        Func<IServiceProvider, object> factory)
        where TView : BindableObject;
}

ViewModel Locator Classes

ViewModelLocator

Provides attached properties for automatic ViewModel location and binding in XAML.

public static class ViewModelLocator
{
    // Attached property to enable automatic ViewModel wiring
    public static readonly BindableProperty AutoWireViewModelProperty;
    
    // Gets/Sets the AutoWireViewModel attached property value
    public static bool GetAutoWireViewModel(BindableObject bindable);
    public static void SetAutoWireViewModel(BindableObject bindable, bool value);
    
    // Attached property to specify a specific ViewModel type
    public static readonly BindableProperty ViewModelTypeProperty;
    
    // Gets/Sets the ViewModelType attached property value
    public static Type? GetViewModelType(BindableObject bindable);
    public static void SetViewModelType(BindableObject bindable, Type? value);
}
ViewModelLocationProvider

Provides a mechanism for resolving ViewModels based on Views using naming conventions.

public static class ViewModelLocationProvider
{
    // Default convention for resolving ViewModel type names from View type names
    public static Func<Type, Type?> DefaultViewTypeToViewModelTypeResolver { get; set; }
    
    // Sets the service provider for ViewModel resolution
    public static void SetServiceProvider(IServiceProvider serviceProvider);
    
    // Registers a ViewModel type for a specific View type
    public static void Register<TView, TViewModel>() where TView : BindableObject;
    public static void Register(Type viewType, Type viewModelType);
    
    // Registers a factory method for creating ViewModels
    public static void Register<TView>(Func<object> factory) where TView : BindableObject;
    
    // Automatically wires up the ViewModel for a View
    public static void AutoWireViewModel(BindableObject view, Type? viewModelType = null);
    
    // Clears all registered ViewModels and factories
    public static void Clear();
}

Advanced Usage

URI-Based Navigation

Navigate to multiple pages in sequence using a URI path:

var navigationService = this.GetNavigationService();

// Navigate through multiple pages
await navigationService.NavigateAsync("/NavigationPage/MasterTabbedPage");

// With parameters
await navigationService.NavigateAsync(
    "/NavigationPage/DetailPage",
    new NavigationParameters { { "id", 123 } }
);

Pop the navigation stack to a specific page:

var navigationService = this.GetNavigationService();

// Navigate back to a specific page
await navigationService.GoBackToAsync("SettingsPage");

// With parameters
await navigationService.GoBackToAsync(
    "MainPage",
    new NavigationParameters { { "result", "updated" } }
);

Pop all pages and return to the root:

var navigationService = this.GetNavigationService();

// Navigate back to root
await navigationService.GoBackToRootAsync();

// With parameters
await navigationService.GoBackToRootAsync(
    new NavigationParameters { { "taskCompleted", true } }
);

Dependency Injection

You can optionally register the navigation service with the DI container:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>();

        // Register navigation service
        builder.Services.AddNavigationAware();
        
        // Register your pages
        builder.Services.AddTransient<MainPage>();
        builder.Services.AddTransient<DetailsPage>();

        return builder.Build();
    }
}

Then inject it into your pages:

public partial class MyPage : NavigationAwarePage
{
    private readonly INavigationService _navigationService;

    public MyPage(INavigationService navigationService)
    {
        InitializeComponent();
        _navigationService = navigationService;
    }
}

Sample Application

The repository includes a sample application demonstrating all features of the plugin. Check out the samples folder to see it in action.

Comparison with Prism

If you're familiar with Prism, this plugin provides similar functionality:

Prism Plugin.Maui.NavigationAware
INavigationAware INavigationAware
INavigationParameters INavigationParameters
NavigationParameters NavigationParameters
OnNavigatedTo() OnNavigatedTo()
OnNavigatedFrom() OnNavigatedFrom()
NavigateAsync(string uri) NavigateAsync(string uri)
GoBackAsync() GoBackAsync()
GoBackToRootAsync() GoBackToRootAsync()
Not available GoBackToAsync(string pageKey)

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Inspired by Prism Library for Xamarin.Forms and .NET MAUI
  • Built with ❤️ for the .NET MAUI community

Support

If you encounter any issues or have questions, please open an issue on GitHub.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-android34.0 is compatible.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-ios18.0 is compatible.  net8.0-maccatalyst was computed.  net8.0-maccatalyst18.0 is compatible.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net8.0-windows10.0.19041 is compatible.  net9.0 was computed.  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 was computed.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
1.0.4 186 10/6/2025
1.0.3 167 10/6/2025
1.0.2 169 10/6/2025
1.0.1 167 10/6/2025
1.0.0 178 10/6/2025