Modern UI For WPF Application by Example (NavigationService - MVVM): Part 2

Scope

The purpose of this article is to show how to create a navigation service for a WPF application that uses the Modern UI.

Introduction

The Modern UI is a set of controls and styles converting our WPF application into a great looking Modern UI app. The Modern UI project can be found in mui.codeplex.com, where it is possible to get the WPF app that demostrates the features provided by the “mui”.

WPF doesn´t have a pattern for navigation when we are using Windows, only a navigation service exists for when we use Pages. ModernUI introduces a special way for the navigation, that uses the ModernFrame.

For the sample we will use MVVMLight Toolkit for help in the MVVM pattern implementation and we will use a new feature provided by this toolkit, the INavigationService interface.

Note: The MVVMLight Toolkit doesn´t have an implementation of INavigationService for WPF, to see more about it read this article Announcing MVVM Light V5 for Windows and Xamarin.

Description

The source code base that we will use in this sample, is similar to the sample used in the article Modern UI for WPF application by example (Default Window).

We will start creating the interfaces IModernNavigationService and NavigationService, then we will configure the navigation in the MainWindow and then wil use the navigation service in the view model to navigate to another view.

The interface will be something like:

  1. public interface IModernNavigationService : INavigationService  
  2. {  
  3.     /// <summary>  
  4.     /// Gets the parameter.  
  5.     /// </summary>  
  6.     /// <value>  
  7.     /// The parameter.  
  8.     /// </value>  
  9.     object Parameter { get; }  
  10. }  
