Tombstoning and MVVM

We’re working pretty hard to get the Who Is That (Full Stack) Windows Phone application tombstone_istock ready. Today I took a look at tombstoning and realized that this was not going to be quite as easy as I thought… and worse, the problem was  MVVM.

The key problem is that the easy way to handle tombstoning is with Page State, which is not available to the ViewModel, but it is the ViewModel that knows what is to be stored.

I came up with a solution, but be warned, this is a first attempt and may not be the best way to handle this.

The spec says that if the user takes a specific set of actions I need to restore any fields that the user has already filled out.  That is, if the user

  • Fills in some fields
  • Switches to another application or calls a launcher or chooser
  • Uses the back button to return

…the fields already filled in should still be filled. 

On the other hand, if the user takes the first two steps, but comes back to the application from the start menu, then the fields should be blank (as that counts as a re-start rather than a return).

The good news is that you can tie your tombstoning to the OnNavigatedTo and OnNavigatedFrom events. The bad news is that only the View has these events, the ViewModel does not, and it is the ViewModel that has the information to preserve; that is, the state of its properties (which the View binds to).

As noted, if you were not using MVVM this would be simple; you place the value of the controls into the Page’s state and the rest is automagic: if you return the values are there, if you re-start the values are not there and you therefore know just what to do.

Unfortunately, that page state is not available to the ViewModel. If the VM is doing the storing it is doing it in Isolated Storage, and Isolated Storage does not differentiate between restore and restart, your values will persist in either case.  There is no easy way for the ViewModel to automatically know if it should restore the values or not. Someone has to tell it.

Then she picked out two somebodies, Sally and Me.

I chose to have the VM own responsibility for saving and restoring state from Isolated Storage, but have the View own responsibility for when to ask the VM to save and when to restore or reinitialize. Thus, in the VM I created three methods:

  • Tombstone (save state to isolated storage)
  • Restore (retrieve state from isolated storage)
  • Init (do not retrieve state from isolated storage)
 internal void Tombstone( )
 {
    WhoIsThatWinPhone.AppServices.IsoStorage.Add( 
          "Contact", _contact );
 }

 internal void Restore( )
 {
    _contact = System.IO.IsolatedStorage
               .IsolatedStorageSettings
               .ApplicationSettings[ "Contact" ] 
                as WhoIsThatWinPhone.Model.Person;
 }
internal void Init( )
{
       InitializeNewContact( );
   }

This is nice and clean and these methods can be called from the OnNavigatedFrom and OnNavigatedTo methods in the view.  But, how does the OnNavigatedTo method know whether to call Restore or Init?

My decision was to take advantage of the fact that Page.State is a reliable bell-weather. If content stored in the page’s state is still there, then we are returning; if it is removed, then we are restarting.  Thus, I add a flag to the page’s state just before telling the View Model to store the state. 

(In this code, _vm is a private member variable in the view that holds a reference to the view model). 

 protected override void OnNavigatedFrom( 
    System.Windows.Navigation.NavigationEventArgs e )
 {
    this.State[ "tombstone" ] = true;
    _vm.Tombstone( );
    base.OnNavigatedFrom( e );
 }

 

When I come to the page, I check whether that flag exists in the page state. If so, then I’m returning, otherwise I’m restarting:

protected override void OnNavigatedTo( 
   System.Windows.Navigation.NavigationEventArgs e )
{
   if ( this.State.ContainsKey( "tombstone" ) )
   {
      _vm.Restore( );
   }
   else
   {
      _vm.Init( );
   }

   base.OnNavigatedTo( e );
}

 

I’ll be interested in comments that indicate a better or cleaner way to do this, though this certainly works, and is pretty straightforward to understand. 

The only alternative that appeals is to pass the entire page state to the VM in each method call, and let the ViewModel play with the PageState.  But that seems like an ugly mixing of a view collection and the ViewModel.

Your thoughts?

Share

About Jesse Liberty

Jesse Liberty is a Master Consultant for Falafel Software, and has three decades of experience writing and delivering software projects. He is the author of 2 dozen books and multiple Pluralsight courses, and has been a Technical Evangelist for Telerik and for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS.
This entry was posted in Data, Full Stack, Mini-Tutorial, WindowsPhone and tagged . Bookmark the permalink.

17 Responses to Tombstoning and MVVM

  1. Pingback: InterKnowlogy Blogs » Blog Archive » Windows Phone 7 – Tombstoning

  2. Pieter says:

    Hello Jesse,

    I was also confronted with the “how-do-I-tombstone-my-viewmodel” issue with MVVM on Windows Phone. In the end I came up with a pattern based on reflection. I can annotate the properties in my view model that must be tombstoned. A framework routine called from within my view is then able to detect all the properties that must be tombstoned without having to know the concrete type of my view model. This allowed me to have decoupled views and viewmodels.

    I wrote an article about it:
    http://pieterderycke.wordpress.com/2011/08/25/my-mvvm-tombstone-pattern/

    Regards,
    Pieter

  3. Don Judd says:

    Simplistically tombstoning in WP7 is a matter of the application because all nested dialog pages will get tombstoned won’t they? So if you are on dialog page C of a series of nested dialogs (dialog page A calls dialog page B, then dialog page B calls dialog page C) all will get tombstoned should the user press the phone’s Windows button.
    To solve this I would think one should set the “tombstone” value using isolated storage in the App where during Application_Launching it is set to false, then during Application_Deactivated it is set to true, and then for before navigating to dialog page A it is set to it false.

    For each dialog page then store each required value in OnNavigatedFrom using the State[] method. Once back from tombstoning in OnNavigatedTo check *both* the State.Count and the “tombstone” value to reset the current dialog page using the State[] methods. That way, provided the user tombstones while on dialog page C, the user can progress back through dialog page B, then dialog page A.
    Please correct me if I am wrong, but I haven’t seen anyone else discuss the nesting of dialog pages issue.

    Also, in WP7 why should I even have to handle tombstoning? Couldn’t Microsoft have handled this better? The WP7 OS developers know the app state. If memory is low the OS could flush an App to storage and bring it back when needed. This is creating quite a bit of work for us developers, when I really wonder the need.

    Something I’ve found that may be useful. In WP7.0 use NavigationEventArgs args.Uri.IsAbsoluteUri to know if you are being tombstoned because during tombstoning then any OnNavigatedFrom (args.Content as x).Item will not exist.

  4. Pingback: A Simple Windows Phone 7 MVVM Tombstoning Example | Colin Eberhardt's Adventures in .NET

  5. pluto says:

    In my understanding, when app is back from tombstoning, it will get a new instance of the page and the viewmodel, right?
    when user got back from tombstoning and got a correct view (name it page1), and user continues to press the back key to go back to previous page(name it page2) of the app, and the page2 was gotten by navigation with parameters, Sometimes we implement the navigation in MVVM pattern by using a dictionary object defined as a global, so in that given scenario, the parameters can not be retrieved back, and the page2 will result as a wrong state. so does this mean that we should save the paremeters dictionary as well?
    (sorry for the long paragraph)
    Thanks!

  6. John Tasler says:

    Rather than having to do this all in the code-behind of the View, it seems like a good place to write some attached properties for a Page type that exposes three ICommand properties: TombstoneCommand, RestoreCommand, and InitCommand. Then, you could just bind these attached properties to your ViewModel from the XAML.

  7. I use a similar approach with Sterling. I provide the view model with an ITombstoneFriendly interface, and then cast the VM to the interface and call the methods when coming in/out. I blogged about it here:

    http://csharperimage.jeremylikness.com/2011/02/windows-phone-7-tombstoning-with-mvvm.html

    The advantage of Sterling is that the serialization handles just about anything so you get a generic persistence store.

  8. I’m not exactly sure how WP7Contrib does it but I do know they use a NavigationService abstraction that kind of has your Tombstone and Restore functions in the VM.

    I’ll also echo your point about the View knowing about the VM. In using MVVMLight for example, the Window’s datacontext is typically set to the VM in Xaml. Your view’s codebehind just needs this.DataContext to point to your VM. As far as the VM knowing about the View, that tends to be a matter of opinion. I prefer to just use properties and push data back and forth via known events like SelectionChanged but that doesn’t solve everything.

    Page State is interesting and it was something I was discussing earlier with a friend. Knowing when you’re tombstoning (which doesn’t call OnNavigatedFrom) and not was a challenge I wasn’t aware of how to solve, so thanks for pointing to the solution.

    I’m not quite to the tombstoning period of my app yet so I’m just throwing this out, but would there be a way to bind to a page state property? In the View’s OnLoad or whatever, have that evaluate page state and push an IsTombstoned boolean to the App class? I believe the NavigationService triggers events here first then starts crawling the View. My only beef is that property would only be used in a specific event path that may never be hit in a perfect world but I think we need something a little more uniform than what we’re given. Page state just seems like the last place I need to look for this and I wonder how much MVVM was really used. I know in some projects I’ve inherited if it isn’t almost a mantra you’re left with certain holes where you’re digging at the UI for things pure MVVM shouldn’t touch. I’m just glad this one isn’t back breaking but because of where page state is, I don’t believe we can ever test this without getting the UI involved. I wouldn’t want to fire up the emulator just for it to pass.

  9. Ade says:

    Using the MVVM Light toolkit, you could send a NotificationMessage in the View in the OnNavigatedTo and OnNavigatedFrom methods? and pick up the message in the ViewModel?

    http://blog.galasoft.ch/archive/2009/09/27/mvvm-light-toolkit-messenger-v2-beta.aspx

    • Why do you need that abstraction? You are pretty confident that the view has a view model, right? So why go through the additional layer of indirection of raising a message when you can just call it directly? What is the perceived advantage? I know it “decouples” the view/viewmodel, but so what … my point is that the view DOES know about the view model. Even if not the type, the data-binding commands are a contract that say “I have something bound to me that has Name, PhoneNumber, etc.” as properties. So why not know it has something that has “Tombstone” and “Restore” methods? You aren’t violating the VM – it still won’t know about the view.

      • Ade says:

        Its a good point you make and its definitely easier to do it that way. Depends how much of a purist one is about the whole MVVM architecture I guess…

  10. Bil Simser says:

    I just stumbled over the Silverlight Binary Serialization (http://whydoidoit.com/2010/04/08/silverlight-serialization/) and while it doesn’t solve your particular problem (and it somewhat deviates from the MVVM pattern a bit) it looks like a slick way to easily tombstone everything. Maybe take a look at that?

  11. Erno says:

    Interesting! Two thoughts/observations/questions:
    1. What if a textbox has some input but did not lose focus? In other words: the data is not yet in the VM but should be saved…
    2. The View has a dependency on the VM to be able to call Tombstone, Init and Restore. Isn’t that breaking an MVVM ‘rule’?

    • I don’t think it breaks MVVM for the view to know about the VM, just the other way around. And even there, there are times when it is necessary for the VM to send a message to the View to get things done.

      The real question is whether the dependency causes a problem of any kind. I’m not sure I see where there would be a problem here.

    • toolsche says:

      @1. Isn’t the textbox losing focus as soon as you leave the page? Otherwise you could save the content on every textchange.
      @2. In MVVM the View always knows the VM. It would break the rule if the VM had a dependency on the View.

    • Colin E. says:

      Erno, your first point is not really specific to tombstoning, you can find generic solutions to this problem, where the view model property is updated as the user types text:

      http://zoltanarvai.com/2009/07/22/binding-update-on-textbox-textchanged-event-using-behaviors/

      Regarding your second point, the visibility in the MVVM pattern is as follows:

      Model <= ViewModel <= View

      i.e. the ViewModel is visible to the View, but not vice-versa.

      Colin E.

      • Steven says:

        @1. Because of this constraint, does it mean that data validation requires either (1) two VM properties per V field : one for character-level input and one for field-level input or (2)
        must handle lost focus as a validation command thereby leaving the binding capability only for character-level transfer and validation?
        Of course, character-level validation (as opposed to field level one) has its own merits and the consequence would be to push that kind of validation also in the VM itself. That would leave us now with possibly two validation schemes per V field in the VM.
        Thanks for your kind advice, Steven

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>