Sharing values across WP7 pages

When you’re writing a Silverlight application, such as Windows Phone 7 application, it is common to have a value one one page and want to use the value on another page. A good solution for this situation is to use application resources, or to be more specific using the ResourceDictionary for the current application. You can access to the application resources using the Application.Current.Resources property.

ApplicationResources class

If you use application resources frequently, you’ll find that working directly with Application.Current.Resources is a little tedious and can clutter the code with details of working with the ResourceDictionary object. I like to wrap these details in a general purpose ApplicationResources class, and then use that class to implement an ApplicationProperties class specific to my application. It provides a nice degree of abstraction, making my application easier to write and later read, and the right amount of reuse across applications.

Let’s start with the ApplicationResources class that I can use without alteration in any WP7 application. This class encapsulates the low-level details of working with application resources. I’ve remove most comments for readability, but you’ll find them in the source code download, see below.

using System.Windows;

namespace VisualStuart.WP7.Applications
{
  public class ApplicationResources
  {

    public static T GetResource<T>( object key )
    {
      object value = AppResources[ key ];
      return ( value == null )  // if resource doesn't exist or is set to null
        ? default( T )          // return null for reference type, 0 for numeric value types, etc.
        : (T)value;             // otherwise return the resource value
    }

    public static void SetResource( object key, object resource )
    {
      if ( AppResources.Contains( key ) )
        AppResources.Remove( key );

      AppResources.Add( key, resource );
    }

    // Helper methods

    private static ResourceDictionary AppResources
    { get { return Application.Current.Resources; } }
  }
}

The ResourceDictionary stores resources as System.Object types, so the GetResource<T> method adds type safety to the return type. It also deals with two related considerations. First, it is possible for an application to try to get a resource before setting the resource. Rather than flagging that situation as an error, e.g., by throwing an exception, and making the consuming code deal with that, I’ve opted to return a reasonable default value when getting resource before it is set. Second, the resource type might be a value type (such as bool, int, or a struct) which cannot be null. Now, using a key to index into a ResourceDictionary that does not contain that key will return null. That’s an acceptable value for reference types, but will generate an error for value types. The default keyword for generics provides just what is needed for both value and reference types.

The SetResource method hides a distracting little detail that application resources are not mutable: once a resource has been added to the ResourceDictionary its value cannot be changed. However, you can create the illusion of changing a resource’s value by first removing the old resource, if it exists, and then adding the new resource.

While the ApplicationResources class successfully encapsulates most of the details of programming with application resources, it does not address the question of managing the keys used within an application. That’s next.

ApplicationProperties

I really want to avoid unrestricted use of string literals as application resource keys, sprinkling them throughout my code. The big problem with that is what happens when one of those string values changes, or I just type it wrong in the first place. A string value is a string value, my code will compile without error, and I’ll be faced with debugging a problem that can be extremely subtle to detect. So I like to create an ApplicationProperties class for each application that is the exclusive consumer of the ApplicationResources class in the application.

using VisualStuart.WP7.Applications;

namespace WP7ShareValueAcrossPages
{
  /// <summary>
  /// Properties backed by application resources.
  /// </summary>
  public class ApplicationProperties
  {
    // Properties

    // Sample property of a reference type
    public static string UserText
    {
      get { return ApplicationResources.GetResource<string>( userTextKey ); }
      set { ApplicationResources.SetResource( userTextKey, value ); }
    }

    // Sample property of a value type
    public static bool UserLikesPears
    {
      get { return ApplicationResources.GetResource<bool>( userLikesPearsKey ); }
      set { ApplicationResources.SetResource( userLikesPearsKey, value ); }
    }

    // Resource keys

    // A key can be an object of any type. Strings are used here for readability.
    private static readonly string userLikesPearsKey = "userLikesPears";
    private static readonly string userTextKey = "UserText";
  }
}

This class encapsulates the keys used for application resources, and makes the resources available as properties. The setter method also strengthens the type-safety, since the property enforces that the getter and setter use identical types.

Since this class is specific to each application that I write, I can customize it as needed. For example, I can create a read-only property by marking the visibility of the set method as private. Or I can insert additional logic in the getter and setter to match the data semantics of my application.

Using application properties

Putting it all together, I have created a sample Widows Phone 7 application containing two pages. Values set on PageOne are consumed on PageTwo.

using System.Windows;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;

namespace WP7ShareValueAcrossPages
{
  public partial class PageOne : PhoneApplicationPage
  {
    // other code elided...

    protected override void OnNavigatedFrom( NavigationEventArgs e )
    {
      ApplicationProperties.UserText = UserTextBox.Text;
    }

    private void LikePearsCheckBox_Checked( object sender, RoutedEventArgs e )
    {
      ApplicationProperties.UserLikesPears = LikePearsCheckBox.IsChecked ?? false;
    }
  }
}

When navigating away from PageOne, the event handler saves a value from a TextBox control on the page into the UserText application property that I created above. The syntax is clear and simple.

Another event handler is invoked each time a a CheckBox control on the page is checked or unchecked, and the handler sets the UserLikesPears application property accordingly. That leaves open the possibility that the CheckBox may never checked before navigating away from PageOne. That works in a completely natural way in the consuming PageTwo.

using System.Windows.Navigation;
using Microsoft.Phone.Controls;

namespace WP7ShareValueAcrossPages
{
  public partial class PageTwo : PhoneApplicationPage
  {
    // other code elided...

    protected override void OnNavigatedTo( NavigationEventArgs e )
    {
      string userText = ApplicationProperties.UserText;

      if ( string.IsNullOrEmpty( userText ) )
        userText = "(You didn't enter any text)";

      userTextBlock.Text = userText;

      pearPreferenceTextBlock.Text = ApplicationProperties.UserLikesPears
        ? "You like pears"
        : "You do not like pears";
    }
  }
}

When PageTwo is navigate to, the UserText and UserLikesPears application properties are used in setting the text on different controls. If the bool UserLikesPears property was not set on PageOne, the default value for bool (false) is provided. Since the CheckBox on PageOne is initially unchecked, this default value works correctly.

Resources

Download the completed code.

You will need the Windows Phone SDK to compile and run the sample code.

Comments are closed.