Windows Phone 7 Tombstoning with MVVM and Sterling

Source: Internet
Author: User

Sterling makes tombstoning very easy because it handles serialization of just about any type of object. to show an example, we'll start with the concept of a view model that holds a set of categories (that a packet is bound) and a set of items that are filtered by category. when tombstoned, the application must remember the category as well as any item that is selected.

Build the Tombstone

First thing to do is create the generic tombstone model to hold values. This is the model:

public class TombstoneModel{    public TombstoneModel()    {        State = new Dictionary<string, object>();    }    public Dictionary<string, object> State { get; set; }    public T TryGet<T>(string key, T defaultValue)    {        if (State.ContainsKey(key))        {            return (T)State[key];        }        return defaultValue;    }}

But wait... what's the point?Isn't that just like the application settings object?Sure... sort. there are a few problems with application settings. first, they don't handle complex object graphs and the objects must be serializable. they don't recognize things like "foreign keys" or sub lists. second, they load into memory as opposed to isolated storage files which can be loaded on demand.

Sterling, on the other hand, can handle almost any type of object (and for the ones it can't handle, you can write acustom serializer ).

Define the Tombstone Table

A full lesson on Sterling is outside the scope of this post. to learn about Sterling, read the User's Guide. to define a table in Sterling, you pass the type of the table and a key. because there will only ever be one instance of the tombstone model, you cheat the key and make it always have a "true" value like this:

CreateTableDefinition<TombstoneModel,bool>(c=>true)

Finally, because the tombstone model is only to hold values that must be saved during a tombstone event, the model shoshould be cleared when the application truly closes (as opposed to being deactivated due to a tombstone event ). therefore, inApplication_ClosingMethod you can delete the tombstone model, if it exists:

private void Application_Closing(object sender, ClosingEventArgs e){    Database.Delete(typeof (TombstoneModel), true);}

Now everything is set to handle the tombstone properties.

Making your View Model a Caretaker

What other way is there to describe a view model that is "tombstone friendly? "To make a view model friendly, decorate it withITombstoneFriendlyInterface that looks like this:

public interface ITombstoneFriendly{    void Deactivate();    void Activate();}

These will be implemented in a moment.

Hooking into the Page

The best way to manage tombstone events is to hook intoOnNavigatedToAndOnNavigatedFromOverrides in the page code-behind. in fact, because forward navigation always generates a new instance of the view, this is the perfect way to maintain state of the view during navigation even when tomstoning isn' t taking place.

Like before, we'll take advantage of some extension methods to make this easy. here's a little caveat: you may be familiar with some "gotchas" with tombstoning if you 've read Jeff Prosise's Real World Tombstoning series (here 'spart 2, part 3, and Part 4 ). oh, and did you know that Sterling automatically serializes WriteableBitmaps using the "fast" technique Jeff describes in those posts? You can easily add one to the tombstone collection and it will serialize for you!

The issue with the specified index happens due to the order of loading. if you restore values on the view model before the view is loaded, the view itself will reset any two-way bindings and you'll find a snapshot simply snap to the first item. this is no good! Therefore, the extension method for the activate waits until the page is loaded,ThenCallthe activate method on the view model.

Here are the extension methods:

public static void DeactivatePage(this PhoneApplicationPage phonePage, IViewModel viewModel){    if (viewModel is ITombstoneFriendly)    {        ((ITombstoneFriendly)viewModel).Deactivate();    }}public static void ActivatePage(this PhoneApplicationPage phonePage, IViewModel viewModel){    RoutedEventHandler loaded = null;    loaded = (o, e) =>                    {                                                     ((PhoneApplicationPage) o).Loaded -= loaded;                        if (viewModel is ITombstoneFriendly)                        {                            ((ITombstoneFriendly) viewModel).Activate();                        }                    };    phonePage.Loaded += loaded;}

The loaded event doesn' t take any conditional arguments, so it wocould be impossible to pass the view model to that event. therefore, an anonymous method is used instead. by using a local variable, it can be unhooked inside the event call so it is not fired again. once the page is loaded, the activate method is called on the view model. because the page has been loaded, binding a variable control item will work perfectly if the specifiedSelectedItemIs synchronized with a value on the view model.

Here's what the calllook like from the code-behind forMainPage. xaml, Hooking into the navigation events and passing the correct view model:

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e){    this.ActivatePage(GlobalManager.GetViewModel<IMainViewModel>());    base.OnNavigatedTo(e);}        protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e){    this.DeactivatePage(GlobalManager.GetViewModel<IMainViewModel>());    base.OnNavigatedFrom(e);}

Raising the Dead

The only thing left is to perform the actual deed. When the view model is flagged for death, it will hydrate out key values. In this case, it is the current category and current item:

public void Deactivate(){    var tombstone = new TombstoneModel();    if (CurrentCategory != null)    {        tombstone.State.Add(ExtractPropertyName(() => CurrentCategory), CurrentCategory.Id);    }    if (CurrentItem != null)    {        tombstone.State.Add(ExtractPropertyName(()=>CurrentItem), CurrentItem.Id);    }    App.Database.Save(tombstone);}

A new model is created because you never care about the old model-this is saving stateThe currentTombstone event, and the state from any other don't matter (they will be wiped when the application closes anyway ). the dictionary is loaded with the key values for the current catalog and the current item.

Now when the application is revived:

public void Activate(){    var saved = App.Database.Load<TombstoneModel>(true);    if (saved == null) return;    var categoryId = saved.TryGet(ExtractPropertyName(() => CurrentCategory), 0);    if (categoryId > 0)    {        CurrentCategory = (from c in Categories where c.Id == categoryId select c).FirstOrDefault();    }    var currentItemId = saved.TryGet(ExtractPropertyName(() => CurrentItem), 0);                if (currentItemId > 0)    {        CurrentItem = (from i in Items where i.Id == currentItemId select i).FirstOrDefault();    }}

In this case, the category list is always loaded in the constructor of the view model. that doesn't change regardless of tombstoning or not. the items is a filter by category, so when the category is updated, the items filter is also updated. when the old state is loaded, first the category is checked and if it exists, the current category is set to the corresponding category item. the same happens with the item.

That's it. when testing the application, going into a previous page always returns to the correct partition column, and the selection state of the list is always maintained. if the application is exited by backing out, the close event deletes the tombstone key and upon re-entering, the prior and list start on the first page with no selection as expected.

Tombstoning with MVVM can be dead simple with a few helper methods and Sterling to serialize the data.

By @ Jeremy Likness

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.