XAML Playground
about XAML and other Amenities

Implement a NavigationService for MVVM in Metro Applications

2012-05-10T00:37:56+01:00 by codeblock

When you write a Metro-style app using the MVVM pattern, you are requested to abstract navigation for the pattern purposes. The problem comes from having navigation methods (Navigate, GoBack, etc...) available only in the View via the Frame control and navigate from the ViewModel may be an hard task.

During the development of my last metro app for my user group, I found a stylish way of creating a NavigationService to be injected in ViewModels. The trick is to create a NavigationService class and let it create and initialize the RootFrame and inject it into the Window.Current.Content.

   1: public class NavigationService : INavigationService
   2: {
   3:     protected Frame RootFrame { get; private set; }
   4:  
   5:     public NavigationService()
   6:     {
   7:         this.RootFrame = new Frame();
   8:     }
   9:  
  10:     public void Initialize(Window window, bool activate = true)
  11:     {
  12:         if (window.Content == null)
  13:             window.Content = this.RootFrame;
  14:  
  15:         if (activate)
  16:             window.Activate();
  17:     }
  18:  
  19:     public virtual void Navigate(Type destination, object parameter = null)
  20:     {
  21:         // avoid navigation if current equals to destination
  22:  
  23:         if (this.RootFrame.CurrentSourcePageType != destination)
  24:             this.RootFrame.Navigate(pageType, parameter);
  25:     }
  26:  
  27:     public virtual void GoBack()
  28:     {
  29:         if (this.RootFrame.CanGoBack)
  30:             this.RootFrame.GoBack();
  31:     }
  32:     
  33:     public virtual void GoForward()
  34:     {
  35:         if (this.RootFrame.CanGoForward)
  36:             this.RootFrame.GoForward();
  37:     }
  38:     
  39:     public virtual bool CanGoBack
  40:     {
  41:         get { return this.RootFrame.CanGoBack; }
  42:     }
  43:     
  44:     public virtual bool CanGoForward
  45:     {
  46:         get { return this.RootFrame.CanGoForward; }
  47:     }
  48:     
  49:     public virtual void Save()
  50:     {
  51:         throw new NotImplementedException();
  52:     }
  53:     
  54:     public virtual void Load()
  55:     {
  56:         throw new NotImplementedException();
  57:     } 
  58: }

The previous box shows the code of my service: first of all the service implements an interface. This serves for the dependency injection since ViewModel only accept INavigationService to make it testable. The Initialize method sets the Window.Content to the frame instance I created in the constructor. Finally the class implements all the required navigation methods. Interesting to say, you can implement Save and Load using Framee.GetNavigationStatus and Frame.SetNavigationStatus to persist the navigation backstack when your app is terminated. Here is how to use the class in the App.xaml.cs:

   1: protected override void OnLaunched(LaunchActivatedEventArgs args)
   2: {
   3:     // get the instance from Injection Container
   4:     INavigationService ns = this.Container.Resolve<INavigationService>();
   5:  
   6:     if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
   7:     {
   8:         // load state if app was terminated
   9:         ns.Load();
  10:     }
  11:     
  12:     // initialize current window
  13:     ns.Initialize(Window.Current);
  14:  
  15:     // navigate to the first page
  16:     ns.Navigate(Pages.MainPageType);
  17: }
  18:  
  19: private void OnSuspending(object sender, SuspendingEventArgs e)
  20: {
  21:     // get the instance from Injection Container
  22:     INavigationService ns = this.Container.Resolve<INavigationService>();
  23:  
  24:     // save navigation status
  25:     ns.Save();
  26: }

This code shows also how to load and save to persistence media the navigation history. In this way when you return to the app after it has been terminated, it can reload again an exact navigation.