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 Smartphone 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:
Collapse
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:
Collapse
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:
Collapse
<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:
Collapse
<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 blog post!), 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:
Collapse
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:
Collapse
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:
Collapse
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-activated 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:
Collapse
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 
query string 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 tombstoned 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.
Collapse
// 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:
Collapse
// 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 
tombstoning, not twitter application development!
Tombstoning UI state
So, now our application saves state during tombstoning 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, tombstoning 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 tombstoned 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 data binding. 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.
Collapse
/// <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:
Collapse
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).