You don’t need an MVVM framework to have a view factory and view model navigation in your Xamarin.Forms app

Motivation

Quite often I get asked what MVVM framework I use when creating apps, or I’m presented a list of MVVM helpers/frameworks/toolkits and am supposed to pick my preference. Let me admit that I indeed do have a preference (it is the MvvmLight toolkit) but this does not mean that my choice is the right choice for you, your project or your team. In fact, I think that there’s quite some misconception going on: many think that using a framework is a requirement to build an MVVM based app. That’s not true. Let’s see what the definition of MVVM is over at Wikipedia:

MVVM facilitates a separation of development of the graphical user interface – be it via a markup language or GUI code – from development of the business logic or back-end logic (the data model). […] MVVM is a variation of Martin Fowler’s Presentation Model design pattern.

There isn’t a single reference to a specific framework, in fact it is not even limited to a specific platform. It is all about decoupling your UI from the actual logic – that’s it. The frameworks and toolkits out there can help you reduce the amount of code you have to write. Code that would otherwise be repetitive and they also add nice features like handling view model navigation which is the topic I want to cover in this blog post.

My intention is to show you that you can focus on creating your app instead of spending too much time on finding the “right” framework. Also, you might not need most of what these frameworks give you or you don’t agree on the way things work in them, or whatever the reason may be.

Scenario

Here, I’m using Xamarin.Forms to demonstrate how to create a simple view model (VM) navigation combined with a view factory that allows having views specific on a target platform without “if-this-then-that” code all over the place. I created my code so that it can be used on any other UI framework; it has no hard dependencies on Xamarin.Forms. However, if you would like to learn more about MVVM and Xamarin.Forms, be sure to check out the course content available over at Xamarin.University!

You can find my demo project on Github.

ViewModel navigation

There are many definitions of what a VM is but the one that works best for me is is actually really simple:

A VM is an abstraction of the view.

A bit more explanation might be required. Imagine a landscape here on beautiful planet earth and let’s try to build a VM: it would have properties for all the things that are part of the scenery, for example a List<Rock> Rocks {get; set; } and a method to Walk() through the scenery. Now let’s put ourselves into the role of a Martian who is living on the red planet. The same VM would work for him too, but his view would be an entirely different one. In other words: we should not care about the actual view but work with the VMs instead Disclaimer: this is not always possible; some operations depend on the actual environment, like “walking on earth” might map to “floating with rocket boots on mars”. But the effect remains the same, at the end of the day we walked/moved/hovered/navigated to the destination we wanted to get to – and this brings us to VM navigation!

If a VM is our view of the world it makes sense to navigate from one VM to another. How that transition actually happens is secondary, we want to be able to go from location A to location B, or better from view A to view B.

Again, there are plenty of ways to implement VM navigation and all major MVVM frameworks support it but what if you want to keep your code free of too many dependencies? Well, then just run your own version. It’s easy.

Abstracting the view model

Navigating VMs requires us to have a VM implementation. I don’t know how you build your VMs, so I thought let’s agree on an interface maybe? Here’s my proposal:

It is a pretty rudimentary version and you might want to extend it with additional methods needed for your app but let’s go through it. We find a NavigateAsync() method which allows us to navigate to another VM. This method accepts an optional parameter, data that you might want to pass on to the VM you are about to navigate to. Notice that we are not navigating to a concrete instance of a VM but just specify the type. We’ll get into the details a bit later. In addition, there is an InitAsync() method – also taking a parameter for optional data – which can be used to (you guessed it) intialize the concrete VM implementation because often VMs need to load some data from a database or from a web service.

An interesting property is NavigationCallback. Remember that the VM does not know anything about the underlying UI platform and thus navigation must be delegated. In Xamarin.Forms this means that the page has to push the target page onto the navigation stack or maybe has to switch to a different tab. Wrapping up: if we call NavigateAsync(), a bit of code runs (later more) and then the callback will be used to perform the actual navigation.

Abstracting the view

The next puzzle piece is an abstraction of the views we’re dealing with. Again, I don’t know what platform you’ll be using, so I did not want to create any dependencies. A view is represented by an interface called IView:

This is really simple and when using Forms you would make your pages implement IView. The only job of this interface is to provide a way for connecting a view to an existing VM, hence the IViewModel type property. When looking at the view factory implementation, things will start to make more sense because that’s what is bringing everything together.

View factory

With the two interfaces IViewModel and IView the job is almost done. Implementing the interfaces on concrete VMs and views (Pages in Xamarin.Forms) one would be able to navigate from one page to another and assign a VM to a view which the page will then have to set as its binding context. However, by adding a view factory we get some benefits which are worth the extra step:

  • Central place to register pairs of VM and view (Example: VM type ItemDetailsViewModel -> ItemDetailsPage)
  • Possibility to register platform specific views (Example: want to have a different view on iPad? Just register it and don’t have any “if-iPad-then-navigate-like-this-otherwise-navigate-like” code)
  • Automated instance creation of both VM and view
  • Automatic assignment of VM to created view

Let’s have a look at these points in more detail. In the demo project you’ll find the ViewFactory.cs file as part of the MvvmHelpers project. Here is a version with removed comments for brevity:

That’s definitely more code than the interfaces from before. Hold my hand, I’ll guide you. The view factory has two dictionaries which either map from a VM type to a view type or from a VM type to a concrete view instance.

Next we find a singleton to make sure there’s only one view factory ever.

The two Register() methods are pretty straightforward: they simply add entries to one of the dictionaries. The magic then happens in Create() which exists in two flavours.

The job of the view factory is to enable registration of pairs of VM and view, create a VM instance and a matching view, assign the VM to the view and return the view back t the caller.

If you look at the code you’ll find that Create() performs these steps:

  1. Verify that the requested type represents an IViewModel.
  2. Check if the VM has been registered.
  3. Create an instance of the IView, either via the default constructor or via a registered creator callback or use a registered view instance.
  4. Get an instance of the VM by calling a VM factory.
  5. Assign the VM to the created view and return the view.

We will see how the IViewModel and IView interfaces and the view factory can help structure an MVVM based app (in my example a Xamarin.Forms app). But let’s quickly focus on one of the topics above before we move on: to create an instance of the VM, the view factory is not using reflection directly like it does for the views but instead calls to a VM factory function which must be assigned to the ViewModelFactory property. I did not have this feature in there in the beginning but found it quite useful, because I thought: what if there is already an IoC container in use that’s supposed to inject dependencies into the VM? Using this factory property allows you to create the VMs or even return existing instances of a VM if you want to reuse one and run the VM creation through whatever DI container you are currently using.

How to use

By adding the two interfaces and the view factory to your project you are ready to explore the world of MVVM navigation without using a complex framework. Here’s how it works in an app that shows a list of items and if one is selected, navigates to a detail screen by pushing the detail page onto the navigation stack.

The view models

In my example Xamarin.Forms app the first step is to create the VMs and views. I created a simple BaseViewModel but you don’t have to do this and can just as well implement the IViewModel interface directly on each of your VMs.

It has the usual support to easily raise the INotifyPropertyChanged event but also implements IViewModel as abstract methods. An actual VM implementation can be seen here:

Notice how this is used to show a list of all items and since it does not need any specific initialisation it simply returns a completed task in InitAsync(). The VM also has a command that gets called if an item in the list of items was selected. This is what triggers the navigation to the details VM by calling the IViewModel‘s NavigateAsync<ItemDetailViewModel>() method, passing along the selected item.

In the details VM we pick up the selected item:

When navigating to the details screen the selected item is passed in as the “context” which is nothing but an arbitrary parameter. The VM sets this as the item currently viewed in the InitAsync() method.

The views

The implementations of IView are of type Xamarin.Forms.ContentPage. For every VM there is one page.

Here’s the page showing the list of items (please check out the complete project to also see the XAML):

As the page implements IView it must have a ViewModel property. This is what gets automatically set if the view/page is created by the view factory (see above for details). Because my pages are using the VM as the page’s BindingContext, the property implementation directly assigns it (note: this is the downside of not coupling the view factory to Xamarin.Forms; if the type Page was used instead of IView, the view factory could directly set the BindingContext. Feel free to adjust the code to match your requirements/preferences).

The other thing our page is responsible for, is the actual navigation. We trigger navigation via the VM but only the page is aware of the UI, that’s why it subscribes itself to the NavigationCallback and pushes the target page onto the navigation stack. To get the target page it is using the ViewFactory class:

  1. The VM associated with the current page tells us which VM to go to
  2. The current VM triggers the NavigationCallback and passes along information about the target VM
  3. The page reacts and uses the view factory to get the view associated with the target VM
  4. The page navigates to the target

Registering VMs and views

The view factory can only create pages and VMs that have been registered. This happens in the Form’s application class:

First, the combination of ItemOverviewViewModel and ItemOverviewPage is registered. The second registration shows another benefit of using a view factory: depending on the platform a different detail page is registered. This allows you to easily take advantage of a platform’s capabilities. You might want to show more details in your desktop app or on a tablet and a condensed view on the phone. By adding the platform specific VM/view combinations, your code will automatically pick the correct page and you don’t have to place checks deep inside your pages.

The final step is to assign a VM factory. In my example I kept it simple: my VMs all have a parameterless constructor, so I just use Activator.CreateInstance(). In your project, you might want to use whatever IoC pattern you prefer.

That’s it! Done. Notice how no additional Nuget packages were added 🙂

Try and run the demo app on a phone and on a tablet and see how it magically navigates, passes data and automatically selects form factor specific pages.

Final words

I wrote this post and demo code to show that MVVM and VM navigation does not require a lot of libraries and complexity. With a few lines of code a lot can be achieved. I’m not saying this is perfect or complete but remember: I didn’t want to give you yet another MVVM framework 🙂

This post was inspired by a chat I had with a coworker over a year ago and ever since I though it was a cool idea and I finally found the time to implement and blog about it.

You can find the demo project on Github. (Note: the UWP version has a bug where OnDisappearing() is called incorrectly and thus will break the navigation. Hopefully this will be fixed in the next Xamarin.Forms release)

Let me know what you think and reach me on Twitter.

Post Media Link

krumelur