Namespaces

Variants
Actions

Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries over the next few weeks. Thanks for all your past and future contributions.

How to pass a complex object to a page by extending NavigationService on Windows Phone

From Wiki
Jump to: navigation, search

This article explains how to pass a complex object to a Windows Phone page by extending the NavigationService.

SignpostIcon XAML 40.png
WP Metro Icon WP8.png
SignpostIcon WP7 70px.png
Article Metadata
Tested with
SDK: Windows Phone 7.1 SDK, Windows Phone 8.0 SDK
CompatibilityArticle
Created: Kunal Chowdhury (04 Nov 2013)
Last edited: hamishwillee (22 Jan 2014)

Contents

Introduction

NavigationService (in System.Windows.Navigation namespace) provides methods and events to support navigation from one page to another in a Windows Phone app. The Navigate() method takes one parameter of type Uri. You can pass strings between pages by specifying them as a Query String parameter when sending, and then retrieve them in the receiving page using the NavigationContext.

This is fine if all that is needed is to pass strings, but in some cases it would be useful to be able to pass other (more complex) objects between pages - for example "persons", "employees", "products". Unfortunately as the class is sealed, you cannot extend it by inheritance to implement this additional functionality. You can however extend it by implementing a C# extension method.

This article shows how to provide the required functionality. It provides two implementations: the first for sharing complex objects between any two pages, the second which uses a dictionary for storing the object and a key - making it easier to share objects in multi-page hierarchies.

Complex object in a static variable

While we could have a separate static variable in the app for holding values to be passed between pages, it is more elegant to use the NavigationService. While this class cannot be extended by inheritance, we can extend it using an extension method! This allows us to create a method overload which takes an arbitrary object, which we store in a static variable.

The implementation of the Navigate() method is as below:

    public static void Navigate(this NavigationService navigationService, Uri source, object data)
{
Data = data;
navigationService.Navigate(source);
}

The first parameter indicates that this is an extension method of NavigationService class, and should not be specified when the function is used. To use this function specify the target page (URI) and object to pass, respectively. The object passed to the method is stored in a static variable.

Here is a code snippet showing how the extension method is used:

    private void OnNextButtonClicked(object sender, RoutedEventArgs e)
{
var person = new Person {Name = "Kunal Chowdhury", Blog = "www.kunal-chowdhury.com"};
NavigationService.Navigate(new Uri("/NextPage.xaml", UriKind.Relative), person);
}

In the destination page we call another extension method GetNavigationData() to get the passed object (instead of calling NavigationContext to get the query string parameter value):

    protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
 
var person = (Person)NavigationService.GetNavigationData();
MessageBox.Show("Blog of " + person.Name + " is: " + person.Blog);
}


You can keep the data object in NavigationService or you can nullify it once you retrieve it - keeping the data object allows you to easily pass the same object to second page.

Below is the complete source code of the extension methods of the NavigationService:

    public static class Extensions
{
private static object Data;
 
/// <summary>
/// Navigates to the content specified by uniform resource identifier (URI).
/// </summary>
/// <param name="navigationService">The navigation service.</param>
/// <param name="source">The URI of the content to navigate to.</param>
/// <param name="data">The data that you need to pass to the other page
/// specified in URI.</param>
public static void Navigate(this NavigationService navigationService, Uri source, object data)
{
Data = data;
navigationService.Navigate(source);
}
 
/// <summary>
/// Gets the navigation data passed from the previous page.
/// </summary>
/// <param name="service">The service.</param>
/// <returns>System.Object.</returns>
public static object GetNavigationData(this NavigationService service)
{
return Data;
}
}


Multi-page hierarchies - Using a Dictionary to store shared data

Consider the case of navigating into a multi-page hierarchy using the API defined in the previous section. If you navigate to a page using a particular object and then navigate to another page using another object then the first object that was passed is lost - if you go back to the page it will have access to data for the wrong page.

While it is possible to work around this in each page, it is again more elegant to store the appropriate navigation data in the extension method. In this section we store the data in a Dictionary and use a key to get the correct object when needed.

To implement such behaviour in your NavigationService extension, first declare a Dictionary of type <string, object> as shown below:

    private static readonly Dictionary<string, object> Data = new Dictionary<string, object>();

Then implement the Navigate() method to pass Uri, key and data to that method. Make sure that, you are checking the pre-existence of the key in the dictionary.

    public static void Navigate(this NavigationService navigationService, Uri source, string key, object data)
{
if (Data.ContainsKey(key))
{
Data.Remove(key);
}
 
Data.Add(key, data);
navigationService.Navigate(source);
}

Similarly, in the GetNavigationData() method get the value from the dictionary and remove it by default before returning it to the caller method. In some case, you might need to store the data to use in multiple places. This method should handle that properly as shown in the code snippet:

    public static object GetNavigationData(this NavigationContext context, string key, bool persistData = false)
{
var value = Data.ContainsKey(key) ? Data[key] : null;
 
if (persistData == false)
{
context.Remove(key);
}
 
return value;
}

Add some additional methods like Remove() and Clear() in the class to handle the dictionary object properly. Here is the new complete extension methods of the NavigationService class:

    public static class Extensions
{
private static readonly Dictionary<string, object> Data = new Dictionary<string, object>();
 
/// <summary>
/// Navigates to the content specified by uniform resource identifier (URI).
/// </summary>
/// <param name="navigationService">The navigation service.</param>
/// <param name="source">The URI of the content to navigate to.</param>
/// <param name="key">The key.</param>
/// <param name="data">The data that you need to pass to the other page
/// specified in URI.</param>
public static void Navigate(this NavigationService navigationService, Uri source, string key, object data)
{
if (Data.ContainsKey(key))
{
Data.Remove(key);
}
 
Data.Add(key, data);
navigationService.Navigate(source);
}
 
/// <summary>
/// Gets the navigation data passed from the previous page.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="key">The key.</param>
/// <param name="persistData">if set to <c>true</c> [persist data].</param>
/// <returns>System.Object.</returns>
public static object GetNavigationData(this NavigationContext context, string key, bool persistData = false)
{
var value = Data.ContainsKey(key) ? Data[key] : null;
 
if (persistData == false)
{
context.Remove(key);
}
 
return value;
}
 
/// <summary>
/// Removes the specified context.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="key">The key.</param>
public static void Remove(this NavigationContext context, string key)
{
if (Data.ContainsKey(key))
{
Data.Remove(key);
}
}
 
/// <summary>
/// Clears the specified context.
/// </summary>
/// <param name="context">The context.</param>
public static void Clear(this NavigationContext context)
{
Data.Clear();
}
}

The next paragraphs show how these extension functions are used.

Call the new extension method NavigationService.Navigate() method passing the Uri of the target page along with the key and the object to be sent to the new page. Any key can be used, but for the purposes of this demonstration, I used a simple string "person":

    var person = new Person {Name = "Kunal Chowdhury", Blog = "www.kunal-chowdhury.com"};
NavigationService.Navigate(new Uri("/NextPage.xaml", UriKind.Relative), "person", person);

To retrieve the object in the destination page, you can call the extension method NavigationContext.GetNavigationData() passing the key that you specified in the original page. The second parameter persistData is a boolean parameter to indicate whether the record should be deleted from the dictionary once you call the method to retrieve the data. By default persistData is set to false, so the data for the key will be deleted.

    // get the object from the context passing the key
var person = (Person)NavigationContext.GetNavigationData("person");
MessageBox.Show("Blog of " + person.Name + " is: " + person.Blog);

Set persistData to true if the data should be passed to another page - the data will remain in the dictionary until you call Remove(key) or Clear() methods explicitly on NavigationContext. Here is a code snippet for your reference:

    // get the object from the context passing the key and persist the data
var person = (Person)NavigationContext.GetNavigationData("person", true);
MessageBox.Show("Blog of " + person.Name + " is: " + person.Blog);

Summary

This article has provided two different ways to implement extension methods to pass complex object to the NavigationService and use it in different pages. There may be other methods - please extend this article as needed!

This page was last modified on 22 January 2014, at 04:07.
462 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×