Colin Eberhardt's Technology Adventures

Tombstoning with PhoneGap for Windows Phone 7 (and KnockoutJS)

October 24th, 2011

A few weeks back I wrote a blog post about how the recent announcement of PhoneGap support for Windows Phone 7 (WP7) which makes it possible to develop HTML5-based applications. In my previous blog post I showed the development of a simple HTML5 / JavaScript application which PhoneGap wraps up within a Silverlight application ‘shell’ allowing it to be deployed to your phone and potentially submitted to the Marketplace.

However, in order to pass the various Marketplace requirements and gain certification, your application must correctly handle the application lifecycle. With the recent Mango release, the lifecycle has become a little more complicated (although better! in that it adds multi-tasking / fast-app switching). I have also covered the lifecycle in a previous blog post and demonstrated how you can handle the various lifecycle events within an MVVM application.

The most tricky part of the application lifecycle that as a developer you need to handle is the tombstoned state, where your application is terminated (i.e. stopped and removed from memory). It is your responsibility to save enough state in order that when your tombstoned application is restarted, it looks to the user as if your application never stopped running, i.e. you restore your application UI to its original state.

The Mango application lifecycle is illustrated below:

The PhoneGap events API includes pause and resume events, which can be used to detect when the application transitions to and from the dormant state, however, for WP7 these events do not give us enough information. When resuming, we need to know whether it has resumed from a dormant or a tombstoned state. Considering that the tombstoned state is peculiar to WP7 (Android, and iPhone simply have a suspend / resume model), I don’t think it makes sense for the PhoneGap APIs to change in order to accommodate this. In this blog post I will show how the WP7 PhoneGap application host can be modified in order to support tombstoning.

But before we get there, I want to digress a while and look at using the MVVM pattern with JavaScript …

Using the MVVM pattern in JavaScript

Handling tombstoning is much easier if you have a good separation between your view and your logic, with the MVVM pattern being a sensible choice for achieving this. When your application is tombstoned (and your application terminated), then re-activated, it is your responsibility to recreate the original state. Your view-model, is a model-of-a-view, so technically should provide all the information required to fulfil this requirement. See my previous blog post for details.

There are numerous JavaScript UI frameworks available (MVC, MVP, MVVM), however, because I feel tombstoning lends itself particularly well to the MVVM pattern, I decided to give KnockoutJS a try. When reading about this framework you will find references to WPF and Silverlight, it is clear that it has been heavily inspired by the Microsoft XAML frameworks.

The application I have built to demonstrate tombstoning is a very simple, single page twitter search application.

The Knockout view model is a JavaScript object where the properties are defined as ‘observables’. These are JavaScript functions which provide change notification, much like CLR properties with INotifyPropergtyChanged within Silverlight / WPF.

The View-Model

The view model for my twitter search application is shown below:

/// <summary>
/// A view model for searching twitter for a given term
/// </summary>
function TwitterSearchViewModel() {
 
  var that = this;
 
  // --- properties
 
  this.isSearching = ko.observable(false);
 
  this.searchTerm = ko.observable("#wp7dev");
 
  this.tweets = ko.observableArray();
 
  // --- functions
 
  // search twitter for the given string
  this.search = function () {
    if (that.searchTerm() != "") {
 
      that.isSearching(true);
      var url = "http://search.twitter.com/search.json?q=" +
            encodeURIComponent(that.searchTerm());
 
      $.ajax({
        dataType: "jsonp",
        url: url,
        success: function (response) {
          // clear the results
          that.tweets.removeAll();
          // add the new items
          $.each(response.results, function () {
            var tweet = new TweetViewModel(this);
            that.tweets.push(tweet);
          });
 
          that.isSearching(false);
        }
      });
    }
  }
}

It comprises a few simple properties and a search function. Note, the items property is an observableArray, this is analogous to the WPF / Silverlight ObservableCollection, which raises events when its contents are modified, allowing the UI to update automatically. The search function queries the twitter APIs to find matching tweets, updating the observable items array with the results.

The TwitterSearchViewModel items collection is populated with TweetViewModel instances:

