Navigation Between Pages Using MVVM Light in Universal Windows Platform(UWP)

This article will help you to setup the basic Navigation with MVVM pattern in a Windows 10 Universal Platform App using MVVM Light Toolkit from Galasoft.

A complete explanation on how to setup your first Windows 10 Universal Windows Platform using MVVM Light can be found in this article: Using MVVM Light with Universal Windows App For Windows 10.

Getting Started

For now let's hope you have already setup a blank project for Universal Windows Platform with MVVM Light Library installed and completed the basic steps to implement MVVM Light. I have named my solution as Win10MVVMLightNavigationDemo.

The App has two Pages FirstPage.xaml and SecondPage.xaml:


FirstPage.xaml

The Xaml code for the page is:

<Page  
    x:Class="Win10MVVMLightNavigationDemo.Views.FirstPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Win10MVVMLightNavigationDemo.Views"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    DataContext="{Binding FirstPageInstance, Source={StaticResource Locator}}" 
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Name="Title" Text="{Binding Title}" FontSize="28" TextAlignment="Center"  VerticalAlignment="Center" HorizontalAlignment="Center" Width="200" Height="50"/>
        <Button Grid.Row="1" Name="Navigate" Command="{Binding NavigateCommand}" Content="Got to Second Page"  VerticalAlignment="Center" HorizontalAlignment="Center" />
    </Grid>
</Page>

And Now the SecondPage.xaml


The Xaml code for the page is:

<Page  
    x:Class="Win10MVVMLightNavigationDemo.Views.SecondPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Win10MVVMLightNavigationDemo.Views"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    DataContext="{Binding SecondPageInstance, Source={StaticResource Locator}}" 
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Name="Title" Text="{Binding Title}" FontSize="28" TextAlignment="Center"  VerticalAlignment="Center" HorizontalAlignment="Center" Width="200" Height="50"/>
        <Button Grid.Row="1" Name="Navigate" Command="{Binding NavigateCommand}" Content="Got to First Page"  VerticalAlignment="Center" HorizontalAlignment="Center" />
    </Grid>
</Page>

Also there are ViewModel in the ViewModels folder for each Page in the App .

FirstPageViewModel.cs

using GalaSoft.MvvmLight;  
using GalaSoft.MvvmLight.Command;  
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;

namespace Win10MVVMLightNavigationDemo.ViewModels  
{
    public  class FirstPageViewModel : ViewModelBase
    {
        public RelayCommand NavigateCommand { get; private set; }
        private bool _isLoading = false;
        public bool IsLoading
        {
            get
            {
                return _isLoading;
            }
            set
            {
                _isLoading = value;
                RaisePropertyChanged("IsLoading");

            }
        }
        private string _title;
        public string Title
        {

            get
            {
                return _title;
            }
            set
            {
                if (value != _title)
                {
                    _title = value;
                    RaisePropertyChanged("Title");
                }
            }
        }

        public FirstPageViewModel()
        {
            Title = "First Page";
            NavigateCommand = new RelayCommand(NavigateCommandAction);
        }

        private void NavigateCommandAction()
        {
            // Do Something
        }
    }
}

And the SecondPageViewModel.cs

using GalaSoft.MvvmLight;  
using GalaSoft.MvvmLight.Command;  
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;

namespace Win10MVVMLightNavigationDemo.ViewModels  
{
    public  class SecondPageViewModel : ViewModelBase
    {
        public RelayCommand NavigateCommand { get; private set; }
        private bool _isLoading = false;
        public bool IsLoading
        {
            get
            {
                return _isLoading;
            }
            set
            {
                _isLoading = value;
                RaisePropertyChanged("IsLoading");

            }
        }
        private string _title;
        public string Title
        {

            get
            {
                return _title;
            }
            set
            {
                if (value != _title)
                {
                    _title = value;
                    RaisePropertyChanged("Title");
                }
            }
        }

        public SecondPageViewModel()
        {
            Title = "Second Page";
            NavigateCommand = new RelayCommand(NavigateCommandAction);
        }

        private void NavigateCommandAction()
        {
            //Do Something
        }
    }
}

The ViewModelLocator.cs for the App which registers each instance of ViewModel Looks like this:

using GalaSoft.MvvmLight;  
using GalaSoft.MvvmLight.Ioc;  
using GalaSoft.MvvmLight.Views;  
using Microsoft.Practices.ServiceLocation;  
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;

namespace Win10MVVMLightNavigationDemo.ViewModels  
{
    /// <summary>
    /// This class contains static references to all the view models in the
    /// application and provides an entry point for the bindings.
    /// </summary> 
    public class ViewModelLocator
    {
        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
            if (ViewModelBase.IsInDesignModeStatic)
            {
                // Create design time view services and models
            }
            else
            {
                // Create run time view services and models
            }

            //Register your services used here
            SimpleIoc.Default.Register<FirstPageViewModel>();
            SimpleIoc.Default.Register<SecondPageViewModel>();


        }


        // <summary>
        // Gets the FirstPage view model.
        // </summary>
        // <value>
        // The FirstPage view model.
        // </value>
        public FirstPageViewModel FirstPageInstance
        {
            get
            {
                return ServiceLocator.Current.GetInstance<FirstPageViewModel>();
            }
        }

        // <summary>
        // Gets the SecondPage view model.
        // </summary>
        // <value>
        // The SecondPage view model.
        // </value>
        public SecondPageViewModel SecondPageInstance
        {
            get
            {
                return ServiceLocator.Current.GetInstance<SecondPageViewModel>();
            }
        }

        // <summary>
        // The cleanup.
        // </summary>
        public static void Cleanup()
        {
            // TODO Clear the ViewModels
        }
    }
}

Also the App.xaml was modified to register locator as a global static resource to make it available for all pages to use.

App.xaml

<Application  
    x:Class="Win10MVVMLightNavigationDemo.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Win10MVVMLightNavigationDemo"
    xmlns:vm="using:Win10MVVMLightNavigationDemo.ViewModels"
    RequestedTheme="Light">
    <Application.Resources>
        <vm:ViewModelLocator xmlns:vm="using:Win10MVVMLightNavigationDemo.ViewModels" x:Key="Locator" />
    </Application.Resources>
</Application>
Using NavigationService

In order to use the Navigation in MVVM Light you will have to register and Configure INavigationService in ViewModelLocator.cs , for this you will have to register each Page with a PageKey.

First declare page key variable for each page in the App as a Public constant string before initializing instance of ViewModelLocator Class

public const string FirstPageKey = "FirstPage";  
public const string SecondPageKey = "SecondPage";  

Now define a new instance of NavigationService and configure it with providing each Page Key for each Page before registering the INavigationService to the SimpleIoc

 var nav = new NavigationService();
            nav.Configure(FirstPageKey, typeof(FirstPage));
            nav.Configure(SecondPageKey, typeof(SecondPage));

After this register the INavigationService to the ViewModelLocator SimpleIoc

SimpleIoc.Default.Register<INavigationService>(() => nav);  

After all the above steps the ViewModelLoacator.cs will look like this:

using GalaSoft.MvvmLight;  
using GalaSoft.MvvmLight.Ioc;  
using GalaSoft.MvvmLight.Views;  
using Microsoft.Practices.ServiceLocation;  
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;  
using Win10MVVMLightNavigationDemo.Views;

namespace Win10MVVMLightNavigationDemo.ViewModels  
{
    /// <summary>
    /// This class contains static references to all the view models in the
    /// application and provides an entry point for the bindings.
    /// </summary> 
    public class ViewModelLocator
    {
        public const string FirstPageKey = "FirstPage";
        public const string SecondPageKey = "SecondPage";
        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
            var nav = new NavigationService();
            nav.Configure(FirstPageKey, typeof(FirstPage));
            nav.Configure(SecondPageKey, typeof(SecondPage));
            if (ViewModelBase.IsInDesignModeStatic)
            {
                // Create design time view services and models
            }
            else
            {
                // Create run time view services and models
            }

            //Register your services used here
            SimpleIoc.Default.Register<INavigationService>(() => nav);
            SimpleIoc.Default.Register<FirstPageViewModel>();
            SimpleIoc.Default.Register<SecondPageViewModel>();


        }


