Navigating with Data in Windows Phone

Tweet

In my previous post you saw how to navigate forward to a new page and then back either via the hardware back button or by calling the GoBack method. Quite often you’ll want to pass some data between pages, for example if a user selects a customer from a list you can pass the customer id or name through to the customer details page. This post will not only cover how to do this, it will also highlight the sequence of events that are raised during the navigation process.

For the purpose of this post we’ll be using a simple example application that has two pages, CustomerListPage and CustomerDetailsPage. From the CustomerListPage the user can select on a customer in the list which will invoke a navigation to the details page. For example:

private void CustomerSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    NavigationService.Navigate(new URI("/CustomerDetailsPage.xaml", URIKind.Relative));
}

This isn’t sufficient as the details page will need to know what customer to display. In a traditional rich client technology such as ‘WinForms’ or ‘WPF’ you might be tempted to try to pass a reference to the customer object through to the customer details page. Unfortunately that strategy will not work in Windows Phone application as the navigation between pages has to be resilient to the application being tombstoned. Without going on a massive tangent to discuss tombstoning, essentially when your application goes into the background it is likely to be tombstoned which means it’s no longer in memory. When the application is subsequently resumed, for example by the user clicking the back button to return to your application, the page that was previously opened will be created and navigated to but any in memory objects will have to be reloaded. More information on Windows Phone application lifecycle and tombstoning can be found in the MSDN documentation.

To avoid running into issues related tombstoning you should treat every page as if it exists in isolation. This means that the only way to pass data into a page is via the navigation URI. As the navigation URI is a standard relative URI it supports both query string parameters and fragments. For example if you wanted to pass the customer id through to the customer details page you could use either of the following:

NavigationService.Navigate(new URI("/CustomerDetailsPage.xaml?customerId=12345", 
                                                                   URIKind.Relative));
NavigationService.Navigate(new URI("/CustomerDetailsPage.xaml#12345", 
                                                                  URIKind.Relative));

The first method specifies the customer id as a query string parameter. This can easily be extracted in the destination page by examining the QueryString property of the NavigationContext.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    var customerId = NavigationContext.QueryString["customerId"];
}

The second method specifies the customer id as a URI fragment. This method is somewhat limiting in that you can only specify a single value, whereas with the query string you can supply multiple key-value pairs. However, what’s nice about using the fragment is that there is an additional event that is raised, allowing the page to navigate to a specific fragment.

protected override void OnFragmentNavigation(FragmentNavigationEventArgs e)
{
    base.OnFragmentNavigation(e);

    var customerId = e.Fragment;
}

At this point I’d like to jump back a little and just recap on the sequence of events that occur when a navigation is triggered. This should help clarify where the above code for extracting the customer id would appear. The navigation process starts with a call to the Navigate method, either directly on the PhoneApplicationFrame or on the NavigationService property of the current page (show at the top of Figure 1).

Navigation With Data Figure 1

Figure 1

The remainder of Figure 1 is split into two rounded boxes: The left box indicates events that occur on the PhoneApplicationFrame. You can optionally subscribe to these events in the App.xaml.cs file, which is useful if you want to log navigation events. In most cases you’ll be more interested in the methods that are available for you to override in the pages of your application, which inherit from the PhoneApplicationPage class.

After a navigation is triggered the first event that is raised on the frame is the Navigated event. Both this event and the corresponding method, OnNavigatingFrom, which is called on the page that the user is navigating away from, expose a NavigationEventArgs argument. This argument inherits from CancelEventArgs which exposes a property, Cancel, which can be set to true if you wish to cancel the navigation. This might be useful if you wanted to run an animation, save transient state or perform page clean up actions.

At this point the navigation service determines whether or not the navigation has been cancelled. If it has the NavigationStopped event is raised on the frame. Assuming that the navigation hasn’t been cancelled the navigation proceeds. If for whatever reason the navigation fails, for example if the URI specified isn’t a valid page, or can’t be resolved to a valid page, the NavigationFailed event will be raised. In the case of either the NavigationStopped or NavigationFailed events, there is no equivalent method called on the page itself.

In the case where the navigation proceeds as expected the Navigated event will be raised on the frame. This is followed by a call to the OnNavigatedFrom method (on the page being navigated away from) and to the OnNavigatedTo method (on the page being navigated to). Lastly, if the URI contains a fragment, the FragmentNavigation event is raised on the frame, followed by a call to the OnFragmentNavigation method on the page being navigated to. As you can see the OnNavigatedTo and OnFragmentNavigation method referenced at the start of this post form the final stage of the navigation sequence, and are the best place to extract either query string or fragment from the navigation URI.

