windows phone mvvm 墓碑化处理.

我觉的处理墓碑化是一个非常棘手的问题.一旦程序从墓碑化返回当前页面,当前页面的所有数据都为空,会重新调用该页面的构造函数,创建一个新的页面,如果这个页面上没有使用后台代码创建的UI的话,还容易一点,如果有使用后台代码创建的UI,还要需要重新添加UI,然后恢这些UI的状态.我觉得这个过程处理起来会很痛苦,我的想法是把需要动态创建UI的部分做成一个UserControl,在UserControl里面处理创建UI的逻辑.首先在UserControl里面定义依赖属性,然后使用数据绑定技术绑定依赖属性,在ViewModel里面能过参数设置来控件UserControl来动态生成UI.如果顺利的话就可以使用下面这个库来处理墓碑化,另外我觉得顺利处理墓碑化的前提是程序良好的使用了MVVM模式.

下面我来介绍一下MVVM模式的墓碑化处理方式

首先介绍一个库在codeplex上面 叫做 MVVM Tombstone for Windows Phone

http://mvvmtombstone.codeplex.com/SourceControl/latest#WindowsPhone.MVVM.Tombstone/WindowsPhone.MVVM.Tombstone/TombstoneAttribute.cs

1 首先使用NuGet来安装 MVVM Tombstone for windows Phone

2 分别在OnNavigatedFrom和OnNavigatedTo里面添加如下代码

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    TombstoneHelper.page_OnNavigatedTo(this, e);
}
 
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    base.OnNavigatedFrom(e);
    TombstoneHelper.page_OnNavigatedFrom(this, e);
}

3.在App.cs的Application_Activated 方法中添加如下代码

1
TombstoneHelper.Application_Activated(sender, e);

4.在你需要保存的属性上面加上[Tombstone()]

[Tombstone()]
public SomeClass MyObject {
   get { return _myobject; }
    set {
      _myobject = value;
      RaisePropertyChanged("MyObject");
   }
}

OK.啦就是这么简单.想知道是怎么实现了,代码在CodePlex上面.我来介绍一下.代码主要是利用反射,在程序切到后台时,遍历当前ViewModel的带有[Tombstone()]标签的属性,将这些属性的值使用Json.Net进行序列化,然后把序列化的结果保存到PhoneApplicationPage.State字典中,当程序从后台返回到前台时,遍历当前ViewModel的带有[Tombstone()]标签的属性,如果这个属性的值为空,将保存在State字典的值读出来,赋给ViewModel对应的属性.

下面介绍一下源代码

1下面这个类就定义了一个属性标签,标签类似于我们使用的注释,只不过这种标签是给编译器看的,在对ViewModel里面的属性进行保存的时候,就是通过判断对带有这种标签的属性进行保存.

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    public class TombstoneAttribute : Attribute
    {

    }
 

2.下面这个类就是核心类啦.

 internal class ApplicationState
    {
        static JsonSerializerSettings settings = new JsonSerializerSettings
        {
            PreserveReferencesHandling = PreserveReferencesHandling.Objects
        };
        
        /// <summary>
        /// Runs over all the properties in the page's viewmodel and saves the state of those that have the tombstone attribute applied
        /// </summary>
        static internal void Save(PhoneApplicationPage page)
        {
            foreach (PropertyInfo tombstoneProperty in GetTombstoneProperties(page.DataContext))
            {
                string key = GetKey(tombstoneProperty);
                
                object value = tombstoneProperty.GetValue(page.DataContext, null);
                page.State[key] = JsonConvert.SerializeObject(value, Formatting.None, settings);
            }
        }

        /// <summary>
        /// Runs over all the properties in the page's viewmodel and restores the state of those that have the tombstone attribute applied and have no value
        /// </summary>
        static internal void Restore(PhoneApplicationPage page)
        {
            foreach (PropertyInfo tombstoneProperty in GetTombstoneProperties(page.DataContext))
            {
                if (tombstoneProperty.GetValue(page.DataContext, null) == null)
                {
                    string key = GetKey(tombstoneProperty);
                    if (page.State.ContainsKey(key))
                    {
                        tombstoneProperty.SetValue(page.DataContext, JsonConvert.DeserializeObject((string)page.State[key], tombstoneProperty.PropertyType, settings), null);
                    }
                }
            }
        }

        /// <summary>
        /// Defines the key we are saving in the page's state bag
        /// </summary>
        private static string GetKey(PropertyInfo Prop)
        {
            return "tshelper.viewmodel." + Prop.Name;
        }

        /// <summary>
        /// Obtains all the propertyinfos that have the TombstoneAttribute applied
       /// <param name="ViewModel">The viewmodel to check. This is set to the page's DataContext</param>
            private static IEnumerable<PropertyInfo> GetTombstoneProperties(object ViewModel)
	    {
		    if (ViewModel != null) {
			    IEnumerable<PropertyInfo> tsProps = from p in ViewModel.GetType().GetProperties() 
                                                where p.GetCustomAttributes(typeof(TombstoneAttribute), false).Length > 0
                                                select p;

			    foreach (PropertyInfo tsProp in tsProps) {
				    if (!tsProp.CanRead || !tsProp.CanWrite) {
    					throw new TombstoneException(string.Format("Cannot restore value of property {0}. Make sure the getter and setter are public", tsProp.Name));
				    }
			    }
			    return tsProps;
		    } else {
			    return new List<PropertyInfo>();
		    }
	    }
    }


GetTombstoneProperties返回带有[Tombstone()]标签的属性的集合.

Restore方法用来在墓碑时恢复数据,遍历带有Tombstone()标签的属性,使用反射从当前的ViewModel中取出属性的值,如果值为空,就从State字典中读出属性的值,反序列化后

把值设置到属性上.

Save方法用来保存数据,遍历带有Tombstone标签的属性,将属性的值序列化后,保存到State字典中.

3.下面这个类是就是我们所使用的接口

 public sealed class TombstoneHelper
    {

         private static bool _hasbeentombstoned = false;       
        public static void Application_Activated(object sender, ActivatedEventArgs e)
        {
            if (!e.IsApplicationInstancePreserved)
            {
                _hasbeentombstoned = true;
            }
        }

        private static List<PhoneApplicationPage> restoredpages = new List<PhoneApplicationPage>();
        public static void page_OnNavigatedTo(PhoneApplicationPage sender, NavigationEventArgs e)
        {
            if (sender != null && _hasbeentombstoned && !restoredpages.Contains(sender))
            {
                restoredpages.Add(sender);
                ApplicationState.Restore(sender);
            }                      
        }

        public static void page_OnNavigatedFrom(PhoneApplicationPage sender, NavigationEventArgs e)
        {
            if (!(e.NavigationMode == NavigationMode.Back))
            {
                if (sender != null)
                {
                    ApplicationState.Save(sender);
                }
            }
        }
    }

当离开当前页面时框架会调用OnNavigateedFrom方面,在离开当前页面时分为两种方式,一种方式是用户按下Back键,这时NavigationMode的值为Back.别一种方式是用户按下Start键,程序进入到后来.当程序切到后台进,是否进入墓碑化是不确定的,所以只要程序一进行到后就对数据进行保存.

if (!(e.NavigationMode == NavigationMode.Back))      
{
}


使用这个判断来排除掉,用户按下Back键的情况.剩下一种当然就是程序切换到后台了.

在page_OnNavigatedTo方法,判断_hasbeentombstoned是否为true,如果为true,并且已经恢复过的页面中不包含当前页面时,恢复数据.(总觉得他设置restoredpages这个List有点多余,干啥还要保存恢复过数据的页面呢)
Application_Activated这个方法就是设置墓碑化标记.

不知道现在还有没有处理墓碑化更好的方法呢?

如果程序不对墓碑进行处理的话,我觉得总是不完美,如果哪位朋友有处理墓碑化更好的方式,请您 一定指教.期待您的回复. 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值