Magellan 2.0 Feature Preview

If you have been following the Magellan change log, you might have seen some pretty big changes go through recently. I'm preparing for a Magellan 2.0 preview release with some pretty significant features, which also require some design changes.

Rethinking Magellan

Previously, Magellan was all about the MVC pattern, taking the learnings from ASP.NET MVC and applying them to WPF. Magellan 2.0 builds around that, creating a new layer that makes Magellan a general navigation framework rather than just an MVC framework.

The following diagram gives an idea of the major components of Magellan in 2.0:

Magellan 2.0 component diagram

Routing takes center stage in Magellan 2.0, with a route being registered with a route handler. This allows you to have different handlers associated with different routes. For example:

var routes = new RouteCollection();
routes.Register("R1", "patients/{action}", new ControllerRouteHandler(controllerFactory));
routes.Register("R2", "message/{message}", new MessageBoxRouteHandler());

This code creates two routes, each with a different route handler - one using Magellan's MVC support, the other using a custom route handler. Route handlers look something like this:

public class MessageBoxRouteHandler : IRouteHandler
{
    public void ProcessRequest(NavigationRequest request)
    { 
        var message = request.RouteValues["message"];
        MessageBox.Show(message);
    }
}

A route collection is then associated with a Navigator Factory. The navigator factory creates and manages navigators associated with frames. For example:

var navigation = new NavigatorFactory(routes);
var navigator = navigation.For(myFrame);

Navigators can be created for:

  1. Frame controls
  2. Plain old ContentControls
  3. Anything implementing INavigationService

A navigator factory is used to create a navigator for a frame - think of it the way NHibernate's ISessionFactory creates ISessions for each session. The navigator then controls all navigation within that frame.

In a multi-tabbed navigation application, you would generally have one route collection, one navigation factory, and many navigators, one per frame.

A key concept is that Navigators only know how to resolve a navigation request to a route, and execute the route handler. They don't know anything about MVC, MVVM, or any other patterns - they just know about IRouteHandlers.

This is nice, because it decouples three different concepts:

  1. Frame management
  2. Request->route matching
  3. Presentation patterns

Navigation-aware MVVM

Magellan 2.0 will include full MVVM support. Previously, you could use external MVVM frameworks with Magellan, but only if you used them alongside MVC. Now, you'll be able to use MVVM without MVC, and I'll include an MVVM framework out of the box (though you can still use your own).

For example, the following routes use MVVM:

var routes = new RouteCollection();
routes.Register("R1", "patients/list", new ViewModelRouteHandler<ListPatientModel, ListPatientView>());
routes.Register("R2", "patients/edit/{patientId}", new ViewModelRouteHandler<EditPatientModel, EditPatientView>());

var navigation = new NavigatorFactory(routes);
var navigator = navigation.For(MainWindow);     // MainWindow is a ContentControl
navigator.Navigate("patients/list");            // Shows the ListPatientView with view model

The ViewModelRouteHandler will resolve the view and view model, and set the model as the data context. Route parameters will be passed as constructor arguments to a view.

If the view or view model implements INavigationAware, it will be given the navigator that it was loaded within. For example, our List Patient view model might navigate to Edit Patient:

public class ListPatientModel : ViewModel, INavigationAware
{
    public ListPatientModel() 
    {
        Edit = new RelayCommand<PatientRecord>(EditExecuted);
    }

    public ICommand Edit { get; private set; }

    public INavigator Navigator { get; set; }

    private void EditExecuted(PatientRecord record)
    {
        Navigator.Navigate(patientId => record.Id);
    }
}

Summary

Magellan 2.0 will support the following combinations of usage scenarios:

  1. Navigation containers:
    1. You want to build page/frame based applications, with back/forward capability
    2. You just want a place to put views, without back/forward support (ContentControl)
  2. Navigation frames:
    1. You just one frame of navigation
    2. You have multiple frames of navigation, that can possibly be closed or opened dynamically (tabbed browsing)
  3. Presentation patterns:
    1. You want to use an ASP.NET-like MVC framework
    2. You want to use an ASP.NET-like MVC framework, with a ViewModel for each view
    3. You want to use MVVM without MVC
    4. You want to use MVVM without MVC, and you have your own/a third party MVVM framework
  4. Platforms
    1. You want to use WPF
    2. You want to use Silverlight

I am very interested in what you think of these plans, and whether you like the new design for navigation. Let me know what you think in the comments :)

A picture of me

Welcome, my name is Paul Stovell. I live in Brisbane and work on Octopus Deploy, an automated deployment tool for .NET applications.

Prior to founding Octopus Deploy, I worked for an investment bank in London building WPF applications, and before that I worked for Readify, an Australian .NET consulting firm. I also worked on a number of open source projects and was an active user group presenter. I was a Microsoft MVP for WPF from 2006 to 2013.