Another concept that has been ported from the web is the concept of URI mapping or rewriting. For Silverlight for the desktop this was introduced primarily as a way for an application to expose human readable deep link urls. In the context of a Windows Phone application the user will never see the URI of the page that they are navigating to. However, there are still circumstances where it makes sense to do URI mapping. Take the previous example where the application was navigating to CustomerDetailsPage and supplying the customer id as a parameter. If the name of this page changes, or the page is moved into a sub-folder, or even if the parameter name changes from customerId to perhaps just ID, in each of these cases you would have to ensure that every navigation method is updated. An alternative would be to define a URI mapping for displaying the customer details page. For example:

/customer/{id}      =>      /CustomerDetailsPage.xaml?customerId={id}

Now, if any of the changes discussed were to happen, all you would have to do is change the mapping:

/customer/{id}      =>      /CustomerInfoPage.xaml?customerId={id}
/customer/{id}      =>      /Pages/CustomerDetailsPage.xaml?customerId={id}
/customer/{id}      =>      /CustomerDetailsPage.xaml?Id={id}

There are two options for defining a URI mapping: You could simply create an instance of the URIMapper class and assign it to the URIMapper property on the frame, or you could create the URIMapper as a resource in the App.xaml file. The latter makes the URIMapper easier to locate and update.

<Application
    x:Class=&quot;CustomerNavigationSample.App&quot;      
    xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;       
    xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
    xmlns:shell=&quot;clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone&quot; 
    xmlns:Navigation=&quot;clr-namespace:System.Windows.Navigation;assembly=Microsoft.Phone&quot; >

    <Application.Resources>
        <Navigation:URIMapper x:Key=&quot;Mapper&quot;>
            <Navigation:URIMapper.URIMappings>
                <Navigation:URIMapping URI=&quot;/customer/{id}&quot;
                                       MappedURI=&quot;/CustomerDetailsPage.xaml?customerId={id}&quot; />
            </Navigation:URIMapper.URIMappings>
        </Navigation:URIMapper>
    </Application.Resources>
   …
</Application>

After defining the URIMapper in the App.xaml you’ll need to assign it to the PhoneApplicationFrame in the App.xaml.cs file.

public App()
{
    // Global handler for uncaught exceptions. 
    UnhandledException += Application_UnhandledException;

    // Standard Silverlight initialization
    InitializeComponent();

    // Phone-specific initialization
    InitializePhoneApplication();
    RootFrame.URIMapper = Application.Current.Resources[&quot;Mapper&quot;] as URIMapper;

   …
}

Now that the URI mapping has been defined you can navigate to the customer details page using the following, irrespective of whether the page changes name, location or the parameter name changes (assuming of course that the mapping is updated).

this.NavigationService.Navigate(new URI(&quot;/customer/12345&quot;, URIKind.Relative));

I’ll leave you with a closing remark in that this separation of navigation intent, from the page implementation is a useful concept within Windows Phone today but becomes even more relevant when you look to the deep linking capabilities of Mango. The Mango update for Windows Phone will introduce deep link both from Start Tiles but also from Toast Notifications. Since Toast Notifications will most commonly be sent from a server, which shouldn’t know or care about how the phone application is implemented, being able to separate the navigation intention from the application specifics in important to allow for the server and application to be versioned independently. For example the server could send the following toast notification out.

<?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?>
<wp:Notification xmlns:wp=&quot;WPNotification&quot;>
    <wp:Toast>
        <wp:Text1>Customer</wp:Text1>
        <wp:Text2>Bob Jones</wp:Text2>
        <wp:Param>/customer/12345</wp:Param>
    </wp:Toast>
</wp:Notification>

This would appear as a toast notification to the phone user with the text “Customer Bob Jones” (similar to an incoming SMS message). When they click on the notification the application would be launched and the Param element value of “/customer/12345” would be interpreted by the application in order to determine what page to display. Using the URI mapping we defined earlier this would route to “/CustomerDetailsPage.xaml?customerId=12345”. However, the application could be restructure and the URI mapping changed without having to modify the server code.

Hopefully after reading this post you will have a good understanding of the navigation service of Windows Phone and the corresponding events and methods. With this knowledge you should be able to effectively pass data between pages whilst still handling the different scenarios that arise from tombstoning.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments