Over a million developers have joined DZone.
Gold Partner

My MVVM Tombstone Pattern

· Mobile Zone

The Mobile Zone is brought to you in partnership with Apptimize.  Discover the 5 little know reasons your app is underperforming and how to better understand your customers mobile needs.

I am developing an application for Windows Phone 7 and during tombstoning I had to store some data of the view model. Tombstoning is done in the code behind of the view in the “OnNavigatedFrom” and “OnNavigatedTo” methods. This forced me to tightly couple my view model to (the code behind of) my view. As I don’t want this tightly coupling, I came with the following pattern:

Properties in the view model that must be tomstoned are decorated with the “Tombstone” attribute.

[Tombstone]
public SomeSerializableType SomeProperty { get; set; }

All my view then has to contain is the following code.

public MyPage()
{
    // ...
    isNewPage = true;
}

Please note: we need the “isNewPage” variable to differentiate between restoring a tombstoned view or revisiting the view in the current application instance.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    if (isNewPage)
    {
        TombstoneHelper.RestoreState(this);

        isNewPage = false;
    }
}

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    base.OnNavigatedFrom(e);

    TombstoneHelper.SaveState(this);
}

TombstoneHelper uses reflection to find all properties that must be tombstoned. It can be used with any view model and it allows my view models and views to be loosely coupled. The only drawback is that it can only be used for properties with public getters and setters, because reflection on WP7 does not allow developers to access private properties or variables.

/// <summary>
/// Attribute to indicate the ViewModel Property should be tombstoned.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class TombstoneAttribute : Attribute
{
}
public static class TombstoneHelper
{
    public static void SaveState(PhoneApplicationPage page)
    {
        foreach (PropertyInfo tombstoneProperty in FindTombstoneProperties(page.DataContext))
        {
            page.State["ViewModel." + tombstoneProperty.Name] = tombstoneProperty.GetValue(page.DataContext, null);
        }
    }

    public static void RestoreState(PhoneApplicationPage page)
    {
        foreach (PropertyInfo tombstoneProperty in FindTombstoneProperties(page.DataContext))
        {
            string key = "ViewModel." + tombstoneProperty.Name;

            if (page.State.ContainsKey(key))
            {
                tombstoneProperty.SetValue(page.DataContext, page.State[key], null);
            }
        }
    }

    // ...

    private static IEnumerable<PropertyInfo> FindTombstoneProperties(object o)
    {
        IList<PropertyInfo> tombstoneProperties = (from p in o.GetType().GetProperties()
                                                        where p.GetCustomAttributes(typeof(TombstoneAttribute), false).Length > 0
                                                        select p).ToList();

        foreach (PropertyInfo tombstoneProperty in tombstoneProperties)
        {
            if (!tombstoneProperty.CanRead || !tombstoneProperty.CanWrite)
            {
                throw new TombstoneException("The getter and the setter of a property that needs to be tomstoned must be declared public.");
            }
        }

        return tombstoneProperties;
    }
}

I also use TomstoneUtility to help tombstoning my controls. I will write more about this in a future blog article.

Source:  http://pieterderycke.wordpress.com/2011/08/25/my-mvvm-tombstone-pattern/



The Mobile Zone is brought to you in partnership with Apptimize.  Learn more about app optimization methods that help teams identify user preferences, decide the next iterative changes to make, and validate them through testing.

Topics:

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}