        // <summary>
        // Gets the FirstPage view model.
        // </summary>
        // <value>
        // The FirstPage view model.
        // </value>
        public FirstPageViewModel FirstPageInstance
        {
            get
            {
                return ServiceLocator.Current.GetInstance<FirstPageViewModel>();
            }
        }

        // <summary>
        // Gets the SecondPage view model.
        // </summary>
        // <value>
        // The SecondPage view model.
        // </value>
        public SecondPageViewModel SecondPageInstance
        {
            get
            {
                return ServiceLocator.Current.GetInstance<SecondPageViewModel>();
            }
        }

        // <summary>
        // The cleanup.
        // </summary>
        public static void Cleanup()
        {
            //  Clear the ViewModels
        }
    }
}

Now you will have to use INavigationService instance in your each ViewModel.

Fist take the case of FirstPageViewModel now you will have to declare INaviagtionSevice as Private readonly before initializes a new instance of the FirstPageViewModel

private readonly INavigationService _navigationService;

Also pass the Navigation service as arguments to the constructor.

 public FirstPageViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;
            Title = "First Page";
            NavigateCommand = new RelayCommand(NavigateCommandAction);
        }

Now all that left is to call the NavigateTo method and pass the Page Key to Navigate to the SecondPage in the NavigateCommandAction

_navigationService.NavigateTo("SecondPage");

After all the above steps your FirstPageViewModel.cs Will have the following code:

using GalaSoft.MvvmLight;  
using GalaSoft.MvvmLight.Command;  
using GalaSoft.MvvmLight.Views;  
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;

namespace Win10MVVMLightNavigationDemo.ViewModels  
{
    public  class FirstPageViewModel : ViewModelBase
    {
        private readonly INavigationService _navigationService;
        public RelayCommand NavigateCommand { get; private set; }
        private bool _isLoading = false;
        public bool IsLoading
        {
            get
            {
                return _isLoading;
            }
            set
            {
                _isLoading = value;
                RaisePropertyChanged("IsLoading");

            }
        }
        private string _title;
        public string Title
        {

            get
            {
                return _title;
            }
            set
            {
                if (value != _title)
                {
                    _title = value;
                    RaisePropertyChanged("Title");
                }
            }
        }

        public FirstPageViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;
            Title = "First Page";
            NavigateCommand = new RelayCommand(NavigateCommandAction);
        }

        private void NavigateCommandAction()
        {
            // Do Something
            _navigationService.NavigateTo("SecondPage");
        }
    }
}

For navigating to the FirstPage follow the above steps all that you need to change the Page Key to FirstPage in NavigateTo method

_navigationService.NavigateTo("FirstPage");

or if you wish to use the back button feature just replace it with:

_navigationService.GoBack();

And yourSecondPageViewModel.cs will have the following code :

using GalaSoft.MvvmLight;  
using GalaSoft.MvvmLight.Command;  
using GalaSoft.MvvmLight.Views;  
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;

namespace Win10MVVMLightNavigationDemo.ViewModels  
{
    public  class SecondPageViewModel : ViewModelBase
    {
        private readonly INavigationService _navigationService;
        public RelayCommand NavigateCommand { get; private set; }
        private bool _isLoading = false;
        public bool IsLoading
        {
            get
            {
                return _isLoading;
            }
            set
            {
                _isLoading = value;
                RaisePropertyChanged("IsLoading");

            }
        }
        private string _title;
        public string Title
        {

            get
            {
                return _title;
            }
            set
            {
                if (value != _title)
                {
                    _title = value;
                    RaisePropertyChanged("Title");
                }
            }
        }

        public SecondPageViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;
            Title = "Second Page";
            NavigateCommand = new RelayCommand(NavigateCommandAction);
        }

        private void NavigateCommandAction()
        {
            //Do Something
            _navigationService.NavigateTo("FirstPage");

            // or use the GoBack if you like to implement a back button functionality

            //_navigationService.GoBack(); 
        }
    }
}

The following shows the working of the App: