Introduction
The Cimbalino Windows Phone Toolkit delivers a set of useful and powerful MVVM-compatible tools and services to help developers build Silverlight applications for Windows Phone. The Toolkit is divided in projects which deliver various features, ranging from base MVVM services and helpers, through to code for background agents and for accessing media library, location services and so on. The base project (Cimbalino.Phone.Toolkit) contains base MVVM services, some very useful converters, helper classes and extension methods.
The Cimbalino NavigationService provides methods, properties and events to support navigation within an XAML application. In effect it is and abstraction of the normal System.Windows.Navigation.NavigationService, adding things like a QueryString property to return a dictionary of parameters passed to the page on navigation.
The kit provides both the INavigationService interface and its implementation NavigationService required to register the service in MVVM Light (note that MVVM and the MVVM Light Toolkit are not "preconditions" to use this service). One advantage of the service approach is that it is possible to unit test the navigation code.
This code example shows a basic MVVM Light app which uses the NavigationService to launch a page without parameters or launch a different page and display the parameters sent.
Screenshots of the example app are shown below.
Example app main screen.
Page launched (no parameter).
Page launched (with parameter).
Building Code Example
The source code for the code example is available here: NavigationService Example (github).
To build the source code you will also need the MVVM Light Toolkit and the Cimbalino Windows Phone Toolkit. Their packages are available in the Nuget Package Manager.
Registering the service
Register the service in the ViewModelLocator constructor as shown below (ViewModelLocator.cs).
- public class ViewModelLocator
- {
- public ViewModelLocator()
- {
- ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
-
- if (!SimpleIoc.Default.IsRegistered<INavigationService>())
- {
- SimpleIoc.Default.Register<INavigationService, NavigationService>();
- }
-
- SimpleIoc.Default.Register<MainViewModel>();
- SimpleIoc.Default.Register<Page2ViewModel>();
- }
-
- public MainViewModel MainViewModel
- {
- get
- {
- return ServiceLocator.Current.GetInstance<MainViewModel>();
- }
- }
-
- public Page2ViewModel Page2ViewModel
- {
- get
- {
- return ServiceLocator.Current.GetInstance<Page2ViewModel>();
- }
- }
Note how we also define properties for the two ViewModels (MainViewModel and Page2ViewModel) and register them.
In the next section we see that the MainViewModel constructor takes a INavigationService parameter. When ViewModelLocator creates the view model it recognises that the parameter is registered, creates an instance of the NavigationService and passes it to MainViewModel.
Implementing the ViewModel
Implement the MainViewModel as shown below. The highlighted sections show the MainViewModel constructor taking theINavigationService parameter and assigning it to a private member. Later on the member is used to call NavigateTo() in order to navigate to the second page (both with and without parameters being passed).
- using System;
- using System.Windows.Input;
-
- using Cimbalino.Phone.Toolkit.Services;
- using GalaSoft.MvvmLight.Command;
-
-
-
- public class MainViewModel : ViewModelBase
- {
-
- private readonly INavigationService _navigationService;
-
-
- public MainViewModel(INavigationService navigationService)
- {
- _navigationService = navigationService;
- NavigateWithoutParameterCommand = new RelayCommand(NavigateWithoutParameter);
- NavigateWithParameterCommand = new RelayCommand(NavigateWithParameter);
- }
-
-
-
- public ICommand NavigateWithoutParameterCommand { get; private set; }
-
-
- public ICommand NavigateWithParameterCommand { get; private set; }
-
-
- private void NavigateWithoutParameter()
- {
- _navigationService.NavigateTo(new Uri("/Page1.xaml", UriKind.Relative));
- }
-
-
- private void NavigateWithParameter()
- {
- _navigationService.NavigateTo(new Uri("/Page2.xaml?parameter=1", UriKind.Relative));
- }
- }
The second view model
Page2ViewModel is similar. Again the constructor takes the INavigationService parameter and assigning it to a private member. In this case though we call CanGoBack() and GoBack() methods to return to the original page. The highlighted line below shows a property for getting the parameter as a string.
- using System.Windows.Input;
-
- using Cimbalino.Phone.Toolkit.Services;
-
- using GalaSoft.MvvmLight;
- using GalaSoft.MvvmLight.Command;
-
- public class Page2ViewModel : ViewModelBase
- {
-
- private readonly INavigationService _navigationService;
-
-
-
- public Page2ViewModel(INavigationService navigationService)
- {
- _navigationService = navigationService;
- GoBackCommand = new RelayCommand(GoBack);
- }
-
-
-
- public string Parameter
- {
- get
- {
- return _navigationService.QueryString["parameter"].ToString();
- }
- }
-
-
-
- public bool CanGoBack
- {
- get
- {
- return _navigationService.CanGoBack;
- }
- }
-
-
- public ICommand GoBackCommand { get; private set; }
-
-
-
- private void GoBack()
- {
- _navigationService.GoBack();
- }
- }
Implementing the views
The rest of the app is "plumbing" to hook up the ViewModels to the View and to send commands from the UI to the invoke the navigation service.
How to use MVVM Light Toolkit for Windows Phone explains most of what is going on, but for completeness the
MainPage.xaml is as shown below:
- <phone:PhoneApplicationPage x:Class="CimbalinoSample.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"
- xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
- xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
- DataContext="{Binding MainViewModel,
- Source={StaticResource Locator}}"
- FontFamily="{StaticResource PhoneFontFamilyNormal}"
- FontSize="{StaticResource PhoneFontSizeNormal}"
- Foreground="{StaticResource PhoneForegroundBrush}"
- Orientation="Portrait"
- SupportedOrientations="Portrait"
- shell:SystemTray.IsVisible="True"
- mc:Ignorable="d">
-
-
- <Grid x:Name="LayoutRoot" Background="Transparent">
- <Grid.RowDefinitions>
- <RowDefinition Height="Auto" />
- <RowDefinition Height="*" />
- </Grid.RowDefinitions>
-
-
- <StackPanel x:Name="TitlePanel"
- Grid.Row="0"
- Margin="12,17,0,28">
- <TextBlock Margin="12,0"
- Style="{StaticResource PhoneTextTitle2Style}"
- Text="Cimbalino Sample" />
- <TextBlock Margin="9,-7,0,0"
- Style="{StaticResource PhoneTextTitle2Style}"
- Text="NavigationService" />
- </StackPanel>
-
-
- <Grid x:Name="ContentPanel"
- Grid.Row="1"
- Margin="12,0,12,0">
- <Button Height="120"
- Margin="0,120,0,404"
- Command="{Binding NavigateWithoutParameterCommand}"
- Content="Navigate without parameter" />
- <Button Height="120"
- Margin="0,50,0,0"
- Command="{Binding NavigateWithParameterCommand}"
- Content="Navigate with parameter" />
- </Grid>
- </Grid>
- </phone:PhoneApplicationPage>
The first highlighted lines show binding the MainViewModel as a DataContext for the page.
- DataContext="{Binding MainViewModel,
- Source={StaticResource Locator}}"
The second highlighted lines bind to the RateCommands in the MainViewModel that cause the navigation methods to be called.
Page1.xaml is shown below. This page is launched without any parameters and doesn't have a view model because it needs no data.
- <phone:PhoneApplicationPage x:Class="CimbalinoSample.Page1"
- 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:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
- xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
- FontFamily="{StaticResource PhoneFontFamilyNormal}"
- FontSize="{StaticResource PhoneFontSizeNormal}"
- Foreground="{StaticResource PhoneForegroundBrush}"
- Orientation="Portrait"
- SupportedOrientations="Portrait"
- shell:SystemTray.IsVisible="True"
- mc:Ignorable="d">
-
-
- <Grid x:Name="LayoutRoot" Background="Transparent">
- <Grid.RowDefinitions>
- <RowDefinition Height="Auto" />
- <RowDefinition Height="*" />
- </Grid.RowDefinitions>
-
-
- <StackPanel x:Name="TitlePanel"
- Grid.Row="0"
- Margin="12,17,0,28">
- <TextBlock Margin="12,0"
- Style="{StaticResource PhoneTextTitle2Style}"
- Text="Cimbalino Sample" />
- <TextBlock Margin="9,-7,0,0"
- Style="{StaticResource PhoneTextTitle2Style}"
- Text="NavigationService" />
- </StackPanel>
-
-
- <Grid x:Name="ContentPanel"
- Grid.Row="1"
- Margin="12,0,12,0">
- <TextBlock>Page1 without parameter</TextBlock>
- </Grid>
- </Grid>
-
- </phone:PhoneApplicationPage>
Page2.xaml is shown below. This page sets Page2ViewModel as the DataContext. This allows us to bind to the Parameter properly in the model in order to display the string that was sent from the MainViewModel.
- <phone:PhoneApplicationPage x:Class="CimbalinoSample.Page2"
- 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:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
- xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
- DataContext="{Binding Page2ViewModel,
- Source={StaticResource Locator}}"
- FontFamily="{StaticResource PhoneFontFamilyNormal}"
- FontSize="{StaticResource PhoneFontSizeNormal}"
- Foreground="{StaticResource PhoneForegroundBrush}"
- Orientation="Portrait"
- SupportedOrientations="Portrait"
- shell:SystemTray.IsVisible="True"
- mc:Ignorable="d">
-
-
- <Grid x:Name="LayoutRoot" Background="Transparent">
- <Grid.RowDefinitions>
- <RowDefinition Height="Auto" />
- <RowDefinition Height="*" />
- </Grid.RowDefinitions>
-
-
- <StackPanel x:Name="TitlePanel"
- Grid.Row="0"
- Margin="12,17,0,28">
- <TextBlock Margin="12,0"
- Style="{StaticResource PhoneTextTitle2Style}"
- Text="Cimbalino Sample" />
- <TextBlock Margin="9,-7,0,0"
- Style="{StaticResource PhoneTextTitle2Style}"
- Text="NavigationService" />
- </StackPanel>
-
-
- <Grid x:Name="ContentPanel"
- Grid.Row="1"
- Margin="12,0,12,0">
- <TextBlock>
- Page2 with parameter<Run Text="{Binding Parameter}" />
- </TextBlock>
- <Button Content="Go back" IsEnabled="{Binding CanGoBack}" Command="{Binding GoBackCommand}" Margin="0,66,0,0"/>
- </Grid>
- </Grid>
-
- </phone:PhoneApplicationPage>