[MVVMbasics] Navigating between windows / pages

All blog posts with a title that starts with [MVVMbasics] are related to the MVVMbasics framework. If you are only interested in my regular blog posts and not in MVVMbasics, ignore these. To list all articles covering MVVMbasics, go to http://mvvmbasics.mobilemotion.eu/documentation!

[important]Edited to reflect the changes in the MVVMbasics 2.0 release. The contents may not fully apply to older versions of the MVVMbasics framework.[/important]

One of the problems of the traditional MVVM pattern is View navigation: Business logic is typically contained within the Viewmodel, and the Viewmodel should be designed to include no knowledge about the Views contained in the App – however, to open another View (a task that is part of business logic and therefore located in the Viewmodel code) it is necessary to know the target View’s URI (Windows Phone and Window Store platform) or the View’s class name (Desktop platform). There exists a variety of approaches – both out-of-the-box and through third party libraries – to combine View navigation with the MVVMpattern, but most of those are restricted to certain platforms. MVVMbasics offers a convenient navigation framework that is consistent over all three platforms (Windows Phone, Windows Store, and Desktop WPF). The big advantage of such a platform-independent approach is that the Viewmodel code can be re-used on all platforms.

The navigation principle

MVVMbasics incorporates tight coupling of Viewmodels to Views: For each View, there exists exactly one Viewmodel, and those two are attached to each other. This offers developers the possibility of providing a Viewmodel as navigation target – due to the strict binding of Viewmodels to Views, the framework can automatically map the provided Viewmodel to the actual View and finally present this View on the screen. The developer does not need to care about calling View-specific functions, everything is solved within the Viewmodel layer. Even all the typical navigation events (that occur when loading or unloading a window or a page) are automatically routed to the respective Viewmodel and can be handled there – no need to include code in the View’s code-behind! (These navigation events are described in a separate article: see Handling navigation events)

Prerequisites

For Viewmodel-based navigation to work properly, the following conditions must apply:

  • All Views (pages / windows) in the App must subclass BaseView
  • All Viewmodel classes must subclass BaseViewmodel
  • The Application class itself must subclass BaseApplication
  • Views must provide an MvvmNavigationTarget attribute (when using one of the NavigatorService‚Äôs RegisterAll methods), or must be registered to the NavigatorService individually by calling the Register method
  • Each Viewmodel may be registered to only one View

Navigation in detail

It is strongly recommended to use MVVMbasics-specific navigation methods, or other libraries’ navigation approaches, but not to mix them in order to ensure that both navigation and the navigation events are handled correctly. To accomplish View navigation using the MVVMbasics framework, the NavigatorService is used. This is an MVVM service that is part of the MVVMbasics library.

To start with, you should once again check that all Views are attached to a unique Viewmodel and are registered to an instance of NavigatorService. In addition, this NavigatorService instance must be registered to the global ServiceLocator instance. All those things are typically done in the Application’s constructor.

Now you’re ready to switch between Views as you like. In any Viewmodel, you can access the NavigatorService through the global ServiceLocator instance. If you plan to do several calls to different navigation methods from within the same Viewmodel, you might want to store the NavigatorService’s instance in a private variable:

public class MyViewmodel : BaseViewmodel
{
	private readonly ServiceLocator _serviceLocator;
	private readonly NavigatorService _navigator;
	
	private MyViewmodel(ServiceLocator serviceLocator)
	{
		_serviceLocator = serviceLocator;
		_navigator = serviceLocator.Retrieve<INavigatorService>();
	}
}

In any of the Viewmodel’s internal methods, you may now use this service reference for the actual navigation. NavigatorService provides functionality for navigating to a new View and for navigating back to a previously shown View (see the Class Reference for details). In addition, both navigation methods support passing parameters between the two Views involved (the one initiating the navigation, and the one that is the navigation’s target).

Navigating to a new View

For navigating to a View that has not yet been shown on the screen (or to re-load a View that was visible on the screen earlier but has already been closed now), the NavigateTo<T>() method in provided. Two overloads of this method are available, in practice the NavigateTo<T>(params Parameter[] parameters) is used in most cases, since it allows to pass an arbitrary number of parameters to the target View, but can also be called without parameters.

In its most basic form, this method expects only a type parameter that represents the desired Viewmodel that shall be navigated to, as in the following example:

_navigator.NavigateTo<T>();

In this case, the View that has been registered to SecondViewmodel while App startup will be shown on the screen, a new instance of SecondViewmodel will be created and the OnNavigatedTo method will be called within this SecondViewmodel instance (detailed information about the Viewmodel’s navigation events is provided in Handling navigation events).

The current View will not be closed but remains in background. When closing the newly loaded View, it will be reactivated and moved to the foreground again. On the Windows Phone and Windows Store platforms, newly loaded pages hide the current View completely because they fill the whole screen. On the Desktop platform, new windows may be shown anywhere on the screen, partially but not fully hiding the current window. The current window will however always lose focus when navigating to a new View.

In addition, you may pass parameters to the newly loaded View. In contrast to the traditional navigation method on the Windows Phone platform that allows only parameters of primitive types as part of the new View’s URI, MVVMbasics support parameters of all types. For example, it’s perfectly possible to pass even nested collections of complex objects between two Views. All objects and values that shall be passed on to the target View are simply provided as parameters to the NavigateTo() method, as in the following example:

_navigator.NavigateTo<T>(new Parameter("parameter1", "parameterValue"), new Parameter("parameter2", 3.1415), new Parameter("parameter3", DateTime.Now));

For details about the Parameter object used to define those parameters, see the Class Reference. All the parameters (in this case, one string value, one number and one DateTime object) will be passed to the target Viewmodel’s OnNavigatedTo method – see Handling navigation events for details on how to retrieve those parameters.

The second overload NavigateTo<T>(ParameterList parameters) is used mostly within a Viewmodel’s OnNavigatedTo method: Let’s assume that when loading a View, you want to immediate navigate on to another View, and that you want to pass on all parameters that have been passed to the current View also to the next View. This can be done by calling NavigateTo from within the OnNavigatedTo method, since this method is called directly after a the current View has been loaded. In this case, you could simply pass the ParameterList object that is provided by OnNavigatedTo to the NavigateTo method – no need to parse the list and pass on the single Parameter objects.

Navigating back to a previously shown View

On the other hand, the NavigateBack() method closes the current View and returns to the previously shown View. This is only possible if the current View is not the App’s main View (otherwise the App would be terminated because the only active view was closed, which is not permitted by the Windows platform).

Also this functionality allows to pass parameters to the target View (which can be retrieved in the target Viewmodel’s OnNavigatedTo method). However, since the NavigateBack() method does not allow parameters, the SetBackParameters method needs to be used. Again, there exist two overloads of this method: One that expects an arbitrary number of Parameter objects, and one that expects one ParameterList object which is mainly used within a Viewmodel’s OnNavigatedTo method.

To pass parameters to the previously shown View, the SetBackParameters method must be called any time before NavigateBack() is invoked, or at the latest within the current Viewmodel’s CancelNavigatingFrom method.

It should be noted that the NavigateBack() method is not the only reason for a View to be closed – the user might also have pressed the hardware back button (on a Windows Phone device) or clicked the current window’s [x] button to close it. If potential parameters shall be passed to the previously shown View also in these cases, the best way is to call SetBackParameters from within the current Viewmodel’s CancelNavigatingFrom method since this method is always called before a View is closed.

If back parameters have been set before and some user input shall result in these parameters not passed to the previously shown View, you can still call the ClearBackParameters() method any time before the View closes, as this method removes all previously set back parameters.