Windows 10 Development: MvvmLight NavigationService and the Behaviors SDK

By August 12, 2015Windows
Windows-10-XAML-Behaviors-SDK

Following our last post, we now have a solid framework for our app to handle state and lifecycle. However, at this point we are still navigating the app directly from the code-behind of the Views, which ties the navigation to the platform code. In addition to cluttering up our code, this also restricts us from fully taking advantage of the cross-platform opportunities offered by MvvmLight.

Today we’ll see how to centralize this navigation code, removing the platform-specific definition and moving from the code-behind to the ViewModels, allowing maximum reuse of code. We’ll begin with the code related to navigation.

NavigationService

MvvmLight includes a cross-platform implementation of a navigation service which provides a device-agnostic way to perform navigation, allowing us to refactor that code into the ViewModels. The result is a fully portable, cross-platform solution.

Up until now we’ve been navigating directly against the Frame container for the application, which accepts as an argument the type of the intended page, as well as a single parameter. Obviously this implementation cannot be cross platform, since the pages are defined only in the Windows project.

Instead, the NavigationService in MvvmLight accepts a key of type string to identify the intended destination, which is resolved at runtime to the desired page. This is accomplished by first registering each page at startup, associating it with the specific key. You would do this once within each platform, reusing the same key when registering the destination so that they can be called consistently from the ViewModel.

For convenience, and to avoid typos, by convention I use the ViewModels themselves as the key, which intuitively links each one to its appropriate page. Here’s an example of the navigation registration code for our sample project:

This registration needs to happen at startup so that it is available immediately throughout the project, and it’s perfectly acceptable to do this in the OnLaunched or similar event. However, since the ViewModelLocator we previously created is defined as a static resource in the App.xaml file, it is automatically instantiated and registered at application startup, so this seems like the perfect place to register the views.

Unfortunately, our current ViewModelLocator is in the portable project, obviously so that it can be leveraged on other platforms. Instead, we’ll create an inherited version of the locator and extend it with the platform-specific code to register the NavigationService and register all the pages.

Reusing the navigation code in other platforms is as simple as creating a new version of the locator for that platform and registering it with that the NavigationService implementation for that platform.

WindowsViewModelLocator

Implementing the locator for Windows is fairly simple; we only need to inherit from the BaseViewModelLocator we already defined, and of course, ensure that the inherited base constructor executes (which we need to register all the ViewModels).

In the inherited constructor, we can proceed to register the NavigationService, and any other platform-specific code that needs to be in place at startup.

We register the NavigationService by binding it to the INavigationService interface from the portable project, and pass to it a factory that initializes the service with the registered views. Here’s the complete code for the inherited locator.

Notice we are again checking if we are in the designer, and if so, skip the navigation registration and register the default NavigationService. This is mostly due to a bug that causes multiple instances to be registered when using a factory, resulting in the error “INavigationService is already registered”.

Skipping the navigation registration frees the designer from any clutter not necessary at design-time so it’s a good practice to follow.

The last thing we need to do with the new locator is register it as a static resource, replacing the previous base locator in App.xaml:

This way the registration happens automatically as soon as the app fires up.

Refactoring Navigation to the ViewModels

Now that we have a centralized, cross-platform way to navigate to a screen, we can proceed to refactor our code to the ViewModels to maximize code reuse. The first thing we need to do is add a reference to the NavigationService to the BaseViewModel class, so it is accessible throughout the app.

However, we cannot add the platform-specific NavigationService we just setup, as that version is only for Windows. Instead, we want to once again take advantage of the ServiceLocator to instead define the reference by its interface, which will be resolved at runtime to the platform-specific version.

We can do that by simply adding the following property to the BaseViewModel:

From there can call NavigateTo on the service to execute the navigation to a specified page. It would look something like this:

To make things simpler I added a generic helper method to make it even easier to call by simply specifying the type and optional parameter to the method:

Now that we have this in order, we can easily execute navigation from the individual ViewModels using another helpful feature of MvvmLight

RelayCommand

The RelayCommand in MvvmLight implements the ICommand interface to allow you to fire events including strongly-typed parameters, exposing them to your views to be executed by UII events (such as a button click).

A thorough discussion of Commands is outside the scope of this post, but if you are new to this concept I highly recommend you take a look at this extensive article on MSDN that goes into incredible detail on the pattern: Commands, RelayCommands and EventToCommandCommands, RelayCommands and EventToCommand.

The RelayCommand in MvvmLight can be either fire a generic method, or send a strongly typed parameter via the generic form RelayCommand. In additon, a RelayCommand can be initialized with a seperate delegate to determine whether or not a command should be allowed to execute.

This is helpful if you want to disable a command for a specific reason, such as disabling a “Refresh” button while a ViewModel is in the “Loading” state, as this would likely mean that the command is currently already executing.

We don’t have any need to disable such commands in our example; we just need a way to select an item from the list on the MainPage and navigate to its details. Since this requires a specific item, we want to use the generic RelayCommand with our TestItem type so that we can use its ID property to properly navigate. Here’s what the command looks like:

Now that we have a command to navigate, we need a way to trigger it. One perfectly acceptable way to do this would be to replace our previous code-behind that fires on event-click to execute the command instead. It might look something like this:

While this gets the job done, there’s a more elegant solution made possible by leveraging the XAML Behaviors SDK.

Behaviors SDK

The Behaviors SDK has its roots in the Expression Blend SDK that allowed such behaviors and actions in XAML. For an in-depth tour of the SDK and its features, I recommend taking a look at this this post on Behaviors SDK by Timmy Kokke, which even describes how to build your own behaviors and actions.

But for our simple project, we simply want to leverage the EventTriggerBehavior to associate a specific event — in this case ItemClick of the ListView on the MainPage — with the command.

First, we need to make sure we add a reference to the SDK to the project:

Windows-10-XAML-Behaviors-SDK

Then we need to add the appropriate namespaces for the behavior we want to use from the SDK, which are Interactivity and Core:

At last we can proceed to use these to attach the behaviors to the ListView, associating the ItemClick event with the following XAML:

There’s one very important property that we haven’t yet covered, which is the InputConverter property. If you leave this out and attempt to call the command without converting the arguments, you’ll get an error similar to this:

Unable to cast object of type ‘Windows.UI.Xaml.Controls.ItemClickEventArgs’ to type ‘Win10MvvmLight.Portable.Model.TestItem’

The reason this happens is that the argument of the ItemClick event is of type ItemClickEventArgs, but the SelectItemCommand is expecting it to be of type TestItem. The InputConverter property lets you specify a class that will convert the arguments to the appropriate type.

This is simply an implementation if IValueConverter and in this case simply gets the clicked item out of the arguments from the click event, and passes it — cast to the appropriate type of course — to the command. Here’s what it looks like:

Be sure that this class is also registered as a static resource in App.xaml, so that it can be referenced in the XAML above.

With this last piece of the puzzle in place, we can now clear out ALL the code-behind from both pages, as all of the code necessary for loading the pages AND navigating between them is entirely defined in the portable project!

Wrapping Up and Next Steps

By registering our views with the NavigationService offered by Mvvm Light, we have a portable, cross-platform way to perform app navigation, separating the code from the platform-specific views. Since this service is implemented on various platforms, including Xamarin, we can use the exact same ViewModels across different devices without changing a single line of code in the portable project.

We’ll come back to this in a future post, showing how we can extend this project to other platforms with Xamarin Forms. In the meantime, we’ll take a break from the sample project to look closer at some of the new controls available in Windows 10, as well as how we leveraged them (and built new ones!) in our Falafel2Go app.

Until then, be sure to sign up here to get the latest version of the sample project code:

Get the CODE!

And as always, I hope this was helpful, and thanks for reading!

The following two tabs change content below.
  • Pingback: Dew Dump – August 17, 2015 (#2070) | Morning Dew()

  • Matthew Herb

    What is I only use Universal Apps and ASP.NET 5.0 .. Do I still need to use a separate DLL project? Right now I am following your code and putting it all in the Universal Apps project. Good stuff.

    • if you are only on a single platform there is certainly no need for a separate project. Personally I find it to be a good architecture to isolate the “core” app into its own project just to keep things logically organized, and limiting the platform app to only the UI and platform-specific stuff. plus it doesn’t hurt to think ahead to be ready to support other platforms, and it’s a good discipline to get used to 🙂 but it is not a requirement in any way for developing win10 apps, so do whatever feels right and works for you!

      thanks for reading!

      • Matthew Herb

        Do you know if the Universal Class is compatible with ASP.NET Web Application project? I could not find a clear answer.. but there is a chance I make want to move the services to aweb project if I want to create a ASP.NET version of th4e app

        • so I have never tried this myself but it appears to be possible. i’m not there yet but i’ll explore this a bit later, if you make any progress I’d be interested to see how you fare.

          • Matthew Herb

            Ill move them to a portable library when that day comes if I need to. But for now I just have them in the Universal App… I want to use a AZURE API APP someday

  • Matthew Herb

    Can’t load the samples .. Works for my own project though since I am not using portable lib (I am using VS2015)
    So when I try to re add mvvmlightlibs I get this:
    Attempting to gather dependencies information for package ‘MvvmLightLibs.5.1.1’ with respect to project ‘Win10MvvmLight.Portable’, targeting ‘.NETPortable,Version=v4.5,Profile=Profile259’

    Attempting to resolve dependencies for package ‘MvvmLightLibs.5.1.1’ with DependencyBehavior ‘Lowest’

    Resolving actions to install package ‘MvvmLightLibs.5.1.1’

    Resolved actions to install package ‘MvvmLightLibs.5.1.1’

    For adding package ‘MvvmLightLibs.5.1.1’ to project ‘Win10MvvmLight.Portable’ that targets ‘portable45-net45+win8+wp8+wpa81’.

    For adding package ‘MvvmLightLibs.5.1.1’ to project ‘Win10MvvmLight.Portable’ that targets ‘portable45-net45+win8+wp8+wpa81’.

    Package ‘MvvmLightLibs.5.1.1’ already exists in folder ‘C:UserspunkouterDocumentsWindows10Development-masterWin10MvvmLightSampleProject4. Maintaining Application StateWin10MvvmLight.Part4.ManagingAppStatepackages’

    Install failed. Rolling back…

    Package ‘MvvmLightLibs 5.1.1’ does not exist in project ‘Win10MvvmLight.Portable’

    Failed to add reference to ‘GalaSoft.MvvmLight’.

    • the sample project already has the nuget references to mvvmlight, you should be able to simply build the solution to have it download them. if all else fails, open the nuget manager for each project, remove the references and reinstall them. hopefully that will do it!

      • Matthew Herb

        Yup.. That is what I tried. Did not restore them.. So I deleted them and tried to add it.. forthe portable class lib it did not work.. But I have everything working on my project except one thing..

      • Matthew Herb

        It was related to the over 256 character limit on file directories

        • ah i would have never thought of that, thanks for the update! hopefully you’re good to go!

  • Matthew Herb

    The only error I have getting it to work with my project is the EventTrigger part.. When I add this XAML I have the error.

    An object of the type “VelocifyTools.Converters.ItemClickEventArgsConverter” cannot be applied to a property that expects the type “Windows.UI.Xaml.Data.IValueConverter”.

    Any idea what I can check? I have been forced to comment that out for now.

    • Matthew Herb

      Seems to be related to this..

  • Matthew Herb

    finally figured it out.. I had the wrong name here thinking it was suppose to be the name of a method when I was quickly renaming things

  • Matthew Herb

    Now I create a Universal App unit test project and wondering how should I got about testing this ? Normally the test code is similar to what the client does but in this case the client does not do anything.. .. So do I just call LOADSTATE and change it to not be void and send something back ?

  • Pingback: Szumma #005 – 2015 35. hét | d/fuel()

  • Pingback: Windows 10 Development: MVVMLight NavigationService and the Behaviors SDK | Dinesh Ram Kali.()

  • Pingback: Developing for Windows 10: Getting Started()

  • Thomas Kitch

    This is very informative and is giving me a good understanding of Mvvm light.
    From a step by step tutorial perspective, it would be really helpful to include namespaces, and clearly state what class we should be placing the code in. A few times I have been scratching my head, trying to work out where i should be placing some the code.

    Thanks for your hard work, appreciate it!

  • Parthasarathi

    Hi, i have developing cross platform mobile application using xamarin (PCL) and UWP. i am using galasoft.mvvmlight mvvm framework.An existing application using prism framework for winRT,but now migrating to UWP( code base is xamarin pcl). In prism framework using viewmodel load and unload( OnNavigatedTo, OnNavigatedFrom ). How can we do it galasoft mvvm ?

  • Maciej Dziadowiec

    How can i download the sample project? “Get the CODE!” link doesn’t work…