The implementation will be:
  1. public class NavigationService : IModernNavigationService  
  2. {  
  3.     private readonly Dictionary<string, Uri> _pagesByKey;  
  4.     private readonly List<string> _historic;  
  5.    
  6.     /// <summary>  
  7.     /// Initializes a new instance of the <see cref="NavigationService"/> class.  
  8.     /// </summary>  
  9.     public NavigationService()  
  10.     {  
  11.         _pagesByKey = new Dictionary<string, Uri>();  
  12.         _historic = new List<string>();  
  13.     }  
  14.    
  15.     /// <summary>  
  16.     /// Gets the key corresponding to the currently displayed page.  
  17.     /// </summary>  
  18.     /// <value>  
  19.     /// The current page key.  
  20.     /// </value>  
  21.     public string CurrentPageKey  
  22.     {  
  23.         get;  
  24.         private set;  
  25.     }  
  26.    
  27.     /// <summary>  
  28.     /// Gets the parameter.  
  29.     /// </summary>  
  30.     /// <value>  
  31.     /// The parameter.  
  32.     /// </value>  
  33.     public object Parameter { getprivate set; }  
  34.    
  35.     /// <summary>  
  36.     /// The go back.  
  37.     /// </summary>  
  38.     public void GoBack()  
  39.     {  
  40.         if (_historic.Count > 1)  
  41.         {  
  42.             _historic.RemoveAt(_historic.Count - 1);  
  43.             NavigateTo(_historic.Last(), null);  
  44.         }  
  45.     }  
  46.    
  47.     /// <summary>  
  48.     /// The navigate to.  
  49.     /// </summary>  
  50.     /// <param name="pageKey">  
  51.     /// The page key.  
  52.     /// </param>  
  53.     public void NavigateTo(string pageKey)  
  54.     {  
  55.         NavigateTo(pageKey, null);  
  56.     }  
  57.    
  58.     /// <summary>  
  59.     /// The navigate to.  
  60.     /// </summary>  
  61.     /// <param name="pageKey">  
  62.     /// The page key.  
  63.     /// </param>  
  64.     /// <param name="parameter">  
  65.     /// The parameter.  
  66.     /// </param>  
  67.     public virtual void NavigateTo(string pageKey, object parameter)  
  68.     {  
  69.         lock (_pagesByKey)  
  70.         {  
  71.             if (!_pagesByKey.ContainsKey(pageKey))  
  72.             {  
  73.                 throw new ArgumentException(string.Format("No such page: {0}. Did you forget to call NavigationService.Configure?", pageKey), "pageKey");  
  74.             }  
  75.    
  76.             var frame = GetDescendantFromName(Application.Current.MainWindow, "ContentFrame"as ModernFrame;  
  77.    
  78.             // Set the frame source, which initiates navigation  
  79.             if (frame != null)  
  80.             {  
  81.                 frame.Source = _pagesByKey[pageKey];  
  82.             }  
  83.             Parameter = parameter;  
  84.             _historic.Add(pageKey);  
  85.             CurrentPageKey = pageKey;  
  86.         }  
  87.     }  
  88.    
  89.     /// <summary>  
  90.     /// Configures the specified key.  
  91.     /// </summary>  
  92.     /// <param name="key">The key.</param>  
  93.     /// <param name="pageType">Type of the page.</param>  
  94.     public void Configure(string key, Uri pageType)  
  95.     {  
  96.         lock (_pagesByKey)  
  97.         {  
  98.             if (_pagesByKey.ContainsKey(key))  
  99.             {  
  100.                 _pagesByKey[key] = pageType;  
  101.             }  
  102.             else  
  103.             {  
  104.                 _pagesByKey.Add(key, pageType);  
  105.             }  
  106.         }  
  107.     }  
  108.    
  109.     /// <summary>  
  110.     /// Gets the name of the descendant from.  
  111.     /// </summary>  
  112.     /// <param name="parent">The parent.</param>  
  113.     /// <param name="name">The name.</param>  
  114.     /// <returns>The FrameworkElement.</returns>  
  115.     private static FrameworkElement GetDescendantFromName(DependencyObject parent, string name)  
  116.     {  
  117.         var count = VisualTreeHelper.GetChildrenCount(parent);  
  118.    
  119.         if (count < 1)  
  120.         {  
  121.             return null;  
  122.         }  
  123.    
  124.         for (var i = 0; i < count; i++)  
  125.         {  
  126.             var frameworkElement = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;  
  127.             if (frameworkElement != null)  
  128.             {  
  129.                 if (frameworkElement.Name == name)  
  130.                 {  
  131.                     return frameworkElement;  
  132.                 }  
  133.    
  134.                 frameworkElement = GetDescendantFromName(frameworkElement, name);  
  135.                 if (frameworkElement != null)  
  136.                 {  
  137.                     return frameworkElement;  
  138.                 }  
  139.             }  
  140.         }  
  141.    
  142.         return null;  
  143.     }  
  144. }  
And the setup for the navigation will be done in the MainWindow.xaml.cs, something like:
  1. private void SetupNavigation()  
  2. {  
  3.     var navigationService = new NavigationService();  
  4.     navigationService.Configure(ViewModelLocator.ResourcePageKey, new Uri("Views/ResourcesView.xaml"));  
  5.     navigationService.Configure(ViewModelLocator.StepsPageKey, new Uri("Views/StepsView.xaml"));  
  6.    
  7.     SimpleIoc.Default.Register<IModernNavigationService>(() => navigationService);  
  8. }  
In the StepsViewModel we will do:
  1. public class StepsViewModel : ViewModelBase  
  2. {  
  3.     private readonly IModernNavigationService _modernNavigationService;  
  4.    
  5.     /// <summary>  
  6.     /// Initializes a new instance of the <see cref="StepsViewModel"/> class.   
  7.     /// </summary>  
  8.     /// <param name="modernNavigationService">  
  9.     /// The modern Navigation Service.  
  10.     /// </param>  
  11.     public StepsViewModel(IModernNavigationService modernNavigationService)  
  12.     {  
  13.         _modernNavigationService = modernNavigationService;  
  14.         ResourcesCommand = new RelayCommand(ShowResources);  
  15.     }  
  16.    
  17.     /// <summary>  
  18.     /// Gets or sets the resources command.  
  19.     /// </summary>  
  20.     /// <value>The resources command.</value>  
  21.     public ICommand ResourcesCommand { getset; }  
  22.    
  23.     /// <summary>  
  24.     /// Shows the resources.  
  25.     /// </summary>  
  26.     private void ShowResources()  
  27.     {  
  28.         _modernNavigationService.NavigateTo(ViewModelLocator.ResourcePageKey);  
  29.     }  
  30. }  
Note:

 

  1. To send a parameter when we navigate we should do:
    1. modernNavigationService.NavigateTo(ViewModelLocator.ResourcePageKey, myParameterValue);  
    And then in the view model use the Parameter property, from the navigation service, to get the parameter.
    1. _modernNavigationService.Parameter;  
  2. To navigate to the preview page use the GoBack method:
    1. _modernNavigationService.GoBack();  
Source Code

Get the source code for this sample in github.

Visual Studio Extension used
<< Modern UI for WPF application by example (Handle Navigation: (Default)) Part-1