Home > .NET Core, C#, MVVM, WPF > An MVVM-aware NavigationService for WPF running on .NET Core

An MVVM-aware NavigationService for WPF running on .NET Core

In the last article we talked about how to use the MVVM pattern in WPF applications running on .NET Core. Today, we’ll add support for navigation through Window from View Models. For this purpose, we start from the code of the last article. Our goal is to extend it adding a method to open new Window from View Models and, at the same time, pass parameters to View Models.

So, let’s start with the NavigationService:

public interface IActivable
    Task ActivateAsync(object parameter);

public class NavigationService
    private Dictionary<string, Type> windows { get; }
        = new Dictionary<string, Type>();

    private readonly IServiceProvider serviceProvider;

    public void Configure(string key, Type windowType) 
        => windows.Add(key, windowType);

    public NavigationService(IServiceProvider serviceProvider)
        this.serviceProvider = serviceProvider;

    public async Task ShowAsync(string windowKey,
        object parameter = null)
        var window = await GetAndActivateWindowAsync(windowKey, parameter);

    public async Task<bool?> ShowDialogAsync(string windowKey,
        object parameter = null)
        var window = await GetAndActivateWindowAsync(windowKey, parameter);
        return window.ShowDialog();

    private async Task<Window> GetAndActivateWindowAsync(string windowKey,
        object parameter = null)
        var window = serviceProvider.GetRequiredService(windows[windowKey])
            as Window;

        if (window.DataContext is IActivable activable)
            await activable.ActivateAsync(parameter);

        return window;

First of all, at lines 1-4 we define an IActivable interface to mark View Models that support “activation”, i.e. the ability to receive parameters when the corresponding Window is loaded. Then, the NavigationService class encapsulates all the logic we need:

  • it defines a Configure method (lines 13-14) to map a string identifier to a window type. We follow this approach because we want to use the NavigationService from View Models that don’t have to know the Views;
  • it provides methods to open a window (modeless or modal, lines 24-25 and 31-32 respectively), getting it from the ServiceProvider using the windowKey parameter (lines 37-38);
  • if the Window.DataContext is a View Model that implements the IActivable interface, it calls its ActivateAsync method (lines 41-44), passing to it the specified parameter, if any.

Now we need to properly register the NavigationService and all the Windows and corresponding View Models of our application in the ConfigureServices method of App.xaml.cs file we defined in the previous post:

public static class Windows
    public const string MainWindow = nameof(MainWindow);
    public const string DetailWindow = nameof(DetailWindow);

private void ConfigureServices(IConfiguration configuration, 
    IServiceCollection services)
    // ...

    services.AddScoped<NavigationService>(serviceProvider =>
        var navigationService = new NavigationService(serviceProvider);
        navigationService.Configure(Windows.MainWindow, typeof(MainWindow));
        navigationService.Configure(Windows.DetailWindow, typeof(DetailWindow));

        return navigationService;



At lines 1-5 we create a Windows class that holds strings corresponding to the Windows of the application, in this sample a MainWindow and a DetailWindow (we’ll talk about them later in this article). Then, we create the NavigationService, configure all the Windows and add it to the services collection (lines 12-19). Finally, at lines 21-25 we add Views Models and Windows themselves.

In the OnStartup method, we can use the new service to show the MainWindow:

protected override async void OnStartup(StartupEventArgs e)
    await host.StartAsync();

    var navigationService = 

    await navigationService.ShowAsync(Navigation.Windows.MainWindow);


Now let’s see how to use it. Our MainWindow already contains a TextBox and a Button from the last sample. These objects are already bound to the MainViewModel, so we need to modify it to obtain something like this:

public class MainViewModel : ViewModelBase
    private string input;
    public string Input
        get => input;
        set => Set(ref input, value);

    private readonly NavigationService navigationService;
    private readonly ISampleService sampleService;
    private readonly AppSettings settings;

    public RelayCommand ExecuteCommand { get; }

    public MainViewModel(NavigationService navigationService, 
        ISampleService sampleService, IOptions<AppSettings> options)
        this.navigationService = navigationService;
        this.sampleService = sampleService;
        settings = options.Value;

        ExecuteCommand = new RelayCommand(
            async () => await ExecuteAsync());

    private Task ExecuteAsync()
        Debug.WriteLine($"Current value: {input}");
        return navigationService.ShowDialogAsync(Windows.DetailWindow, input);

First of all, in the constructor we pass a reference to the NavigationService (line 16). As usual, the .NET Core ServiceProvider is responsible to instantiate it and pass it to the class. Then, in the ExecuteAsync method we show the DetailWindow as dialog using the service, passing the specified input as parameter (line 30).

The DetailWindow can be something like the following:

        ="{Binding Source={StaticResource Locator}, Path=DetailViewModel}"

        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock Text="Passed Parameter:" />
            <TextBlock Text="{Binding Parameter}" />

Note in particular the DataContext property at line 4-5 and the binding at line 11. Now we need to define the DetailViewModel itself:

public class DetailViewModel : ViewModelBase, IActivable
    private string parameter;
    public string Parameter
        get => parameter;
        set => Set(ref parameter, value);

    public Task ActivateAsync(object parameter)
        Parameter = parameter?.ToString();
        return Task.CompletedTask;

When we navigate to DetailWindow from MainViewModel, we pass a parameter, so the DetailViewModel must be able to handle it. For this reason, it implements the IActivable interface (lines 10-13): in this way, the input value from MainViewModel is automatically passed to the ActivateAsync method.

Now we can finally try running the application:

Navigation in the WPF Application with MVVM running on top of .NET Core

Navigation in the WPF Application with MVVM running on top of .NET Core

You can download the sample application using the link below:

An MVVM-aware NavigationService for WPF running on .NET Core

Categories: .NET Core, C#, MVVM, WPF

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: