Monday, April 30, 2012

Navigation in Cocoon – MVVM for Metro-style apps

Over the next series of posts I’m going to introduce a new subsystem within the Cocoon framework for Metro-style applications written in managed languages.
Modern rich client applications often consist of a number of interconnected pages or views. For example consider the Windows 8 “Store”. This has a home page that acts as a hub the links to a number of category pages, which in turn link to the individual application detail pages. The user navigates forward through the application flow by clicking images, buttons or links. They can also navigate backwards by using a back button that is placed in the top left hand corner of each page. In addition there are ‘special pages’ such as search, or even the settings panes available from the settings charm.

There are a number of challenges for developers who wish to correctly implement such a navigation structure.

Navigation in Cocoon

Although not limited to this, the Cocoon framework is designed to work great with the Model-View-ViewModel (MVVM) pattern that has become common when designing XAML based applications. One problem when using this pattern is that you need some way of associating a view with a view-model. Often this is done by navigating directly to a page, and using a “ViewModelLocator” to identify and wire up the respective view-model.

In Cocoon a slightly different approach is used. Here, rather than navigating directly to a view, you navigate to a named page. Although the behaviour is extensible, by default Cocoon will use MEF (the built in composition framework included in .Net) to locate both the view and view-model associated with this page name, create and initialise instances of these and wire them together.

You can export any XAML view (for example the pages created by any of the Visual Studio templates) by adding the ‘PageExport’ attribute to the code-behind file,

[PageExport("BrowsePhotos")]
public sealed partial class BrowsePhotosPage : MyPhotoApp.Common.LayoutAwarePage
{
    public BrowsePhotosPage()
    {
        this.InitializeComponent();
    }
}

And similarly you can export the view-model using the ‘ViewModelExport’ attribute,


[ViewModelExport("BrowsePhotos")]
public class BrowsePhotosViewModel
{
    ...
}

The Navigation Manager

In order to navigate between pages, Cocoon includes a navigation manager that can be accessed by importing the INavigationManager interface through MEF (simplified by the fact that all views and view models in Cocoon are themselves are composed by MEF).


public interface INavigationManager
{
    bool CanGoBack { get; }
    
    ...
 
    void GoBack();
    void NavigateTo(string pageName);
    void NavigateTo(string pageName, object arguments);
}

The most interesting methods are the two overloads of ‘NavigateTo(…)’ that both take the page name to navigate to. The navigation manager will also keep track of the current navigation stack so that you can easily use the ‘CanGoBack’ property and ‘GoBack()’ method to traverse backwards through the previously visited pages.

For example we could add methods to our BrowsePhotos view model as such,


[ViewModelExport("BrowsePhotos")]
public class BrowsePhotosViewModel
{
    // *** Fields ***
 
    private readonly INavigationManager navigationManager;
 
    // *** Constructors ***
 
    [ImportingConstructor]
    public BrowsePhotosViewModel(INavigationManager navigationManager)
    {
        this.navigationManager = navigationManager;
    }
 
    // *** Command Methods ***
 
    public void ViewPhoto()
    {
        navigationManager.NavigateTo("ViewPhoto");
    }    
    
    public void NavigateBack()
    {
        navigationManager.GoBack();
    }
}

Summary


I have shown above how the Cocoon navigation framework allows you to easily construct a page based navigation structure for applications based upon the MVVM pattern. I have of course glossed over how you navigate to the first page of your application. I will discuss this further next time.

As usual the code is freely available for download from the Cocoon CodePlex site (to get the latest version go to the “Source Code” tab, select the first change set and use the “Download” link).

No comments: