Friday, September 24, 2010

Chrysalis – Tombstoning support (Part I - Introduction)

As I discussed in my previous post, when you are writing Silverlight (or XNA) based Windows Phone 7 applications, one of the considerations you must make is regarding supporting the “tombstoning” of your application. At any point during its execution your application may receive notification that you must save any state and exit, to possibly (but not always) be restarted at a later point. This however is you application’s view – to the user they must believe that you application was waiting patiently in the background, unaware of the tearing down and restarting that is actually occurring.

If you are following the Model-View-ViewModel presentation pattern then the best placed entity in the system to know about what state to persist when tombstoning is the ViewModel. You have to firstly ensure that the application subscribes correctly to the PhoneApplicationService.Deactivated and PhoneApplicationService.Activated events. These must pass the requests onto the ViewModel which must store its state in the correct locations. Upon reactivation the ViewModel must know that it has some stored state, retrieve it and then restore the UI to that before the deactivation.

Transparently handling these steps is one of the key features of the Chrysalis framework.

How to use Chrysalis for Tombstoning?

Supplied with yesterdays drop of the Chrysalis framework is a sample calculator application. Whilst not particularly useful, it is intended to show how Chrysalis simplifies tombstoning. At its heart is the CalculatorViewModel.

   1: public class CalculatorViewModel : ViewModelBase
   2: {
   3:     // *** Fields ***
   4:     
   5:     private int numberX = 10;
   6:     private int numberY = 5;
   7:     
   8:     // *** Properties ***
   9:     
  10:     public int NumberX
  11:     {
  12:         get
  13:         {
  14:             return numberX;
  15:         }
  16:         set
  17:         {
  18:             numberX = value;
  19:             OnPropertyChanged("NumberX");
  20:             OnPropertyChanged("Total");
  21:         }
  22:     }
  23:     
  24:     public int NumberY
  25:     {
  26:         get
  27:         {
  28:             return numberY;
  29:         }
  30:         set
  31:         {
  32:             numberY = value;
  33:             OnPropertyChanged("NumberY");
  34:             OnPropertyChanged("Total");
  35:         }
  36:     }
  37:     
  38:     public int Total
  39:     {
  40:         get
  41:         {
  42:             return NumberX + NumberY;
  43:         }
  44:     }
  45: }

This derives from Chrysalis.ViewModelBase base class and includes three properties. The properties ‘NumberX’ and ‘NumberY’ are editable and both bound to TextBoxes in the associated UI (CalculatorView.xaml). The ‘Total’ property simply returns the sum of the two other values and is bound to a read-only TextBox in the UI. Property changed notifications are raised by calling OnPropertyChanged(…) on the base class.


At this stage everything will look familiar to the Silverlight or WPF developer, and the application will run as expected,


Calculator


The problem occurs if we now deactivate and reactivate the application. In the emulator provided with the developer tools you can do this by clicking the “Start” button to return to the start page, then clicking “Back” to return to your application. This will result in all values being reset to their initial values as we are not persisting any state. Remember though that the user should believe that the application has been sitting patiently in the background, and expects all the values to be consistent with that.


To solve this problem Chrysalis.ViewModelBase provides a couple of virtual methods that you can override.



protected virtual object SaveState() {...}
protected virtual void RestoreState(object state) {...}

The SaveState() method is called when your application is being deactivated and can return some state to be persisted. The RestoreState(…) method is called when your application is being activated and returns the state so that you can restore the property values, and hence the UI.


Also, to enable the Chrysalis support we need to add a couple of lines to the App.xaml. Firstly an ‘xmlns’ definition at the top of the file to reference the Chrysalis.Services namespace, and the <csv:ChrysalisService/> item in the ApplicationLifettimeObjects section. Appart from those changes (shown in the code below) ,the rest of the App.xaml file can be left as is.



   1: <Application 
   2:     ...
   3:     xmlns:csv="clr-namespace:Chrysalis.Services;assembly=Chrysalis">
   4:  
   5:     <Application.ApplicationLifetimeObjects>
   6:         ...
   7:         <csv:ChrysalisService/>
   8:     </Application.ApplicationLifetimeObjects>
   9:  
  10: </Application>

For our calculator sample we do this with a class ‘CalculatorViewModelState’. Note: This class must be declared with public scope. Since this will be serialized transparently by the phone for the duration of deactivation, we need it to be public otherwise we will receive a SecurityException when the phone tries to deserialize it.



   1: // *** Overriden Base Methods ***
   2:  
   3: protected override object SaveState()
   4: {
   5:     return new CalculatorViewModelState()
   6:     {
   7:         NumberX = numberX,
   8:         NumberY = numberY
   9:     };
  10: }
  11:  
  12: protected override void RestoreState(object state)
  13: {
  14:     CalculatorViewModelState vmState = (CalculatorViewModelState)state;
  15:  
  16:     if (vmState != null)
  17:     {
  18:         NumberX = vmState.NumberX;
  19:         NumberY = vmState.NumberY;
  20:     }
  21: }
  22:  
  23: // *** Sub-classes ***
  24:  
  25: public class CalculatorViewModelState
  26: {
  27:     public int NumberX { get; set; }
  28:     public int NumberY { get; set; }
  29: }

We simply set the properties and return the state within the SaveState() method, and restore the values in the RestoreState(…) method. If you now try to tombstone the application, the state will be persisted on exit, and on returning the UI will look and perform identically to how it did prior to deactivation.


Success!


Navigation and Storing State


You may notice that in the sample application we have a button that takes us to a second page of the application – CalculatorInformationPage.xaml. We should also be able to restore any application state if we navigate to this page, tombstone, return to the application, and navigate back to the calculator page.


No need to worry though, since the Chrysalis framework ensures that this occurs transparently without having to write any further code. This works by calling SaveState()/RestoreTo(..) when a user navigates away from and back to the page, and handling the persistence of the state as required.


Summary


As you will have seen, the Chrysalis framework allows Silverlight developers using the MVVM pattern to simplify the persistence of state during tombstoning on their applications. By simply overriding two methods, the ViewModel itself can specify what state to persist, and the framework handles all the plumbing required to store and retrieve this.


In my next posts I will be discussing how this all works under the covers and when you should be returning state vs when you should be using isolated storage. After that, I will be releasing and describing the next feature currently being added to Chrysalis related to choosers.


Update 17/10/2010: With the latest version RestoreState(…) should check for a null state & MainViewModel is refactored to CalculatorViewModel.

No comments: