Silverlight Navigation With the MVVM Pattern

I recently had a query from a customer that was one of those ones that you think “Aha! That’s easy, you just do this”. Then you think a bit more about it and realise that if you want to do it properly it’s not quite as simple as you first thought.

This particular query related to how to use the navigation framework introduced in Silverlight 3 with the MVVM pattern. For those that don’t know the Model-View-ViewModel pattern is a common pattern often used to build Silverlight and WPF applications since it has strong support for databound UIs and provides good abstraction for unit testing and keeping your view logic separate from the view and the model.

Enter the Silverlight Navigation Framework

Handling the page style navigation that we’ve all become used to on the web can be a real pain in RIAs written using Silverlight, Flash or AJAX involving lots of tracking of page clicks using HTML bookmarks and some liberal use of JavaScript. Fortunately Silverlight 3 added a navigation framework to help with this.

This framework works by you adding a Frame element to your root XAML and then creating many Page derived classes. These classes can then navigate between each other by calling into a NavigationService instance that each page inherits from it’s base class.

The issue with MVVM is that page navigation is a view logic, that means that it should sit inside the ViewModel, however the NavigationService is only on the Page class which the ViewModel doesn’t have access to.

Solving this problem is simple, just pass the NavigationService instance to the ViewModel, job done! Well ok, that works if you hate unit testing. If you like unit testing though you may find you have problems then when it comes to testing your ViewModel as you won’t have a NavigationService instance to pass it.

Additionally since many pages are likely to want to do this I’d like to have a reusable approach that doesn’t take much code to reuse.

The Solution

The solution I came up with is one of a few different ways you could implement this but this works for me at the moment. Feel free to point out any glaring errors in my design though.

Wrapping the NavigationService

First I need to make the NavigationService mockable so I can use it in unit tests. To do this I created a new interface INavigationService that exposes the methods and properties of the NavigationService. This sample version only exposes Navigate() but you could easily expose more as needed.

    public interface INavigationService
    {
        void Navigate(string url);
    }

Not the most complex interface ever devised you’ll agree.

Next I created a class that implemented the interface and took a reference to a System.Windows.Navigation.NavigationService on it’s constructor.

    public class NavigationService : INavigationService
    {
        private readonly System.Windows.Navigation.NavigationService _navigationService;

        public NavigationService(System.Windows.Navigation.NavigationService navigationService)
        {
            _navigationService = navigationService;
        }

        public void Navigate(string url)
        {
            _navigationService.Navigate(new Uri(url, UriKind.RelativeOrAbsolute));
        }
    }

Supporting INavigationService in the ViewModel

Now I had an abstraction I needed to pass the INavigationService to the ViewModel. I wanted to do this in a standard way. I could have made it a constructor argument but I couldn’t always guarantee I’d be there at construction. The best way seemed to be to add a property. I decided to put that property on an interface so I had a defined contract that ViewModels could support.

INavigable.

    public interface INavigable
    {
        INavigationService NavigationService { get; set; }
    }

This interface provides a single property that a ViewModel can implement that will contain a reference to the INavigationService that the ViewModel should use to perform navigation when it needs to.

Passing the INavigationService to the ViewModel

Next I need to create an instance of the NavigationService that wraps the System.Windows.Navigation.NavigationService in the Page class and pass that to the ViewModel. I’d like this to be reusable code and if possible I don’t want any code behind in my View.

This is a perfect use for an attached behaviour. What’s one of those? It’s simply an attached property with a property changed handler on it that hooks up code to the DependencyObject that the property gets attached to. It’s simpler than it sounds and is a nice way of making reusable logic that you want to attach to objects in XAML.

The Navigator.

    public static class Navigator
    {
        public static INavigable GetSource(DependencyObject obj)
        {
            return (INavigable)obj.GetValue(SourceProperty);
        }

        public static void SetSource(DependencyObject obj, INavigable value)
        {
            obj.SetValue(SourceProperty, value);
        }

       public static readonly DependencyProperty SourceProperty =
            DependencyProperty.RegisterAttached("Source", typeof(INavigable), typeof(Navigator), new PropertyMetadata(OnSourceChanged));

        private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Page page = (Page) d;

            page.Loaded += PageLoaded;
        }

        private static void PageLoaded(object sender, RoutedEventArgs e)
        {
            Page page = (Page)sender;

            INavigable navSource = GetSource(page);

            if (navSource != null)
            {
                navSource.NavigationService = new NavigationService(page.NavigationService);
            }
        }
    }

This class provides a single attached property definition of the type INavigable. This property has a handler that when invoked grabs the DependencyObject you are attaching the property to and hooks up it’s Loaded event.

So when you attach this property to a Page instance in XAML it will fire off the PageLoaded method when the Page you attach it to loads. In the page load handler I then query the Page instance for the Source attached property. Remember that this property is of type INavigable. If the source supports INavigable I then create a new instance of the NavigationService, wrapping the Page’s instance and set it on the source using the INavigable.NavigationService property.

And there we have it, a reusable way of attaching the navigation service instance for a Page to the Page’s view model.

The View XAML

And finally using the attached property is a case of assigning the ViewModel to the DataContext of the Page as normal and then doing this.

<navigation:Page x:Class="SLNavigation.Page1"
           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"
           mc:Ignorable="d"
           xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
           xmlns:SLNavigation="clr-namespace:SLNavigation"
           d:DesignWidth="640" d:DesignHeight="480"
           Title="Page1 Page"
           SLNavigation:Navigator.Source="{Binding}"
    >

Summary

So that’s it, my solution. I think it’s reasonable, it minimised the amount of code needed in the View code behind to nil, makes the only code you need on the ViewModel is to derive from and implement INavigable which is one property and to put an attached property on the Page in the XAML. It keeps a good separate of concerns as the ViewModel is still unit testable but it can now support navigation.

Let me know what you think.

Sample code: NavigationSample

*edit* Added a sample

21 thoughts on “Silverlight Navigation With the MVVM Pattern

  1. Hi, I’ve followed your step but I don’t know how can I use your NavigationService with View, can you gimme sample solution?

  2. Hi David, I downloaded your code, but was surprised to find a code in your codebehind for both views that handles the button click events.
    Could you show us how to do the binding in XAML instead of in code-behind (as recommended by the MVVM pattern)?
    Thank you.
    Tami

    • Hi Tami,

      My name is Robert, not sure where you got David from! 🙂

      You are correct, I did indeed put event handlers in the code behind for the buttons, that was mainly for clarity of not trying to demo several things at once as supporting commands in MVVM wasn’t the purpose of the post. I may put another one up later covering that but the gist of it would be you can either use a behaviour (from System.Windows.Interactivity) or write an attached property. The benefit of the former is that a designer can hook it up but you’d need to write a behaviour for each action since you can’t databind with them (as far as I know). The benefit of the latter is you can databind but a designer would have to edit the XAML to hook them up.

      If you look at Composite Application Guidance for WPF and Silverlight, then the Silverlight version has an attached property for hooking up commands to button click handlers.

    • This approach is indeed much simpler and would work. However there are a few things going on here. The first is that you want to be able to navigate so we need to ensure that actually is possible. Both solutions offer that and as you pointed out mine is a little more complex, although once implemented it’s pretty simple to use.

      But then you need to ask why are we using a view model and the MVVM pattern? For me it’s for a couple of reasons, separation of concerns and testable code. I want a nice testable view model so that I can automate unit testing and I want to encapsulate the translation of the model into view specific data and actions so it’s not proliferating throughout my views. There is some debate whether the view code behind should contain any code at all, I think that pragmatically it can but it should be limited to things that a totally view specific such as animation support etc.

      The issue I have with the solution you linked is that it will work but it will tightly couple the view model to the application’s root visual which is a UI element. This breaks my ability to unit test my view model. Imagine I had a command on my view model that when invoked caused navigation to occur. How would I test this in a unit test framework?

      With the approach I’ve taken I can mock out the INavigationService, instantiate the view model, pass it my mocked INavigationService via the INavigable interface and invoke it’s command. I can then check my mock to see if it was indeed told to navigate to the expected page.

      In my actual code I’ve also extended the INavigable interface to support extra methods too, an action for Navigated and a func for Navigating that can return a bool which will stop navigation. You could do this with the other approach but it would further couple to the UI element, how would you raise the navigating events from the PhoneApplicationFrame class into your view model? With my approach I can just call the OnNavigating or Navigated methods on the INavigable interface and test their behaviour.

      At the end of the day its up to what’s important for you. Directly binding to the PhoneApplicationFrame is possible and much simpler, however it’s far less testable.

  3. Hi Robert,

    I like this solution, but I want to avoid instantiating the view models on the code behind.
    Now, if i try this solution with the normal way to instantiate the viewmodel on the xaml (add it as resource for the page, the set the layout root’s data context to this resource), then the solution stops working cause i can’t bind the navigation.source property the view model.

    do you have any thoughts on how this could be circumvented?

    thanks

  4. Hi Rob

    Wonderful example indeed, good work and thanks for your post. Especially the way you hooked on the INavigation service, and INavigable to hook on to the current visible page’s NavigationService.

    I also tried adding Forward() functionality, but then I found the System.Windows.Navigation.NavigationService a bit buggy (I’m not sure if I’m missing any configuration).
    Here’s what: after debugging it, I found something rather strange; in NavigationService.Back()
    public void Back()
    {
    if(_navigationService.CanGoBack)
    _navigationService.GoBack();
    }
    when the _navigationService.CanGoBack is false and the page actually navigates back! and when its true, it throws Exception!

    So I now maintain a stack of my own (of the list of pages visited, for navigating back/forward), and just keep doing the NavigationService.Navigate() every-time, and not using the _navigationService.GoBack(); method (or the _navigationService.GoForward(); methods) for at all.

    If you think, I’m missing something, your comments would be greatly appreciated.

    Thanks
    Vinay

  5. I have spent some time playing around with this sample, it works fine when there are code behind on the page.

    But I have a very hard time getting it to work without code-behind on the page. The DependencyProperty SourceProperty doesn’t work properly with just the Navigation:Navigator.Source=”{Binding}” hookup in the XAML of the page. It get called from the XAML code, but it doest fire the other members of the Navigation class, as it does when doing code-behind.

    But If I do a DataContext = new Page1ViewModel(); in the constructor of the code-behind on the page it all works fine.

    So I would be pleased to hear from anyone who has succeeded in getting this sample to work with commands and no code-behind on the page, thanks in advance.

    Thomas

  6. Hey

    I found out, that your code isn’t working, when I try to run the code from the same page/usercontrol, where the Navigation is in.
    Let me explain:
    In your solution you work with a MainPage, where the Navigation is in it. And then call the Pages, which you want. So this code line: myNavigator:Navigator.Source=”{Binding}
    got’s information about the Navigation
    But when I do the same in only one page. (from this page I want to call an other page), then it doesn’t work, because the Navigator Property is NULL.

    Could you please provide me a solution for this problem? – Where can I bind the Navigator in XAML?

  7. Rob,

    Not sure if you are still monitoring this page or not, but I have a question about the Navigation when your pages are of type UserControl” instead of “Page”. Page inheirts from UserControl and has the NavigationService as an added property. Do you use only Page controls for all your views or if you use UserControls for them how would you modify this code?

    Thanks

    dbl

  8. Hello Sir
    I am using your sample page code to navigate my current page to another page. Actually i have implement INavigble interface to my base class and inherit that base class to ViewModel class. but i am getting an object reference exception…
    please let me know

  9. How do you pass data between views (Person List, Add Person, Edit Person & delete Person views)?

  10. Thank you very much for sharing such an elegant solution with everyone. Everything worked out just as how you described it. My only concern now is that my view model now refers to one of my pages (actually more like a url to my page) but I guess there ain’t no such thing as a free lunch.

  11. I was trying to download the NavigationSample code you uploaded but it is no longer available. I needed to see how you call the navigation service from the viewmodel.

    • Sorry, it looks like when I moved my blog I missed the samples folder and I no longer have that code. Basically I injected an INavigate interface into the view model which had a Navigate method which the implementation of INavigate method maps to the Navigate method on the frame.

Comments are closed.