Xaml Universal App and Prism navigation

phew... that was very long and unproductive week ;) But it's time to pick up next topic.
Lets see how does the navigation present in Prism 6 for UWP, that is still quite fresh - first release of version 6.0.0 has been made in October 2015. Basically, functionalities involved in Prism 6 UWP are ported directly from Prism for WindowsRT 2.0 release (excluding some minor changes).

Prism 6.0 packages in a nutshell
Foundation of new Prism is Prism.Core package that includes cross-platform PCL library (Prism.dll) that encapsulates basic stack around MVVM/Commands/Events/Logging. On top of Prism.Core are built platform-specific implementations: Prism.Wpf for WPF, Prism.Forms for Xamarin Forms and Prism.Windows for Windows 10 UWP. Third layer, that we can add on the top, is related to IoC containers used for bootstrapping application and for ServiceLocator implementation. For UWP we have support for two of them: Prism.Unity and Prism.Autofac. For other platforms support exists also for StructureMap, Ninject and MEF.

Installing Prism
To get Prism (lets also include Autofac) we have to simply type in nuget:
install-package Prism.Autofac
This package should refer to all necessary assemblies: Prism.Autofac.Windows, Prism.Windows and Prism
Bootstrapping
To bootstrap prism application you have to replace entry point - Windows.UI.Xaml.Application with implementation delivered by Prism. In our case that will be: PrismAutofacApplication from Prism.Autofac.Windows assembly.
App.xaml:
<windows: PrismAutofacApplication
x: Class="Uwp.Xaml.Navigation.Prism.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns: x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns: windows="using:Prism.Autofac.Windows"
RequestedTheme="Light">

windows: PrismAutofacApplication>
App.xaml.cs:
using System.Threading.Tasks;
using Windows.ApplicationModel.Activation;
using Prism.Autofac.Windows;

namespace Uwp.Xaml.Navigation.Prism
{
sealed partial class App : PrismAutofacApplication
{
public App()
{
this.InitializeComponent();
}
protected override Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args)
{
Window.Current.Activate();
return Task.FromResult( true);
}
}
}
Next we have to create shell for our application. Shell is nothing else than our MainPage that is host for injected Frame (yes, implementation of Prism navigation for UWP is based on native Frames mechanism, or more appropriately for current implementation - only single Frame :( ). So to create our Shell we have to simply override CreateShell method in our PrismAutofacApplication subclass.
App.xaml.cs:
protected override UIElement CreateShell(Frame rootFrame)
{
var shell = Container.Resolve<MainPage>();
shell.SetFrame(rootFrame);
return shell;
}
the MainPage is a standard xaml page with ContentControl that becomes a frame host:
MainPage.xaml:
<Page
x:Class="Uwp.Xaml.Navigation.Prism.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<ContentControl x:Name="FrameHost">ContentControl>
Grid>
Page>
MainPage.xaml.cs:
using Windows.UI.Xaml.Controls;

namespace Uwp.Xaml.Navigation.Prism
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}

public void SetFrame( Frame frame)
{
FrameHost.Content = frame;
}
}
}

View and ViewModel naming convention
Ok, so now we have to define our views and view models that will be our navigation targets. Prism supports naming convention that allows it to create instances of views/viewModels and wire them up together without any additional configurations on our side. Default convention is:
  • views:
    • Have to be located in Views/ folder
    • Have to be named as <name>Page
    • To automatically inject related ViewModel into DataContext property View have to be marked with property AutoWireViewModel="true"
    • example: Views/HomePage.xaml
  • view Models:
    • Have to be located in ViewModels/ folder
    • Have to be named <view_name>Model or <view_name>ViewModel
    • example: ViewModels/HomePageModel or ViewModels/HomePageViewModel
However if you don't like this nasty convention you can change it without pain. If you want to change view naming convention you have to make simple override of GetPageType(string pageToken) method in PrismAutofacApplication. This method simply takes string pageToken passed to Navigation method and returns exact type that will be afterwards injected into Frame by Prism.
If you want to change view model naming convetion you have to use ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(Func<Type,Type>) and pass function that will return view model type based on view type input.
I prefer convention like this: Views/HomeView.xaml, ViewModels/HomeViewModel.cs so override of GetPageType method will be enough to cover it:
App.xaml.cs:
protected override Type GetPageType(string pageToken)
{
var type = Type.GetType(string .Format(CultureInfo .InvariantCulture, GetType().AssemblyQualifiedName.Replace(GetType().FullName, GetType().Namespace + ".Views.{0}View"), pageToken));
if (type != null)
return type;
throw new ArgumentException(string .Format(CultureInfo .InvariantCulture, ResourceLoader.GetForCurrentView("/Prism.Windows/Resources/" ).GetString("DefaultPageTypeLookupErrorMessage"), pageToken, GetType().Namespace + ".Views"), nameof(pageToken));
}

Navigation executing and handling
Prism allows us to execute navigation action via INavigationService implementation (that is quite similar under the hood to naive approach that i have presented in previous post). Navigate method provides single overload that allows to pass pageToken (for below example, in our convention, target view is FirstView and view model is FirstViewModel) and context as an object.
NavigationService.Navigate("First", new object());
Handling of navigation events can be done on view level as well as on view model. View can handle it via standard virtual methods present in Page implementation, however the Prism provides SessionStateAwarePage that is Page subclass that overloads OnNavigationTo and OnNavigatedFrom underneath and automatically saves and restores navigation state.
FirstView.xaml and FirstView.xaml.cs:
namespace Uwp.Xaml.Navigation.Prism.Views
{
public sealed partial class FirstView : Page
{
public FirstView()
{
this.InitializeComponent();
Debug.WriteLine(string.Format("Creating: {0}",GetType().Name));
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
Debug.WriteLine("OnNavigatedFrom");
base.OnNavigatedFrom(e);
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
Debug.WriteLine("OnNavigatedFromCancel");
base.OnNavigatingFrom(e);
}
public FirstViewModel ViewModel => DataContext as FirstViewModel;
}
}
<Page
x: Class="Uwp.Xaml.Navigation.Prism.Views.FirstView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mvvm="using:Prism.Windows.Mvvm"
mvvm:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">

<Grid Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height="*">RowDefinition>
<RowDefinition Height="Auto">RowDefinition>
Grid.RowDefinitions>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="{x:Bind ViewModel.Title}">TextBlock>
<Button VerticalAlignment="Center" Grid.Row="1" Click="{x:Bind ViewModel.OnClick}"> Navigate to Page ThreeButton>
Grid>
Page>
To handle navigation events on view model level you have to inherit from ViewModelBase that provides same sort of virtual methods as view has (OnNavigatedTo, OnNavigatedFrom). If you want to execute navigation you have to create INavigationService parameter in constructor, Prism take care of injecting it during creation.
FirstViewModel.cs:
public class FirstViewModel : ViewModelBase
{
private readonly INavigationService _navigationNavigationService;

public FirstViewModel(INavigationService navigationService)
{
_navigationNavigationService = navigationService;
}

public string Title => "First View Model";

public void OnClick(object sender, RoutedEventArgs e)
{
_navigationNavigationService.Navigate("Third", null);
}

public override void OnNavigatedTo(NavigatedToEventArgs e, Dictionary<string ,object> dictionary)
{
base.OnNavigatedTo(e, dictionary);
}

public override void OnNavigatingFrom(NavigatingFromEventArgs e, Dictionary<string ,object> viewModelState, bool suspending)
{
base.OnNavigatingFrom(e, viewModelState, suspending);
}
}

Constraints
Current UWP implementation has one heavy weight constraint that i have not been awared of before i have started to work with it. In standard WPF Prism navigation is based on RegionManager that keeps track on many placeholders for view. If we take a look into UWP Prism implementation we can see that PrismApplication is tightly coupled with single INavigationService implementation that holds single Frame. Briefly, if you want nested (or deep-in or however you call it) navigation in UWP, Prism is not your best choice at the moment.
You can find example implementation at github/sandbox