下面我来介绍一下MVVM模式的墓碑化处理方式
首先介绍一个库在codeplex上面 叫做 MVVM Tombstone for Windows Phone
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
|
|
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这个方法就是设置墓碑化标记.
不知道现在还有没有处理墓碑化更好的方法呢?
如果程序不对墓碑进行处理的话,我觉得总是不完美,如果哪位朋友有处理墓碑化更好的方式,请您 一定指教.期待您的回复.