A Matter of State, Part 2

The previous post discussed using the Page State Dictionary to restore a page’s state when navigating back to that page following the application being tombstoned. This post will discuss the use of the Application State Dictionary, and the final post in the series will discuss using Isolated Storage.

The Application State Dictionary

The Application State Dictionary has a broader scope for availability than the Page State Dictionary, but still has some similar limitations. It is also implemented as an IDictionary<String, Object>, and is exposed through the PhoneApplicationService instance’s State property. The current instance of the PhoneApplicationService is usually accessed through the class’s Current static property – eg PhoneApplicationService.Current. Any objects stored in the Application State Dictionary need to be serializable. It is important to note that if an item is added to the dictionary when debugging, an ArgumentOutOfRangeException will be raised, but at runtime this exception is silently ignored.

Although the documentation does not call out any specific size limitations, empirical testing has shown there is one of about 1.5MB (it actually varies between the emulator and an actual device.) Storing data larger than that will result in an unhandled COM Exception with the message “The client who called IAccessControl::IsAccessPermitted was the trustee provided to the method” [sic]. This error does not happen immediately, but instead happens when the application is tombstoned, after the Application’s Deactivated event.

The contents of the Application State Dictionary can only be accessed within or after the application’s Activated event has been raised and within or before the application’s Deactivated event has been raised. Any attempt to access the contents outside of these times will result in an InvalidOperationException being thrown. Just like the Page State Dictionary, once these limitations are understood, the use of the Application State Dictionary is straightforward.

private void Application_Deactivated(Object sender, DeactivatedEventArgs e)
{
PhoneApplicationService.Current.State[“MyStuffToSave”] = value;
}

private void Application_Activated(Object sender, ActivatedEventArgs e)
{
if (PhoneApplicationService.Current.State.ContainsKey(“MyStuffToSave”))
{
value PhoneApplicationService.Current.State[“MyStuffToSave”];
    }
}

 

Because the PhoneApplicationService is available throughout the application via the PhoneApplicationService.Current static property, the Application State Dictionary is available throughout most of the application (though it usually wise to respect proper architectural boundaries and hence be aware of what layer it is being called from.) In that light, the Application State Dictionary can be seen as a kind of globally-scoped collection of name-value pairs.

Both of the State Dictionaries keep their data in memory as opposed to Isolated Storage, which uses the phone’s file system. As a result, access to data is significantly faster. Some quick testing showed this to be ~30% faster on the emulator, and ~55% faster on an actual device, though individual devices are likely to report other results due to hardware differences.

There are some interesting scenarios that this state repository offers, including:

  • Remembering page control values for returning forward to a page
  • Passing data between pages that is too complex to pass via a simple QueryString parameter (though the key to the value in the dictionary may certainly be passed in the QueryString), without declaring a global variable to hold the data.
  • Remembering application-wide settings for restoration after a tombstoned application is reactivated

To use the Application State Dictionary to set page values following navigation, instead of using the Page State Dictionary to store a control’s state when leaving a page and to restore the state when returning to the page, the Application State Dictionary can be used, as illustrated in Figure 1, below.

Figure 1

  • The “Go to Page 2” button is used from Page 1 to navigate to Page 2.
  • In Page 2, the user enters “User State 1” in the first textbox and “User State 2” in the second textbox.
  • When the user presses the Back Button from Page 2, the value of textbox 1 is put into the Application State Dictionary during the OnNavigatedFrom method
  • The user presses the “Go to Page 2” button again. In the page’s OnNavigatedTo method, the Application State Dictionary is interrogated to see if it has a value, and if so, the value is applied to the first textbox
  • Note – if there is the possibility that the page whose state is being saved can be used in different contexts (for example, a page that shows details for a selected item on the first page), the key value for the setting should likely include the context, in order to properly resolve which value is to be displayed.

Another scenario where the Page State Dictionary can be used is for passing complex objects between pages. Remembering that navigation between pages on the phone does not involve actually creating an instance in code of the page to navigate to – the call to the Navigate method of the NavigationService with the Uri of the page to navigate to takes care of creating the new page instance. Although data can be communicated between pages via a QueryString parameter, complex data may not translate well to QueryString values. Using the Application State Dictionary to supplement the exchange of data between pages via QueryString information is illustrated in Figure 2.

Figure 2

  • The “Go to Page 2” button is used from Page 1 to navigate to Page 2
  • A value is added to the Application State Dictionary. Remember that the item must be serializable to be placed in the Application State Dictionary.
  • The key for the value added to the Application State Dictionary in the previous step is added as a ValueKey parameter to the Uri provided for the Navigate command
  • In the Page 2 OnNavigatedTo method, the NavigationContext is interrogated for the QueryString ValueKey parameter. If the key is found, its value is used to retrieve the serialized object from the Application State Dictionary
  • Page 2 is then able to interact with the object that was passed to it from Page 1

