我覺的處理墓碑化是一個非常棘手的問題.一旦程式從墓碑化返回當前頁面,當前頁面的所有資料都為空白,會重新調用該頁面的建構函式,建立一個新的頁面,如果這個頁面上沒有使用後台代碼建立的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這個方法就是設定墓碑化標記.
不知道現在還有沒有處理墓碑化更好的方法呢?
如果程式不對墓碑進行處理的話,我覺得總是不完美,如果哪位朋友有處理墓碑化更好的方式,請您 一定指教.期待您的回複.