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: