02.Apr.15

Vladimir Milev

Vladimir Milev

Share Code Between WPF and Universal Apps (Windows Store Apps) – Navigation, Unity and Commanding

This blog post part of a series about sharing code and XAML between WPF and Universal Apps. You may want to check out the previous part which covers the basics of the project and PRISM implementation.

Navigation

One of the most important aspects of LOBs (line of business applications), especially ones that implement the MVVM pattern, is to be able to navigate between your Views. This is one of the tasks that the PRISM framework takes care of for you. Unfortunately for us, however, the flavors of PRISM for WPF and for Store Apps handle this quite differently. The version for store apps handles navigation through the use of the INavigationService. In WPF PRISM things are handled using Regions and the RegionManager.

For the purposes of our cross-platform MVVM application it makes sense to use one navigation solution across the board. But as we have seen already there isn't a good candidate out of the box. Have we reached a dead end? How do we proceed? We have two choices here: try to stick with the PRISM implementation or do the navigation by ourselves. In this article I will discuss one possible solution for this problem - a hybrid solution which is the middle road between reusing out-of-the-box functionality and writing your own.

The INavigationService interface

While investigating this problem I took a look at the implementation of navigation in PRISM for StoreApps. Fortunately, it follows good programming practices and the authors have included an interface defining the common navigation-related tasks offered by the framework. Here is what the interface looks like:

Giving it a quick glance it looks like the most useful parts are the Navigate(); method and GoBack(); There are others as well such as the RestoredSavedNavigation() and Suspending() which are specific to the Universal Apps platform. This explains why there is no equivalent in PRISM for WPF. Since we are writing a cross-platform application we are going to have to accept that we have to work with a suitable subset of this functionality that will work on both platforms. At this point I decide that being able to call Navigate() and GoBack() on all platforms will suffice for our particular MVVM application. It is enough to switch between our views and honestly anything more is just sugar coating. It is now clear what we have to do: provide our own implementation of the INavigationService for WPF. By doing this we will enable our application to reuse the existing PRISM code for the UniversalApps part and will allow our cross-platform ViewModels to have a one approach to navigation.

Implementing INavigationService in WPF

Before diving into the implementation it is a good idea to read up on how navigation in WPF works, especially if you are not familiar with the topic. This is a good guide on the topic by Paul Stovell.

In order to emulate the navigation behavior of universal apps (store apps) we are going to need to implement a navigation host within the WPF app main window. We are going to use the Frame control as a host. While there isn't a native/PRISM implementation for the INavigationService there is a WPF class called NavigationService. It is exposed by the Frame control and we can aggregate it within our implementation. But first things first - let's define our root navigation host inside the WPF MainWindow class:

Once we do this we are going to set the StartupUri="MainWindow.xaml" in the App.xaml definition thus creating the navigation shell for our application. When the app launches it will load the MainWindow which will simply serve as container for the navigation frame which will provide navigation between our Views.

Since the INavigationService interface does not exist in WPF we will have to define a copy of it in our WPF project. It is the exact same definition as the one in the code snippet above. Now that we have the interface defined it is time to write our implementation of INavigationService. I will call it SimpleNavigationService. Here is the code:

Some explanation is in order. First you will notice that some of the methods are not implemented and throw exceptions. This is OK. There is no concept of application state saving/resuming/restoring such as the one available in windows phone and WinRT. As I said earlier, we are simply going to have to do with a subset of the functionality if we want to be cross-platform. We are not going to use those. What we are going to use, however, is the Navigate method. We are going to redirect the call to the aggregated WPF NavigationService in our implementation. Notice the format string - we are going to be looking for all our pages within the Views directory. This is part of the PRISM conventions and we are going to follow it.

Hooking things up with Unity

Since we are using PRISM and MVVM we want to keep things decoupled. In order to do that we have to use a IoC Container and hook it up with PRISM. I have chosen Unity. Setting up Unity is trivial for both Universal Apps and WPF. If you want to see the details just download the updated source code of MyApp at the end of the article. What I am going to focus on here is how to hook up our WPF INavigationService with Unity. First, we need to declare it in the our App.xaml.cs:

As you can see we define a reference to an implementation of INavigationService in the App class. We also define a helper method that we will call in order to initialize the navigation service. Next, we need to call it from our MainWindow.xaml.cs when we obtain a reference to our Fame:

Finally, when we have initialized the instance we navigate to our initial page (which is called MainPage in our case). We have now successfully initialized an instance of SimpleNavigationService by aggregating the native NavigationService and registered our implementation with the Unity container. We are ready to use the navigation service in our ViewModels!

Navigation with commands

Since we have chosen to follow the MVVM pattern we have to perform navigation within our ViewModels. We can now reap the fruit of the Unity container that we configured in the last section. To obtain an instance to the INavigationService in our ViewModel, we simply declare our dependency to it by adding it as a parameter to the constructor. This is called constructor injection. When PRISM tries to create an instance of the ViewModel it will ask our Unity container to provide us with the appropriate implementation of the interface.

In order to interact with ViewModels from the UI hosted in our Views we have to use Commands. WPF/WinRT provide the ICommand interface and the PRISM library provides the DelegateCommand. Implementation is simple:

So now that we have defined the command in our ViewModel we simply have to bind some UI to it in the View:

<Button Grid.Row="1" Content="Navigate Away" Command="{Binding NavigateAway}" />

 

Clicking this button will cause the ViewModel to navigate to another View. Please, note that the source code of the ViewModel is shared across WPF/Windows Phone/WinRT. In the WPF app the INavigationService is implemented by our own SimpleNavigationService and in the other cases it is an instance of the PRISM FrameNavigationService. The end result is that we have a ViewModel capable of navigation regardless of the platform it is running on.

Prism Conventions Unification

There is one more interesting point I have to make. In the FrameNavigationService which is part of PRISM for StoreApps the convention is to have each View end in the "Page" suffix. By default if you want to navigate to the "MainPage.xaml" View you have to call INavigationService.Navigate("Main", null); You will notice that this is not the way I do it in the WPF implementation. The reason is simple - I don't like this convention because I find it a bit limiting. Regardless of whether you like the convention this is a nice opportunity to learn how to override the PRISM conventions to your liking. In WPF I have already implemented the SimpleNavigationService to work as I like - it will look for the view inside Views/<ViewName>.xaml. All we have to do is override this behavior in the PRISM for StoreApps. Since our App class is inheriting from MvvmAppBase we simply need to override the GetPageType() method:

Since the source code of PRISM is publically available we can copy+paste the original implementation and just change the string.Format() statement to match the convention we have chosen to use.

Conclusion

This concludes today's discussion of cross-platform navigation with PRISM for WPF and StoreApps. I hope you have found this tutorial useful. You can download the full source code of my sample application here: MyApp.navigation

If you found this article useful, could you hit any of the share buttons below, so that others can benefit from it, too? Thanks!

Need consulting on this topic?

Yes No