/// <summary>
/// A view model that represents a single tweet
/// </summary>
function TweetViewModel(tweet) {
 
    // --- properties
 
    this.author = tweet.from_user,
    this.text = tweet.text,
    this.time = parseDate(tweet.created_at);
    this.thumbnail = tweet.profile_image_url;
 
    // --- functions
 
    // parses the tweet date to give a more readable format
    function parseDate(date) {
      var diff = (new Date() - new Date(date)) / 1000;
 
      if (diff < 60) {
        return diff.toFixed(0) + " seconds ago";
      }
 
      diff = diff / 60;
      if (diff < 60) {
        return diff.toFixed(0) + " minutes ago";
      }
 
      diff = diff / 60;
      if (diff < 10) {
        return diff.toFixed(0) + " hours ago";
      }
 
      diff = diff / 24;
      return diff.toFixed(0) + " days ago";
    }
};

Note, here the properties are not observables, again much like WPF / Silverlight you can bind to a property that does not notify of changes if this is not required.

Also, the Knockout documentation typically defines view-models as literal objects. I prefer to use constructor functions, allowing the creation of multiple instances of the same view model.

The View

With Knockout the view is defined in HTML, you can create it directly, or via a template. I have created the following templates:

<script type=text/x-jquery-tmpl" charset="utf-8" id="twitterSearchView">
  <form data-bind="submit: search">
      <input data-bind="value: searchTerm, valueUpdate: 'afterkeydown'" />
      <button type="submit" data-bind="enable: searchTerm().length > 0 &amp;&amp; isSearching() == false">Go</button>  
  </form>    
  <ul data-bind="template: {name: 'tweetView', foreach: tweets}"> </ul>
</script>
<script type="text/x-jquery-tmpl" charset="utf-8" id="tweetView">
  <li class="tweet">
    <div class="thumbnailColumn">
      <img data-bind="attr: { src: thumbnail }" class="thumbnail"/>
    </div>
    <div class="detailsColumn">
      <div class="author" data-bind="text: author"/> 
      <div class="text" data-bind="text: text"/> 
      <div class="time" data-bind="text: time"/> 
    </div>
  </li>
</script>

The data-bind attribute is used by Knockout to set up the various bindings, connecting your view model properties and observables to the UI.It also defines functions to invoke when DOM events are raised, in much the same way as commands do in WPF / Silverlight.

The application code

Instantiating the view-model and the view is as simple as the following:

$(document).ready(function () {
  document.addEventListener("deviceready", onDeviceReady, false);
});
 
// phonegap is initialised
function onDeviceReady() {
 
  // create the view model
  twitterSearchViewModel = new TwitterSearchViewModel();
 
  // create an instance of the view
  $("#twitterSearchView").tmpl("").appendTo("#app");
 
  // wire-up
  ko.applyBindings(twitterSearchViewModel)
}

Originally I wanted to have the HTML for each view within a separate file, loading them via jQuery as described in this blog post. However, I just couldn’t get this to work within the embedded WP7 browser.

The tweetView tempate is used via the template / foreach Knockout binding, mimicking the Silverlight / WPF ItemsControl.ItemTemplate concept.

The application, after a bit of styling, looks like this:

Tombstoning

Now that the application has a decent structure, we can tackle the application-lifecycle. We need a way to store the view model state when the application transitions into a dormant state. Fortunately Knockout makes this very easy by supplying a toJSON function, which can create a JSON representation of your view-model graph (minus the observables). I have added a getState function to the TwitterSearchViewModel as follows:

// gets the view model state as a JSON string
this.getState = function () {
  return ko.toJSON(this);
}

Now we need a way to invoke this function when the application pauses. PhoneGap provides a pause lifecycle event, however, we need to store the output of this function in the WP7 PhoneApplicationService.Current.State dictionary. Because this is a very much WP7 specific feature, I decided to do this outside of the PhoneGap lifecycle events.

My handler for the Deactivated event simply invokes the above method, storing the state in the application state dictionary:

private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
  var viewModelState = PhoneGapView.Browser.InvokeScript("getState") as string;
  PhoneApplicationService.Current.State[ModelKey] = viewModelState;
}

Note, to do this I have had to modify the current PhoneGap WP7 library to provide access to the underlying WebBrowser control, I have raised an issue requesting this change to the PhoenGap library.

The application now stores it state when it becomes dormant, the next step is to use this state when an application is activated from a tombstoned state. Within the Activated handler we can read this state as follows:

/// <summary>
/// Gets the state that has been retrieved from isolated storage
/// in order to re-activated after tombstoning.
/// </summary>
public string TombstoneState { get; private set; }
 
private void Application_Activated(object sender, ActivatedEventArgs e)
{
  if (!e.IsApplicationInstancePreserved)
  {
    if (PhoneApplicationService.Current.State.ContainsKey(ModelKey))
    {
      var viewModelState = PhoneApplicationService.Current.State[ModelKey] as string;
      TombstoneState = viewModelState;
    }
  }
}

Note, we check IsApplicationInstancePreserved, if this is true, we do not need to use the state that was saved during deactivation, this allows for fast-application switching.

Unfortunately as this point our UI has not been created and our JavaScript application code is not running, which is why the tombstoned state is simply stored in a public property of our application. To pick this state up, we add a new step to our JavaScript view model creation code:

// phonegap is initialised
function onDeviceReady() {
 
  // create the view model
  twitterSearchViewModel = new TwitterSearchViewModel();
 
  // ---> check for tombstoned state
  window.external.Notify("getState");
 
  // instantiate the view an bind
  $("#twitterSearchView").tmpl("").appendTo("#app");
  ko.applyBindings(twitterSearchViewModel)
}

When the PhoneGap view is created, we add a handler to the ScriptNotify event, allowing us to handle this getState notification:

// Constructor
public MainPage()
{
  InitializeComponent();
 
  phoneGapView.Browser.ScriptNotify += Browser_ScriptNotify;
}
 
 
private void Browser_ScriptNotify(object sender, NotifyEventArgs e)
{
  string commandStr = e.Value;
 
  if (commandStr == "getState" && App.Current.TombstoneState != null)
  {
    phoneGapView.Browser.InvokeScript("setState", new string[] { App.Current.TombstoneState });
  }
}

This checks for the presence of tombstone state, and if found, invokes setState back on our JavaScript view model:

// sets the view model state based on the given JSON string.
this.setState = function (stateString) {
  var state = $.parseJSON(stateString),
      that = this;
 
  this.isSearching(state.isSearching);
  this.searchTerm(state.searchTerm);
 
  this.tweets.removeAll();
  $.each(state.tweets, function () {
    that.tweets.push(this);
  });
}

Note, re-creating our view-model form JSON data is a little more involved than the opposite. I have also cheated a little here, rather than re-creating each TweetViewModel I am using the JSON representation, because this view model has no public functions (i.e. commands).

Conclusions

With the above code the PhoneGap application now successfully handles all of the WP7 lifecycle states and transitions. There are a couple of things to note if you try to run this code yourself:

  1. You can force tombstoning via the Debug properties, “Tombstone upon deactiviation while debugging”.
  2. I have fund that the WebBrowser on the emulator does not tombstone correctly, when your application resumes the WebBrowser control fails to execute any JavaScript! Fortunately on a real device it works just fine.

Now that I can tombstone a PhoneGap application, I feel that it is one step closer to be a viable solution for application development. The final thing that I still haven’t quite worked out yet is navigation and back-button support. Fortunately Knockout has a lot to offer in this area as well, but more on that later …

You can download the full sourcecode (including PhoneGap library mods) here: PhoneGapExample.zip

Regards, Colin E.

A Windows Phone 7.1 (Mango) MVVM Tombstoning Example

October 3rd, 2011

In this blog post I look at the new application lifecycle model that Windows Phone 7.1 (Mango) introduces, and show how to handle the various lifecycle events in a simple MVVM application.

In a previous blog post I described the development of a simple Windows Phone 7 application using the Model-View-ViewModel (MVVM) pattern and how to handle application lifecycle events, specifically tombstoning. In this blog post I will look at updating this sample for Windows Phone 7.1 (Mango) to handle fast-app switching and the new Dormant state.

As an aside, I have bumped into a number of developers who were under the impression that Mango removes the need to tombstone. I’m afraid that this is not the case!

Introduction and a Recap

The WP7 application lifecycle model is designed to allow the user to move fluidly between applications whilst minimising the memory that these applications consume. Neither WP7 or WP7.1 (Mango) allow true multi-tasking, where more than one application is running at the same time. Instead, they employ a number of techniques to give the illusion of multi-tasking. As a developer you are exposed to this via the application lifecycle.

The WP7 (pre-Mango) application lifecycle, with the events that are fired for each transition, is illustrated below:

You can see the tasks that you must perform under each state transition.

When the user pressed the Start button, your application stops running and becomes tombstoned. It is your responsibility to save your application state such that when the user pressed the back-button (navigating the back-stack) to return to your application, it is in the same state as when they left it. This involves saving both the state of your application logic, within the application PhoneApplicationService.State dictionary and the UI state (e.g list scroll position) within the PhoneApplicationPage.State dictionary for each page. I described how you can achieve this in my previous blog post. I also want to point out that the rather tricky task of saving UI state can be made much simpler by using things like Matt Lacey’s popular Tombstoning Helper utilities.

One other thing that the Execution Model Overview on MSDN does not make terribly clear is that the back-stack has a limited size. What this means is that your tombstoned application state will not necessarily be re-activated. You can test this by running your application with the debugger attached, if you create a long enough back-stack (by repeatedly hitting the Start button and starting a new application), your debugging session will eventually terminate. This occurs when your application state has been pushed off the back-stack.

The upshot of this is that when your application is tombstoned you must save your state to isolated storage as well as the State dictionary (because your application might never get closed). Although, as it is recommended that your application tombstones swiftly, you might want to store application state to isolated storage at more regular intervals.

Windows Phone 7.1 Lifecycle

Windows Phone 7.1 (Mango) introduces a much more convincing illusion of multi-tasking, where the user can press and hold the back-button to rapidly switch between a small number of apparently concurrently running applications. However, again this is not true multitasking.

To achieve this, WP7.1 introduces a new state in the application lifecycle, the Dormant state:

When you application is in a Dormant state it is still in memory, however it is not executing. This allows the phone to re-start your application much more rapidly than when it is in a tombstoned state.

The phone can only store a limited number of dormant applications, as a result it might choose to move your application from a dormant to a tombstoned state. You do not receive any notification that this has occurred. However, the tombstoning and re-activiation process is the same as that for WP7.0, where you re-activate your application from the data saved in the State dictionary.

Again, there is always the possibility that your tombstoned data will be pushed out of the back-stack and lost.

Notice that there have not been any new events added to indicate that your application has returned from a Dormant state, your application will always receive Activated event when it is restarted regardless of whether it was dormant or tombstoned. This is good news, because it is backwards compatible with WP7.0.

Handling the Dormant State

So how do you handle this new state in your application? In order to maintain backwards compatibility you don’t strictly have to, however it is actually very simple to do.

The event arguments passed to the Activated event now have a new IsApplicationInstancePreserved argument which is true if your application has been re-started from a dormant state and false if it has been restarted from a tombstoned state. If this argument returns true, you simply do nothing! This ensures that you do not perform any redundant actions and helps your application restart faster.

The code below is all of the lifecycle code (apart from per-page UI state) that my MVVM example application contains. You can see that the code for a re-activated dormant state does nothing.

// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
  Debug.WriteLine("Launching");
 
  LoadViewModelFromIsolatedStorage();
 
  // if the view model is not loaded, create a new one
  if (ViewModel == null)
  {
    ViewModel = new FeedViewModel();
    ViewModel.Update();
  }
 
  // set the frame DataContext
  RootFrame.DataContext = ViewModel;
}
 
// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
  if (e.IsApplicationInstancePreserved)
  {
    Debug.WriteLine("Activated From Dormant State");
  }
  else
  {
    Debug.WriteLine("Activated From Tombstoned State");
 
    LoadViewModelFromAppState();
 
    RootFrame.DataContext = ViewModel;
  }      
}
 
// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
  Debug.WriteLine("Deactivated");
  SaveViewModelToAppState();
  SaveViewModelToIsolatedStorage();
}
 
// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
  Debug.WriteLine("Closing");
  SaveViewModelToIsolatedStorage();
}
 
private void SaveViewModelToAppState()
{
  PhoneApplicationService.Current.State[ModelKey] = ViewModel;
}
 
private void LoadViewModelFromAppState()
{
  if (PhoneApplicationService.Current.State.ContainsKey(ModelKey))
  {
    ViewModel = PhoneApplicationService.Current.State[ModelKey] as FeedViewModel;
  }
}
 
private void LoadViewModelFromIsolatedStorage()
{
  // load the view model from isolated storage
  using (var store = IsolatedStorageFile.GetUserStoreForApplication())
  using (var stream = new IsolatedStorageFileStream("data.txt", FileMode.OpenOrCreate, FileAccess.Read, store))
  using (var reader = new StreamReader(stream))
  {
    if (!reader.EndOfStream)
    {
      var serializer = new XmlSerializer(typeof(FeedViewModel));
      ViewModel = (FeedViewModel)serializer.Deserialize(reader);
    }
  }
}
 
private void SaveViewModelToIsolatedStorage()
{
  // persist the data using isolated storage
  using (var store = IsolatedStorageFile.GetUserStoreForApplication())
  using (var stream = new IsolatedStorageFileStream("data.txt",
                                                    FileMode.Create,
                                                    FileAccess.Write,
                                                    store))
  {
    var serializer = new XmlSerializer(typeof(FeedViewModel));
    serializer.Serialize(stream, ViewModel);
  }
}

Debugging Dormant and Tombstoned state

Because the application now has a couple of states that it can be in when it is not running, you need to employ a few tricks to be able to force the application into these states while developing. Typically the emulator will always place your application into a dormant state when you hit the Start button.
The fast-app switch UI is not enabled on the emulator by default, however you can enable it with a bit of hackery as described on this forum thread.

So, if your application always moves into a dormant state, how do you test your tombstoning code? Fortunately there is a build setting which you can enable which will ensure that your application is always tombstoned rather than placed in a dormant state:

You should test your application with this setting enabled and disabled to ensure that it accommodates tombstoning and dormant state.

I hope this blog post has helps understand the slightly complex application lifecycle that Mango introduces. It is clear that this delivers the best performance to the user, however it is a little complex for the developer!

You can download my updated MVVM example here: WindowsPhoneMVVMExample.zip

Regards, Colin E.

A Simple Windows Phone 7 MVVM Tombstoning Example

May 23rd, 2011

This blog post shows how to implement tombstoning within a Windows Phone 7 application that following the Model-View-ViewModel pattern.

UPDATE: I have published a update to this blog post to handle the new dormant state in Windows Phone 7.1 (Mango).


Colin Eberhardt is a Scott Logic technical Evangelist and a Technical Architect for Visiblox, suppliers of high-performance WPF and Silverlight charts.

I have to admit Windows Phone 7 tombstoning had me in a bit of a muddle for a while, so many places to store state, a confusing lifecycle and navigation model. Most of the blog posts I read either detailed tombstoning for non-MVVM applications, or described how to use or adapt an existing MVVM framework for the purposes of tombstoning. I only really understood the ins-and-outs of tombstoning after writing my own simple MVVM application. I thought I would share this application here in this blog in the hope that it might help other similarly confused developers!

What is tombstoning?

Mobile phones have limited resources compared to desktop computers, for that reason most smartphones OSs limit the number of applications that are currently loaded into memory and executing. For Windows Phone 7 this limit is one!

If the user hits the phone start button while you application is running, the screen lock engages, or you invoke a chooser / launcher from your application, then your application is terminated. However, when the user navigates back to your app, the screen unlocks or the chooser / launcher closed, the user expects to see your application again in its original state.

To support this, the WP7 OS maintains state information which allows you to ‘restore’ your application by starting a new application instance and using this state information to start the application in the same state as the one which was terminated. For a full overview of this process I would recommend reading the Execution Model Overview for Windows Phone on MSDN, or the three part series on the Windows Phone Developer Blog (1), (2), (3).

This probably sounds like a lot of work, and to be honest, it is. You might be wondering if the new multi-tasking capabilities that the Mango update will bring (demoed at the MIX11 day 2 keynote and to be released probably late 2012) will mean the that tombstoning will disappear. I have not seen any official confirmation one way or the other, however, personally I think you will still need to tombstone in Mango. It is most likely that the number of concurrent applications will be limited and applications will still need to tombstone as a result.

The Example Application

The example application I am using for this blog post is illustrated below. The application displays a list of tweets, clicking on a tweet displays it full screen. Twitter applications are the new Hello World!

The ViewModel

The view model is pretty trivial, each tweet is represented by a FeedItemViewModel:

public class FeedItemViewModel
{
  public FeedItemViewModel()
  {
  }
 
  public long Id { get; set; }
 
  public string Title { get; set; }
 
  public string Author { get; set; }
 
  public DateTime Timestamp { get; set; }
}

The above view model does not change state, so there is no need to implement INotifyPropertyChanged.

The top-level view model simply exposes a collection of the above feed items. It also has an Update method which populates this list by querying Twitter’s search API for tweets that contain the #wp7 hashtag:

public class FeedViewModel 
{
  // Twitter search for #WP7
  private readonly string _twitterUrl = "http://search.twitter.com/search.atom?rpp=100&&q=%23wp7";
 
  private WebClient _webClient = new WebClient();
 
  private readonly ObservableCollection<FeedItemViewModel> _feedItems =
                                             new ObservableCollection<FeedItemViewModel>();
 
  public FeedViewModel()
  {
    _webClient.DownloadStringCompleted += WebClient_DownloadStringCompleted;
  }
 
  /// <summary>
  /// Parses the response from our twitter request, creating a list of FeedItemViewModelinstances
  /// </summary>
  private void WebClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
  {
    var doc = XDocument.Parse(e.Result);
    var items = doc.Descendants(AtomConst.Entry)
             .Select(entryElement => new FeedItemViewModel()
             {
                Title = entryElement.Descendants(AtomConst.Title).Single().Value,
                Id = long.Parse(entryElement.Descendants(AtomConst.ID).Single().Value.Split(':')[2]),
               Timestamp = DateTime.Parse(entryElement.Descendants(AtomConst.Published).Single().Value),
               Author = entryElement.Descendants(AtomConst.Name).Single().Value
             });
 
    _feedItems.Clear();
    foreach (var item in items)
    {
      _feedItems.Add(item);
    }
  }
 
  /// <summary>
  /// Gets the feed items
  /// </summary>
  public ObservableCollection<FeedItemViewModel> FeedItems
  {
    get { return _feedItems; }
  }
 
  /// <summary>
  /// Gets a feed item by its Id
  /// </summary>
  public FeedItemViewModel GetFeedItem(long id)
  {
    return _feedItems.SingleOrDefault(item => item.Id == id);
  }
 
  /// <summary>
  /// Update the feed items
  /// </summary>
  public void Update()
  {
    _webClient.DownloadStringAsync(new Uri(_twitterUrl));
  }
}

The View

The FeedView page that is used to render the FeedViewModel (i.e the list of tweets) is simply a NavigationList control (An ItemsControl optimised for WP7 navigation scenarious) that has an ItemTemplate which renders each item:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
  <l:NavigationList x:Name="navigationControl"
                    ItemsSource="{Binding FeedItems}"
                    Navigation="NavigationList_Navigation">
    <l:NavigationList.ItemTemplate>
      <DataTemplate>
        <StackPanel Orientation="Vertical"
                    Height="100">
          <TextBlock Text="{Binding Author}"
                      Style="{StaticResource PhoneTextNormalStyle}"/>
          <TextBlock Text="{Binding Title}"
                      Margin="20,0,0,0"
                      Style="{StaticResource PhoneTextSmallStyle}"
                      TextWrapping="Wrap"/>
        </StackPanel>
      </DataTemplate>
    </l:NavigationList.ItemTemplate>
  </l:NavigationList>
</Grid>

The FeedItemView that is used to render the FeedItemViewModel is even simpler:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
  <StackPanel Orientation="Vertical">
    <TextBlock Text="{Binding Author}"
                Style="{StaticResource PhoneTextLargeStyle}"
                Foreground="{StaticResource PhoneAccentBrush}"/>
    <TextBlock Text="{Binding Title}"
                Style="{StaticResource PhoneTextLargeStyle}"
                TextWrapping="Wrap"/>
  </StackPanel>
</Grid>

Now that we have the ViewModels and their respective Views we need to bring them together by making the ViewModel the DataContext for each View. There are a number of different ways you can do this (Paul Stovell documents 8 different ways in his MVVM Instantiation Approaches blogpost!), however, I find the simplest approach with WP7 applications is to associated the ViewModel with the application. Therefore, we add our view model as a property of the App class and instantiate it when the Application_Launching method (which handles the Launching lifecycle event) is invoked:

public partial class App : Application
{
 
  /// <summary>
  /// Gets the ViewModel
  /// </summary>
  public FeedViewModel ViewModel { get; private set; }
 
  ...
 
  private void Application_Launching(object sender, LaunchingEventArgs e)
  {
    ViewModel = new FeedViewModel();
    ViewModel.Update();
 
    // set the frame DataContext
    RootFrame.DataContext = ViewModel;
  }
 
  ...
}

The DataContext of the RootFrame is set to our ‘top level’ view model. The RootFrame is an instance of PhoneApplicationFrame, which contains the current PhoneApplicationPage instance, hence when you navigate from one page to the next the content of the PhoneApplicationFrame is replaced within the page that has been navigated to. As a result, each of your pages will inherit the DataContext from the application frame.

Navigation

The DataContext of the RootFrame is inherited by the FeedView page, so once the tweets are loaded they will be rendered in our NavigationList. In order to navigate to a tweet when a user clicks on it we need to handle the Navigation event:

public partial class FeedView : PhoneApplicationPage
{
  ...
 
  private void NavigationList_Navigation(object sender, NavigationEventArgs e)
  {
    var selectedItem = e.Item as FeedItemViewModel;
 
    // navigate to the feed items page
    NavigationService.Navigate(new Uri("/FeedItemView.xaml?id=" + selectedItem.Id, UriKind.Relative));
  }
 
}

The above code uses the NavigationService to navigate to the FeedItemView page. We use the querystring to pass the id of the selected tweet. We could have passed this information from View the ViewModel by adding a SelectedItemId property, however, we will find out later that there are some advantages to using the querystring.

When the FeedItemView page is loaded we need to obtain this id from the querystring and use it to locate the correct FeedItemViewModel instance. This is done as follows:

public partial class FeedItemView : PhoneApplicationPage
{
  public FeedItemView()
  {
    InitializeComponent();
  }
 
  protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
  {
    base.OnNavigatedTo(e);
 
    // parse the querystring to extract the id
    long feedItemId = long.Parse(NavigationContext.QueryString["id"]);
 
    // obtain this item from the view model.
    this.DataContext = App.Current.ViewModel.GetFeedItem(feedItemId);
  }
}

Tombstoning

With the above code our simple application works just fine, you can navigate the list of tweets, click on one to see it in the FeedItemView page, then use the back button to return to the original list of tweets. It would be great if our job was complete at this point, however, the application fails miserably if it is tombstoned. To test this, load the application, then hit the start button, followed by the back button to return to the application. This is what you are met with:

Tombstoning terminates the application, therefore the ViewModel and all the state it contains is lost. Hence we return to an empty list.

So how do we fix this?

When your application gets tombstoned a Deactivated event is fired before your application terminates, and when the user navigates back to your application, a new instance is created an the Activated event is fired. The Visual Studio WP7 application template helpfully adds event handlers for these events on your App class.

Note that when an application is re-activeated the Launching event is not fired, therefore the code we added to construct a new view model is not executed in this case.

The fix to this problem is rather simple, the framework provides a PhoneApplicationService class which has a State property which allows you to store your application state within a dictionary. The WP7 OS will persist this dictionary of state on your behalf when your application is tombstoned. You can place anything within this dictionary as long as it is serializable. Therefore a simple solution to this problem is to simply place our view model into this dictionary, retrieving it when the application is re-activated:

private readonly string ModelKey = "Key";
 
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
  PhoneApplicationService.Current.State[ModelKey] = ViewModel;
}
 
private void Application_Activated(object sender, ActivatedEventArgs e)
{
  if (PhoneApplicationService.Current.State.ContainsKey(ModelKey))
  {
    ViewModel = PhoneApplicationService.Current.State[ModelKey] as FeedViewModel;
    RootFrame.DataContext = ViewModel;
  }
}

With this simple change the application now returns to its previous state when the user hits the back button. Note that this also works when the application is tombstoned on the FeedItemView page:

This is because when the application is re-activated, the NavigationService Journal which records the pages that have been visited is also restored. The ‘back stack’ contains the URI of each page which in our case contains the querystring which holds the Id of the tweet which is currently being viewed.

Persisting State Between Sessions

In the above example, the application state was saved as tomstoned state. But what if the user simply hits the back button until they navigate back out of our application? In this context your application is closed, rather than tombstoned. The PhoneApplicationService.Current.State dictionary which we used to store our view model is not persisted in this context. For long-term persistence you should save your data to the phones isolated storage.

We can serialize our view model to isolated storage when the application exits by adding code to the Application_Closing methods which handles the Closing event. In the example below I am using the framework XmlSerializer, which is the same serializer which is used to persist data in the application state dictionary. However, as you have full control over serialization, you might choose to use a serializer with better performance.

// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
    // persist the data using isolated storage
    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
    using (var stream = new IsolatedStorageFileStream("data.txt",
                                                    FileMode.Create,
                                                    FileAccess.Write,
                                                    store))
    {
      var serializer = new XmlSerializer(typeof(FeedViewModel));
      serializer.Serialize(stream, ViewModel);
    }
}

The code for launching the application now needs to be modified to first try and load the data from isolated storage. If no data is found a new view model is created:

// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
  // load the view model from isolated storage
  using (var store = IsolatedStorageFile.GetUserStoreForApplication())
  using (var stream = new IsolatedStorageFileStream("data.txt", FileMode.OpenOrCreate, FileAccess.Read, store))
  using (var reader = new StreamReader(stream))
  {
    if (!reader.EndOfStream)
    {
      var serializer = new XmlSerializer(typeof(FeedViewModel));
      ViewModel = (FeedViewModel)serializer.Deserialize(reader);
    }
  }
 
  // if the view model is not loaded, create a new one
  if (ViewModel == null)
  {
    ViewModel = new FeedViewModel();
    ViewModel.Update();
  }
 
  // set the frame DataContext
  RootFrame.DataContext = ViewModel;
}

NOTE: This is no a fully functional twitter application, in a real application you would probably want to update the data that is loaded from isolated storage to add the latest tweets, however this blog post is about tomstoning, not twitter application development!

Tombstoning UI state

So, now our application saves state during tomstoning and to isolated storage when it exits. Surely we’re all done now?

Well … almost.

If you start the application and scroll down the list of tweets, then hit the start button, tomstoning the application, then hit the back button to re-activate it our tweets are all there, but our list is scrolled back to the top again:

Why is this? Well, when you simply navigate back to from one page within your application to another, the original page is still present in memory and as a result, any state held by the controls within the application UI is maintained.

However, as we have seen already, when an application is tomstoned it is terminated. The only state that remains is that which you manually place into the State dictionary. Therefore, in order to maintain the scroll location, which we should do, we need to determine its location and store it in the tombstone state.

A while back I blogged about exposing a ScrollViewers scroll location as an attached property in order to permit databinding. This approach could be used here to bind the scroll location to a property on the view model. However, in this case I think something a bit more lightweight is more appropriate.

In the previous sections we have used the application-level code>PhoneApplicationService.Current.State for storing tombstone state. You can also store state on a page-level via the PhoneApplicationPage.State property. This is a much more appropriate place for storing state relating to UI controls within pages, rather than state which is more closely related with your applications business logic.

It is still a little tricky to extract the required state information from list controls. The code below uses Linq-to-VisualTree to locate the VirtualizingStackPanel that can be used to get / set the scroll position. The scroll location is placed in the State dictionary when the user navigates away from the page.

/// <summary>
/// Gets the NavigationList ItemsPanel
/// </summary>
private VirtualizingStackPanel ItemsPanel
{
  get
  {
    return navigationControl.Descendants<VirtualizingStackPanel>()
                            .Cast<VirtualizingStackPanel>()
                            .SingleOrDefault();
  }
}
 
private static readonly string ScrollOffsetKey = "ScrollOffsetKey";
 
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
  base.OnNavigatedFrom(e);
 
  // persist the scroll position within the page state
  var scroll = ItemsPanel;
  if (scroll != null)
  {
    State[ScrollOffsetKey] = ItemsPanel.ScrollOwner.VerticalOffset;
  }
}

Restoring this state is simply a matter of checking for the existence of this key in the State dictionary when the page is navigated to:

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
  base.OnNavigatedTo(e);
 
  // restore the scroll position
  this.Loaded += (s, e2) =>
    {
        if (State.ContainsKey(ScrollOffsetKey))
        {
          var scroll = ItemsPanel;
          if (scroll != null)
          {
            ItemsPanel.SetVerticalOffset((double)State[ScrollOffsetKey]);
          }
        }
    };
}

Note that the state is restored after the Loaded event is fired because the VirtualizingStackPanel which manages the scroll location is defined within the NavigationList template and hence will not be present in the visual tree when the page is not initially constructed (the same is true if you use a ListBox as well).

So ... finally, we are done!

Conclusions

This blog post feels like it has turned into something of an epic! as I mentioned in the introduction tomstoning within WP7 applications is complex and it is easy to get confused with all the various places where state can be stored (It even seems to have Silverlight guru Jesse Liberty in a bit of a muddle!).

It is worth noting that this example tombstones the entire ViewModel state, and in more complex applications it might be more appropriate to tombstone an abstraction of the ViewModel, especially if it is not serializable. However, I think the simple example that this blog presents is still a useful starting point for understanding the tombstoning process.

You can download the sourcecode for this example application: WP7MVVMTombstone.zip

Regards, Colin E.