Xamarin Forms - View Model First Navigation

Hi! I released a NuGet package that does VM First Navigation! This article here still contains valuable info - but this other article has the necessary info to start using the VM First NuGet package.

I recently started a brand spanking new Xamarin Forms project and I wanted to have the flow of the app run through the view model layer. In other words I wanted the currently active view model to determine which other view model should be loaded next, and then have the app display the correct associated view … all without the view model layer knowing anything about the views. The only problem … there isn’t a way to perform this “view model first” navigation in Xamarin Forms out of the box.

Naturally my next step was off to Google to see what I could find. I came across a couple of things, but nothing that suited my needs 100%. So I took my experiences with MVVMCross (which has a very nice implementation of view model first navigation), ReactiveUI and an app built with ReactiveUI called CodeHub, and a great article by Jonathan Yates in his Adventure in Xamarin Forms series … mashed them all together and came up with the following view model first framework for Xamarin Forms.

I can’t say with 100% certainty this is going to work in every situation – but so far it’s working well in my project with TabbedPage and NavigationPage’s. If you have any comments one way or the other – or you decide to use it, let me know what you think!

(I just found that Xamarin’s own James Clancey has his own version of view model first navigation, and overall it’s not too far from what I have … so I must be going down the right path)!

INavigationService

In order to keep the view models blissfully unaware of how the views perform navigation – it’s best to wrap Xamarin Forms’ navigation into a service in order to abstract it from the view models. Xam.Forms provides an interface INavigation which provides 5 navigation metaphors that I wanted the view models to also follow: PushAsync(Page) , PushModalAsync(Page) , PopAsync , PopModalAsync , PopToRootAsync . INavigation has more functions, but those are the 5 we’re going to start with.

So we want to provide something that models those metaphors – but instead of operating on Page ‘s operate on view model objects. So the first crack at our navigation service interface will look like this:

public interface INavigationService
{
    Task PopAsync();
    Task PopModalAsync();
    Task PushAsync(BaseViewModel viewModel);
    Task PushModalAsync(BaseViewModel viewModel);
    Task PopToRootAsync();
}

You’ll notice the push operations are working on a BaseViewModel class instead of a Page class.

That’s great – now let’s start talking about the concrete implementation of this so we can actually do some view model first navigation! (And for what it’s worth, the BaseViewModel that I’m using here is from James Montemagno’s MVVM Helpers library. Something you should check out – lots of stuff to make life easier when doing MVVM).

Pairing Views To View Models

Because our INavigationService interface specifies only view models as inputs, the concrete implementation of it will need to have some way of looking up which view belongs to the passed in view model, so the service can instantiate the proper view and show it.

We could expose a dictionary object and manually link up all the view to view model associations ourselves – or we could use the magic of reflection to do most of the work for us!

ReactiveUI has an interface called IViewFor and IViewFor<T> which you use to specify which view belongs to which view model. Although they are not used for view model first navigation in RxUI, an app called CodeHub does make use of them for that purpose.(It was pointed out to me that RxUI does indeed use IViewFor to provide routing – see Kent’s comment below. My intention here was to note that in a traditional Xamarin (i.e. not Xam.Forms) app w/ RxUI – view model first routing isn’t something that’s normally done.) Following their lead, the interfaces look like:

public interface IViewFor
{
    object ViewModel { get; set; }
}
 
public interface IViewFor<T> : IViewFor where T : BaseViewModel 
{
    T ViewModel { get; set; }
}

As you can see they expose a single property, ViewModel , which in the generic case needs to be a BaseViewModel.

By having every Page class in our app implement this interface we get a two-fer … one we get a strongly typed ViewModel property in the view in case there is anything we need to do with it in the presentation logic (hopefully not). And we’ll also be able to use reflection to find everything that implements IViewFor<T> and the T it implements and put them into an internal dictionary the navigation service maintains without manually having to register every view – view model combination ourselves! (Kind of MVVMCross-ish).

Of course we’ll still have to call a “startup” function in order to kick off all the registrations, so let’s add void RegisterViewModels(System.Reflection.Assembly asm) to the INavigationService. The actual implementation of RegisterViewModels is on GitHub here.

In order to run it, we’ll need to tell RegisterViewModels which assembly holds the views and view models it’s looking for. If you’re using a PCL based project, you’ll need to call this in the platform specific project. If you’re doing a shared project, you don’t have to call it in the platform specific one, it can be in the App class’s constructor let’s say, but may as well put it into the platform specific project… like the following for iOS:

[Register ("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
    public override bool FinishedLaunching (UIApplication app, NSDictionary options)
    {
        global::Xamarin.Forms.Forms.Init ();
 
        // Registering the views to view models
        INavigationService navService = new NavigationService ();
        navService.RegisterViewModels (typeof(RootTabPage).Assembly);
 
        LoadApplication (new App ());
 
        return base.FinishedLaunching (app, options);
    }

Creating The Views

OK – the view and view model associations have been registered in the service, now it’s time to create the view based on the view model that gets passed in. Using the internal dictionary the service maintains, we’ll be able to look up the view based on the view model type, create it, and since the view will implement IViewFor – we’ll also be able to set its ViewModel property right away as well. (And we can also set the binding context right away or have the ViewModel property implementation set them for us).

The function to create the view will look like the following:

private IViewFor InstantiateView(BaseViewModel viewModel)
{
    // Figure out what type the view model is
    var viewModelType = viewModel.GetType();
 
    // look up what type of view it corresponds to
    var viewType = _viewModelViewDictionary[viewModelType];
 
    // instantiate it
    var view = (IViewFor)Activator.CreateInstance(viewType);
 
    view.ViewModel = viewModel;
 
    return view;
}

Finally – we’re getting to the point where we wanted to be – actually performing the navigation! To move about within Xamarin Forms, our class implementing INavigationService will need to have a reference to Xamarin Forms’ INavigation . The easiest way to get at that would be to use App.Current.MainPage . So let’s create an internal property that takes into account that our main page may be a TabbedPage or something else:

INavigation FormsNavigation { 
    get {
        var tabController = App.Current.MainPage as TabbedPage;
 
        if (tabController != null) {
            return tabController.CurrentPage.Navigation;
        } else {
            return App.Current.MainPage.Navigation;
        }
    }
}

Finally, an implementation of a push operation in the service would look like:

public async Task PushAsync (BaseViewModel viewModel)
{
    var view = InstantiateView (viewModel);
 
    await FormsNavigation.PushAsync ((Page)view);
}

Wrapping It Up

Once all of the plumbing is done, all that’s left is to utilize the service within our view models! Obviously because the service needs to go through a registration process to get at view to view model associations we want to keep it around and not instantiate it every time. You can do this through a number of ways – in the example posted on GitHub I’m using Splat’s service locator (for no reason other than it’s easy to work with). Using an IoC container may be something you’d want to do too.

Then to actually perform a navigation within a view model – you’ll end up with something like this:

await _navService.PushAsync (new TabTwoChildViewModel ());

That’s all there is to it! As I said, this is a rough first go at it that I’m currently using on a project. (In fact as I was writing this post I thought of a couple refactorings I could make, but nothing huge … yet). There is a full working project on GitHub and as I make changes and refine it, I’ll be sure to keep that up to date.