Finally, the most common use of the Application State Dictionary is to preserve and restore application-scoped state when an application is tombstoned and reactivated. This process is shown in Figure 3.

Figure 3

  • At some point the application is tombstoned as a response to some user action (pressing the Start or Search button, etc.)
  • In the Application’s Deactivated event handler, the application’s state is stored in the Application State Dictionary.
  • The user presses the Back button to return to the application.
  • In the Application’s Activated event handler, data is read form the Application State Dictionary and used to restore the application state.

Another important note is that the application’s Deactivated event handler must complete in 10 seconds or less, or the application might be forcibly terminated and may be unavailable via the back stack.

The Application State Dictionary and Choosers

The term “Chooser” is applied to Windows Phone API elements that allow the user to launch an application that is part of the Windows Phone system in order to make a selection, and upon completion, return that item to the calling program by raising a Completed event. Choosers include:

  • The CameraCaptureTask launches the Camera application and returns a PhotoResult object (which includes a stream that contains the image data.)
  • The EmailAddressChooserTask launches the Contacts application and returns an EmailResult object (which includes the selected email address as a string.)
  • The PhoneNumberChooserTask launches the Contacts application and returns a PhoneNumberResult object (which includes the selected phone number as a string.)
  • The PhotoChooserTask launches the Photo Picker application and returns a PhotoResult object (outlined above.)
  • The SaveEmailAddressTask saves the provided email address to the Contacts list (it does not actually return any data, other than an indication as to whether the address was added.)
  • The SavePhoneNumebrTask saves the provided phone number to the Contacts list (it does not actually return any data, other than an indication as to whether the address was added.)

Choosers are distinguished from Launchers, which allow the application to launch certain Windows Phone system applications in a fire-and-forget model. Some Launchers include the WebBrowserTask, which opens a web browser with the given address, and the EmailComposeTak, which displays a new email message with given values for recipients, message subject, and message body, and there are several additional Launchers.

The guidance given for using Choosers advises that the Chooser’s completed event should be subscribed in the Page’s constructor. This is because when the Chooser is invoked, the running application *might* be tombstoned, losing the event subscription as part of that process. (There is documentation indicating that Choosers have been optimized to not always tombstone an application. Ssee the bottom of the article at http://msdn.microsoft.com/en-us/library/ff817008%28v=VS.92%29.aspx for a description of the optimization that may bypass this…still, since there is a grain of unpredictability, the action needs to be handled as if tombstoning is a certainty.)

The problem with Choosers is that the Completed event is actually raised before the page’s OnNavigatedTo method is called. This means that the Page State Dictionary is not available when responding to a Chooser’s completed event, and attempting to access it will result in an InvalidOperationException. Imagine the following scenario:

  • A page displays a list of items
  • The user can select an item in the list and press a Send button
  • The Send button invokes a Chooser to allow the user to select an email address
  • On completion of the Chooser, the page will use the EmailComposeTask Launcher to compose an email to the selected address with the text from selected item in the email’s body.

Using the Application State Dictionary, the selected item in the list can be saved off while the Email Address Chooser (potentially) tombstones the application and restored when the application returns with the chosen address, as the following code illustrates:

private readonly EmailAddressChooserTask _chooser = new EmailAddressChooserTask();

public Page1()
{
InitializeComponent();
    _chooser.Completed += HandleEmailAddressChooserCompleted;
}

private void SendButton_Click(Object sender, RoutedEventArgs e)
{
if (SelectionListBox.SelectedItem == nullreturn;
    PhoneApplicationService.Current.State[“InterestingItemToSend”] = SelectionListBox.SelectedItem.ToString();
    _chooser.Show();
}

private void HandleEmailAddressChooserCompleted(Object sender, EmailResult e)
{
if (e.TaskResult == TaskResult.OK)
    {
     var emailComposeLauncher = new EmailComposeTask
         {
             To = e.Email,
                Subject = “This is Interesting”,
                Body =
                 “Check out this interesting thing” +
                    PhoneApplicationService.Current.State[“InterestingItemToSend”]
            };
        emailComposeLauncher.Show();
    }
}

 

So now we’ve seen how the Application State Dictionary adds to the functionality offered by the Page State Dictionary, including exchanging information through several parts of an application, saving application state information across tombstoning events, and how it can be used to allow Choosers to get access to page state information when they return control to the calling application. The next installment will explore support for Isolated Storage in the Windows Phone system.

Advertisements

One thought on “A Matter of State, Part 2

  1. Pingback: A Matter of State, Part 3 « DotNet Gator

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s