×
Namespaces

Variants
Actions
(Difference between revisions)

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

From Nokia Developer Wiki
Jump to: navigation, search
hamishwillee (Talk | contribs)
m (Hamishwillee - Add draft, fix categories)
Kunal Chowdhury (Talk | contribs)
(Kunal Chowdhury -)
Line 84: Line 84:
 
         }
 
         }
 
     }
 
     }
 +
  
 
== Using a Dictionary to Store the Hierarchy of the Navigation and it's associated Data ==
 
== Using a Dictionary to Store the Hierarchy of the Navigation and it's associated Data ==
 +
Sometime you might need to store complex object from one page to other and then another, e.g. ordering a product. In such scenario, the above simple code will not work for you. To handle this, you will need to store all the navigational data in a list like Dictionary and then use it whenever you need. To implement such functionality, you have to store the data in proper way and remove it when require as per your need.
  
== Further Improvements ==
+
To implement such behavior in your {{Icode|NavigationService}} extension, first declare a Dictionary of type <string, object> as shown below:
 +
    private static readonly Dictionary<string, object> Data = new Dictionary<string, object>();
 +
Now, you need to implement the {{Icode|Navigate}} method to pass Uri, key and data to that method. Make sure that, you are checking the pre-existance 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 {{Icode|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 below 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 my {{Icode|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();
 +
        }
 +
    }
  
  
== Summary ==
+
== Further Improvements ==
 +
These were two different ways to implement extension method to pass complex object to the {{Icode|NavigationService}} and use it in different pages. I still think that, this code can be improve to make it easier for the developers.

Revision as of 19:21, 25 November 2013

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: Kunal Chowdhury (25 Nov 2013)

Contents

Introduction

The sealed class NavigationService present under System.Windows.Navigation namespace provides methods and events to support navigation from one page to another inside Windows Phone applications. As the class is Sealed, you can not extend it directly to implement your functionalities.

The Navigate(…) method of NavigationService only takes one parameter of type Uri where you can pass strings as part of query string parameters.This is simple if you are passing few strings as query string parameter value to the other page and you can get the value from the NavigationContext. But what about complex type values like Person, Employee, Product etc. that holds different simple and/or complex data? Here we are going to discuss about it further and see various alternative methods to achieve the said functionalities.

Passing Complex Data Types using Extension Method

The workaround to this problem is assigning a static/shared variable or using the Application State to pass value from one page to other. But hold on… do we have any other alternative to that or can Navigation Service itself handle it? To answer your query, I will first say “No, you can not do this with Navigation Service” but secondly “Yes, you can do so”. Huh!!! Isn’t it a contradictory answer of my own reply? Okay, let me clarify what I said here first.

The NavigationService does not allow you to pass a complex object or a complex data type as part of query string parameter. You can not even extend it using inheritance because it is a sealed class. But you can extend the class using an Extension method and do whatever you want to play with it.

Let’s see the extension method to implement our own Navigate() method where we will be able to pass a complex data type to it:

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

Here the first parameter says that, it is an extension method of NavigationService class, the second parameter takes the URI of the page where we want to navigate and the third parameter takes the data object. Here please note that, you don’t have to pass an instance of NavigationService as part of the first parameter because it is an extension method of it. You just have to pass the source and the data.

As stated earlier, call the NavigationService.Navigate(…) method passing the URI of the page and the object that you want to send to the destination page. Here is a sample code snippet for you to visualize it clearly:

   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);
   }

Now once you are in the destination page, instead of calling NavigationContext to get the query string parameter value, you can call the GetNavigationData() method which is again an extension method of the class NavigationService. Calling the method will return you the passed object. Here is a code snippet of the same:

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

As per your requirement, you can keep the data object in NavigationService as long as you want or you can nullify it once you retrieve it. This way you will be able to use the NavigationService and pass objects to second page in a completely cleaner way.

Here is my complete source code of the extension methods of my NavigationService that helped me in many way during my whole application:

   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;
       }
   }


Using a Dictionary to Store the Hierarchy of the Navigation and it's associated Data

Sometime you might need to store complex object from one page to other and then another, e.g. ordering a product. In such scenario, the above simple code will not work for you. To handle this, you will need to store all the navigational data in a list like Dictionary and then use it whenever you need. To implement such functionality, you have to store the data in proper way and remove it when require as per your need.

To implement such behavior 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>();

Now, you need to implement the Navigate method to pass Uri, key and data to that method. Make sure that, you are checking the pre-existance 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 below 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 my 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();
       }
   }


Further Improvements

These were two different ways to implement extension method to pass complex object to the NavigationService and use it in different pages. I still think that, this code can be improve to make it easier for the developers.

266 page views in the last 30 days.
×