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>();
- }
- }
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;
- /// This class contains properties that the main View can data bind to.
- public class MainViewModel : ViewModelBase
- {
- /// The navigation service.
- private readonly INavigationService _navigationService;
- /// Initializes a new instance of the MainViewModel class.
- public MainViewModel(INavigationService navigationService)
- {
- _navigationService = navigationService;
- NavigateWithoutParameterCommand = new RelayCommand(NavigateWithoutParameter);
- NavigateWithParameterCommand = new RelayCommand(NavigateWithParameter);
- }
- /// Gets the navigate without parameter command.
- public ICommand NavigateWithoutParameterCommand { get; private set; }
- /// Gets the navigate with parameter command.
- public ICommand NavigateWithParameterCommand { get; private set; }
- /// Navigates the without parameter.
- private void NavigateWithoutParameter()
- {
- _navigationService.NavigateTo(new Uri("/Page1.xaml", UriKind.Relative));
- }
- /// Navigates the with parameter command.
- private void NavigateWithParameter()
- {
- _navigationService.NavigateTo(new Uri("/Page2.xaml?parameter=1", UriKind.Relative));
- }
- }
- using System.Windows.Input;
- using Cimbalino.Phone.Toolkit.Services;
- using GalaSoft.MvvmLight;
- using GalaSoft.MvvmLight.Command;
- public class Page2ViewModel : ViewModelBase
- {
- /// The navigation service
- private readonly INavigationService _navigationService;
- /// Initializes a new instance of thePage2ViewModel
- /// "navigationService is the navigation service.
- public Page2ViewModel(INavigationService navigationService)
- {
- _navigationService = navigationService;
- GoBackCommand = new RelayCommand(GoBack);
- }
- /// Gets the parameter.
- public string Parameter
- {
- get
- {
- return _navigationService.QueryString["parameter"].ToString();
- }
- }
- /// Gets a value indicating whether [can go back].
- /// true if [can go back]; otherwise false
- public bool CanGoBack
- {
- get
- {
- return _navigationService.CanGoBack;
- }
- }
- /// Gets the go back command.
- public ICommand GoBackCommand { get; private set; }
- /// Go back
- private void GoBack()
- {
- _navigationService.GoBack();
- }
- }
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">
- <!-- LayoutRoot is the root grid where all page content is placed -->
- <Grid x:Name="LayoutRoot" Background="Transparent">
- <Grid.RowDefinitions>
- <RowDefinition Height="Auto" />
- <RowDefinition Height="*" />
- </Grid.RowDefinitions>
- <!-- TitlePanel contains the name of the application and page title -->
- <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>
- <!-- ContentPanel - place additional content here -->
- <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>
- DataContext="{Binding MainViewModel,
- Source={StaticResource Locator}}"
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">
- <!-- LayoutRoot is the root grid where all page content is placed -->
- <Grid x:Name="LayoutRoot" Background="Transparent">
- <Grid.RowDefinitions>
- <RowDefinition Height="Auto" />
- <RowDefinition Height="*" />
- </Grid.RowDefinitions>
- <!-- TitlePanel contains the name of the application and page title -->
- <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>
- <!-- ContentPanel - place additional content here -->
- <Grid x:Name="ContentPanel"
- Grid.Row="1"
- Margin="12,0,12,0">
- <TextBlock>Page1 without parameter</TextBlock>
- </Grid>
- </Grid>
- </phone:PhoneApplicationPage>
- <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">
- <!-- LayoutRoot is the root grid where all page content is placed -->
- <Grid x:Name="LayoutRoot" Background="Transparent">
- <Grid.RowDefinitions>
- <RowDefinition Height="Auto" />
- <RowDefinition Height="*" />
- </Grid.RowDefinitions>
- <!-- TitlePanel contains the name of the application and page title -->
- <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>
- <!-- ContentPanel - place additional content here -->
- <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>

Join the conversation! Your thoughts help the community grow.