Share via


WPF: Modern UI for application (handle navigation)

Scope

This article has the goal to show how to create a blank window in WPF using Modern UI to handle navigation.

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

WPF doesn't have a pattern for the navigation of Windows, there is only navigation for Pages and UserControls. ModernUI introduces a special way for the navigation - the ModernFrame.

One particular useful aspect of this is when you want to add events around the navigation from one view to another.

Description

In the sample Modern UI for WPF application by example (Default Window) we saw the default window provided by Modern UI, and in this sample, we will use the same base code but we will change the code to handle the navigation.

If we search the properties from the  ModernWindow, we will see that it doesn't have any event or method to handle navigation. After searching documentation, we find an article Handle navigation events in your content.

Let's apply it!

The MainWindow contains a MenuLinkGroups which contains a LinkGroup with two Links. Each link is defined by a UserControl. To handle navigation, each user control will implement the interface IContent, like following

/// <summary>
   /// Interaction logic for StepsControl.xaml.
   /// </summary>
   public partial class StepsControl : IContent
   {
       /// <summary>
       /// Initializes a new instance of the <see cref="StepsControl"/> class.
       /// </summary>
       public StepsControl()
       {
           InitializeComponent();
       }
 
       /// <summary>
       /// Called when navigation to a content fragment begins.
       /// </summary>
       /// <param name="e">An object that contains the navigation data.</param>
       public void OnFragmentNavigation(FragmentNavigationEventArgs e)
       {
           Debug.WriteLine("StepsControl- OnFragmentNavigation");
       }
 
       /// <summary>
       /// Called when this instance is no longer the active content in a frame.
       /// </summary>
       /// <param name="e">An object that contains the navigation data.</param>
       public void OnNavigatedFrom(NavigationEventArgs e)
       {
           Debug.WriteLine("StepsControl -OnNavigatedFrom");
       }
 
       /// <summary>
       /// Called when a this instance becomes the active content in a frame.
       /// </summary>
       /// <param name="e">An object that contains the navigation data.</param>
       public void OnNavigatedTo(NavigationEventArgs e)
       {
           Debug.WriteLine("StepsControl- OnNavigatedTo");
       }
 
       /// <summary>
       /// Called just before this instance is no longer the active content in a frame.
       /// </summary>
       /// <param name="e">
       /// An object that contains the navigation data.
       /// </param>
       /// <remarks>
       /// The method is also invoked when parent frames are about to navigate.
       /// </remarks>
       public void OnNavigatingFrom(NavigatingCancelEventArgs e)
       {
           Debug.WriteLine("StepsControl- OnNavigatingFrom");
       }
   }

Using Debug.WriteLine we will show in output window the flow when we navigate in MainWindow.

The output will be something like

StepsControl - OnNavigatedTo
StepsControl - OnNavigatingFrom
StepsControl - OnNavigatedFrom
ResourcesControl - OnNavigatedTo
ResourcesControl - OnNavigatingFrom
ResourcesControl - OnNavigatedFrom
StepsControl - OnNavigatedTo
StepsControl - OnNavigatingFrom
StepsControl - OnNavigatedFrom
ResourcesControl - OnNavigatedTo

See the code source in github - ModernUIForWPFSample.Navigation (Default) .

Suppose when we navigate we want to do some task and we are using the  MVVM pattern. We could get the view model defined in DataContext and then call the methodwe want to apply what , something like

public void OnNavigatedTo(NavigationEventArgs e)
       {
           var viewModel = DataContext as MyViewModel;
           if (viewModel != null)
           {
               viewModel.DoSomething();
           }
       }

or we can define events that will be called when the methods from IContent are raised and this events can be defined in UI using the EventTrigger and InvokeCommandAction. With this we will avoid code in code behiden.

First we will create our ModernUserControl which is a UserControl and implements the IContent. This control will have four events, one for each method required for IContent.

The implementation will be something like

public class ModernUserControl : UserControl, IContent
  {
      /// <summary>
      /// Handles the <see cref="E:FragmentNavigation"/> event.
      /// </summary>
      /// <param name="e">The <see cref="FirstFloor.ModernUI.Windows.Navigation.FragmentNavigationEventArgs"/> instance containing the event data.</param>
      public void OnFragmentNavigation(FragmentNavigationEventArgs e)
      {
          if (FragmentNavigation != null)
          {
              FragmentNavigation(this, e);
          }
      }
 
      /// <summary>
      /// Handles the <see cref="E:NavigatedFrom"/> event.
      /// </summary>
      /// <param name="e">The <see cref="FirstFloor.ModernUI.Windows.Navigation.NavigationEventArgs"/> instance containing the event data.</param>
      public void OnNavigatedFrom(NavigationEventArgs e)
      {
          if (NavigatedFrom != null)
          {
              NavigatedFrom(this, e);
          }
      }
 
      /// <summary>
      /// Handles the <see cref="E:NavigatedTo"/> event.
      /// </summary>
      /// <param name="e">The <see cref="FirstFloor.ModernUI.Windows.Navigation.NavigationEventArgs"/> instance containing the event data.</param>
      public void OnNavigatedTo(NavigationEventArgs e)
      {
          if (NavigatedTo != null)
          {
              NavigatedTo(this, e);
          }
      }
 
      /// <summary>
      /// Handles the <see cref="E:NavigatingFrom"/> event.
      /// </summary>
      /// <param name="e">The <see cref="FirstFloor.ModernUI.Windows.Navigation.NavigatingCancelEventArgs"/> instance containing the event data.</param>
      public void OnNavigatingFrom(NavigatingCancelEventArgs e)
      {
          if (NavigatingFrom != null)
          {
              NavigatingFrom(this, e);
          }
      }
 
      /// <summary>
      /// Occurs when [navigating from].
      /// </summary>
      public event NavigatingCancelHandler NavigatingFrom;
 
      /// <summary>
      /// Occurs when [navigated from].
      /// </summary>
      public event NavigationEventHandler NavigatedFrom;
 
      /// <summary>
      /// Occurs when [navigated to].
      /// </summary>
      public event NavigationEventHandler NavigatedTo;
 
      /// <summary>
      /// Occurs when [fragment navigation].
      /// </summary>
      public event FragmentNavigationHandler FragmentNavigation;
  }

After it, StepsUserControl and ResourcesUserControl will be a ModernUserControl has following

<controls:ModernUserControl x:Class="ModernUIForWPFSample.Navigation.Views.StepsControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:controls="clr-namespace:ModernUIForWPFSample.Navigation__MVVM_.Controls"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             d:DesignHeight="300"
             d:DesignWidth="600"
             mc:Ignorable="d"
             DataContext="{Binding StepsViewModel, Source={StaticResource Locator}}">
    <i:Interaction.Triggers>
         <i:EventTrigger EventName="NavigatedTo">
            <i:InvokeCommandAction Command="{Binding NavigatedToCommand}" />
        </i:EventTrigger>
        <i:EventTrigger EventName="NavigatedFrom">
            <i:InvokeCommandAction Command="{Binding NavigatedFromCommand}" />
        </i:EventTrigger>
        <i:EventTrigger EventName="NavigatingFrom">
            <i:InvokeCommandAction Command="{Binding NavigatingFromCommand}" />
        </i:EventTrigger>
        <i:EventTrigger EventName="FragmentNavigation">
            <i:InvokeCommandAction Command="{Binding FragmentNavigationCommand}" />
        </i:EventTrigger>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding LoadedCommand}" />
        </i:EventTrigger>
        <i:EventTrigger EventName="IsVisibleChanged">
            <i:InvokeCommandAction Command="{Binding IsVisibleChangedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <Grid>
        <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap">
            Steps:
            <LineBreak />
            1st Install the ModernUI from Nuget
            <LineBreak />
            2nd Define in App.xaml the resources for ModernUI.xaml and ModernUI.Light.xaml
            <LineBreak />
            3rd Change the MainWindow: Replace the tag "Window" to ModernWindow
            <LineBreak />
            4th Define the content for the MainWindow using TitleLinks, MenuLinkGroups, LinkGroup...
            <LineBreak />
            5th Define which content is shown when the application start, by using the ContentSource
            <LineBreak />
            6th For each content (in this case for StepsControl and ResourcesControl) must implement the interface IContent 
            <LineBreak />
            7th For each navigation method (OnFragmentNavigation, OnNavigatedFrom, OnNavigatedTo and OnNavigatingFrom) add the behavior you want
            <LineBreak />
            <LineBreak />
            Note: For change the appearance use  AppearanceManager class
        </TextBlock>
    </Grid>
</controls:ModernUserControl>

For help in th MVVM pattern implementation we will use MVVMLight Toolkit and we will have two view models (StepsViewModel and ResourcesViewModel). Here are the class diagram

http://s29.postimg.org/aijz6i31z/image.png

ViewModelLocator will help in binding the view model to the view and is where we setup the dependencies when we use dependency injection.

When we run the application the output will be something like

ModernUserControl - OnNavigatedTo
StepsViewModel - LoadData
ModernUserControl - OnNavigatingFrom
StepsViewModel - NavigatingFrom
ModernUserControl - OnNavigatingFrom event called
ModernUserControl - OnNavigatedFrom
StepsViewModel - NavigatedFrom
ModernUserControl - OnNavigatedFrom event called
ModernUserControl - OnNavigatedTo
StepsViewModel - LoadData
ResourcesViewModel - LoadData
ModernUserControl - OnNavigatingFrom
ResourcesViewModel - NavigatingFrom
ModernUserControl - OnNavigatingFrom event called
ModernUserControl - OnNavigatedFrom
ResourcesViewModel - NavigatedFrom
ModernUserControl - OnNavigatedFrom event called
ModernUserControl - OnNavigatedTo
StepsViewModel - NavigatedTo
ModernUserControl - OnNavigatedTo event called
ResourcesViewModel - LoadData
StepsViewModel - LoadData

With it, we can conclude when we use events, the NavigateTo event is not raised at the first time the control is shown, it's because the NavigateTo event wasn't subscribed when the first navigation occurs.

See others solutions for Navigation in ModernUI applications.

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

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

Source code

Get the source code for this sample in github.