Share via


Modern UI for WPF application by example ( NavigationService - MVVM )

Scope

This article has the goal to show how to create a navigation service for WPF application that uses Modern UI.

Introduction

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 find in mui.codeplex.com, here is possible to get the WPF app that demostrate the features provided by "mui".

WPF doesn´t have a pattern for navigation of Windows, the navigation service only works for Pages and UserControls. ModernUI introduces a special way for navigation - using 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 any implementation of INavigationService for WPF,  to read see  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 to create the interface IModernNavigationService and the NavigationService, after it we will configure the navigation in MainWindow and then will use the navigation service in the view model for navigate to another view.

The interface will be something like

public interface IModernNavigationService : INavigationService
   {
       /// <summary>
       /// Gets the parameter.
       /// </summary>
       /// <value>
       /// The parameter.
       /// </value>
       object Parameter { get; }
   }

which implementation will be

public class NavigationService : IModernNavigationService
{
    private readonly Dictionary<string, Uri> _pagesByKey;
    private readonly List<string> _historic;
 
    /// <summary>
    /// Initializes a new instance of the <see cref="NavigationService"/> class.
    /// </summary>
    public NavigationService()
    {
        _pagesByKey = new Dictionary<string, Uri>();
        _historic = new List<string>();
    }
 
    /// <summary>
    /// Gets the key corresponding to the currently displayed page.
    /// </summary>
    /// <value>
    /// The current page key.
    /// </value>
    public string CurrentPageKey
    {
        get;
        private set;
    }
 
    /// <summary>
    /// Gets the parameter.
    /// </summary>
    /// <value>
    /// The parameter.
    /// </value>
    public object Parameter { get; private set; }
 
    /// <summary>
    /// The go back.
    /// </summary>
    public void GoBack()
    {
        if (_historic.Count > 1)
        {
            _historic.RemoveAt(_historic.Count - 1);
            NavigateTo(_historic.Last(), null);
        }
    }
 
    /// <summary>
    /// The navigate to.
    /// </summary>
    /// <param name="pageKey">
    /// The page key.
    /// </param>
    public void NavigateTo(string pageKey)
    {
        NavigateTo(pageKey, null);
    }
 
    /// <summary>
    /// The navigate to.
    /// </summary>
    /// <param name="pageKey">
    /// The page key.
    /// </param>
    /// <param name="parameter">
    /// The parameter.
    /// </param>
    public virtual void NavigateTo(string pageKey, object parameter)
    {
        lock (_pagesByKey)
        {
            if (!_pagesByKey.ContainsKey(pageKey))
            {
                throw new ArgumentException(string.Format("No such page: {0}. Did you forget to call NavigationService.Configure?", pageKey), "pageKey");
            }
 
            var frame = GetDescendantFromName(Application.Current.MainWindow, "ContentFrame") as ModernFrame;
 
            // Set the frame source, which initiates navigation
            if (frame != null)
            {
                frame.Source = _pagesByKey[pageKey];
            }
            Parameter = parameter;
            _historic.Add(pageKey);
            CurrentPageKey = pageKey;
        }
    }
 
    /// <summary>
    /// Configures the specified key.
    /// </summary>
    /// <param name="key">The key.</param>
    /// <param name="pageType">Type of the page.</param>
    public void Configure(string key, Uri pageType)
    {
        lock (_pagesByKey)
        {
            if (_pagesByKey.ContainsKey(key))
            {
                _pagesByKey[key] = pageType;
            }
            else
            {
                _pagesByKey.Add(key, pageType);
            }
        }
    }
 
    /// <summary>
    /// Gets the name of the descendant from.
    /// </summary>
    /// <param name="parent">The parent.</param>
    /// <param name="name">The name.</param>
    /// <returns>The FrameworkElement.</returns>
    private static FrameworkElement GetDescendantFromName(DependencyObject parent, string name)
    {
        var count = VisualTreeHelper.GetChildrenCount(parent);
 
        if (count < 1)
        {
            return null;
        }
 
        for (var i = 0; i < count; i++)
        {
            var frameworkElement = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
            if (frameworkElement != null)
            {
                if (frameworkElement.Name == name)
                {
                    return frameworkElement;
                }
 
                frameworkElement = GetDescendantFromName(frameworkElement, name);
                if (frameworkElement != null)
                {
                    return frameworkElement;
                }
            }
        }
 
        return null;
    }
}

and the setup for the navigation will be done in the MainWindow.xaml.cs, something like

private void SetupNavigation()
    {
        var navigationService = new NavigationService();
        navigationService.Configure(ViewModelLocator.ResourcePageKey, new Uri("Views/ResourcesView.xaml"));
        navigationService.Configure(ViewModelLocator.StepsPageKey, new Uri("Views/StepsView.xaml"));
 
        SimpleIoc.Default.Register<IModernNavigationService>(() => navigationService);
    }

In the StepsViewModel we will do

public class StepsViewModel : ViewModelBase
   {
       private readonly IModernNavigationService _modernNavigationService;
 
       /// <summary>
       /// Initializes a new instance of the <see cref="StepsViewModel"/> class. 
       /// </summary>
       /// <param name="modernNavigationService">
       /// The modern Navigation Service.
       /// </param>
       public StepsViewModel(IModernNavigationService modernNavigationService)
       {
           _modernNavigationService = modernNavigationService;
           ResourcesCommand = new RelayCommand(ShowResources);
       }
 
       /// <summary>
       /// Gets or sets the resources command.
       /// </summary>
       /// <value>The resources command.</value>
       public ICommand ResourcesCommand { get; set; }
 
       /// <summary>
       /// Shows the resources.
       /// </summary>
       private void ShowResources()
       {
           _modernNavigationService.NavigateTo(ViewModelLocator.ResourcePageKey);
       }
   }

Note:

1. For send a parameter when navigate we should do

_modernNavigationService.NavigateTo(ViewModelLocator.ResourcePageKey, myParameterValue);

and then in the view model use the Parameter property, from the navigation service, for get the parameter

_modernNavigationService.Parameter

2. For navigate to the preview page use the GoBack method

_modernNavigationService.GoBack();

See others solutions for Navigation in ModernUI applications.
Modern UI for WPF application by example (handle navigation)
Modern UI for WPF application by example ( NavigationMessageService - MVVM )

Source code

Get the source code for this sample in github.