Writing WP7 applications (and the new Metro UI Apps in Windows 8) you will have to deal with concepts like ‘Tombstoning’ or ‘Suspension’; if you follow the MVVM pattern, there’s a very good chance that the status of your application page is represented by the ViewModel itself. So a quick and dirty way to handle those suspended states is to ‘persist’ the ViewModel (or the part of it that have actual meaning) to the application state or to a file storage and retrieve it at a later time when the application is reactivated.
if you put something that is not a primitive type inside the Application State or the Page State dictionaries, it will be serialized using the standard DataContractSerializer (on the subject of serialization and deserialization in WP7 you can read these previous posts of mine:
WP7 Understanding Serialization and
http://www.primordialcode.com/blog/post/wp7-datacontractserializer-bug).
A good way to avoid headaches when using this approach to handle tombstoning/suspension is the to test not only the behavior of your ViewModels, but also if your ViewModels are serializable; you can do this by using an helper class similar to this one:
public static class DataContractSerializerHelpers |
public static string ToXml( object obj) |
public static string ToXml( object obj, IEnumerable<Type> knownTypes) |
Type objType = obj.GetType(); |
DataContractSerializer ser = new DataContractSerializer(objType, knownTypes); |
using (StringWriter sw = new StringWriter()) |
XmlWriterSettings settings = new XmlWriterSettings(); |
settings.OmitXmlDeclaration = true ; |
using (XmlWriter writer = XmlWriter.Create(sw, settings)) |
ser.WriteObject(writer, obj); |
public static object FromXml( string data, Type type) |
return FromXml(data, type, null ); |
public static object FromXml( string data, Type type, IEnumerable<Type> knownTypes) |
using (StringReader sr = new StringReader(data)) |
XmlReaderSettings settings = new XmlReaderSettings(); |
using (XmlReader reader = XmlReader.Create(sr, settings)) |
DataContractSerializer ser = new DataContractSerializer(type, knownTypes); |
return ser.ReadObject(reader); |
public static T FromXml<T>( string data) where T : class |
return FromXml(data, typeof (T), null ) as T; |
public static T FromXml<T>( string data, IEnumerable<Type> extraTypes) where T : class |
return FromXml(data, typeof (T), extraTypes) as T; |
It’s basically a simple wrapper around the DataContractSerializer function to call it in a convenient way that mimic what the infrastructure does.
Using the standard DataContractAttribute and DataMemberAttribute on the ViewModels you can fine tune what will be persisted (obviously you should limit this to the properties that have some meaning for your application state, you will also have to design the ViewModels to provide lazy initialization for everything that depends on those data). Your tests will then look similar to this one:
public class SerializableTypes : SilverlightTest |
public void ViewModel_Serialize() |
MainViewModel vm = new MainViewModel(); |
DataContractSerializerHelpers.ToXml(vm); |
public void ViewModel_DeSerialize() |
MainViewModel vm = new MainViewModel(); |
var data = DataContractSerializerHelpers.ToXml(vm); |
var loaded = DataContractSerializerHelpers.FromXml<MainViewModel>(data); |
Assert.IsNotNull(loaded); |
With this approach the code that you have to write on the views to handle tombstoning will become trivial; in your views you will have something similar to this:
private SingleFeedViewModel Vm |
get { return (SingleFeedViewModel)DataContext; } |
set { DataContext = value; } |
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) |
if (ApplicationHelper.IsApplicationInstancePreserved && Vm != null ) |
if (State.ContainsKey(StateKeys.SingleFeedVm)) |
SingleFeedViewModel itm = State[StateKeys.SingleFeedVm] as SingleFeedViewModel; |
Vm = new SingleFeedViewModel(); |
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e) |
if (State.ContainsKey(StateKeys.SingleFeedVm)) |
State.Remove(StateKeys.SingleFeedVm); |
State.Add(StateKeys.SingleFeedVm, Vm); |
WARNING! I was about to forget an important point: events cannot be serialized (once again a reference to another old post of mine:
Serialization Exception: PropertyChangedEventManager is not serializable, it’s related to binary serialization or objects, but the concept is the same), so remember to not mark the events with the DataMemberAttribute or, if your ViewModel has everything public, and you didn’t used the DataContractAttribute to opt-in what you want to be serialized, it’s better to mark the events with [field: IgnoreDataMember] to exclude them from the serialization process.
by guardian
No comments:
Post a Comment