Great developers like Laurent Bugnion, Rob Eisenberg and Rocky Lhotka write Frameworks.
At the opposite end of the spectrum are developers like me. I write helper classes. Usually I don’t even do that and simply resort to copy and pasting from text files I have saved all over my harddrive.
In the zip file you will find a ViewModel base class designed specifically for doing MVVM in a Windows Phone Silverlight application. It can be used with either the Satellite VM pattern or the Anchor VM pattern as described in the Patterns of Windows Phone Architecture series. It supports Blendability, State persistence, Tombstoning and Xaml-only instantiation.
Also included are the helper classes for saving state described in this post.
Basic usage looks like this:
public class MainViewModel: SerializableViewModelBase<MainViewModel>{}
All the features mentioned above come free simply by inheriting the base class. Add a property, rinse, repeat.
And here’s the implementation below. The code in the base constructor takes the currently newed up instance and throws it into the static instance.
You’ll also note that unlike a typical Singleton implementation, the instance property is not static. This allows XAML instantiation to work properly. A protected Singleton property is provided if you want the Instance property to be static in the derived class. To implement a static Instance property in the derived class, just add one with the new qualifier like this:
public static new MainViewModel Instance { get { return Singleton; } }
BackupMore and RestoreMore can be overridden to save additional state data that might not get serialized with the ViewModel but belongs with it nonetheless (for instance, private fields that need to be saved off).
InitializeDesigner and InitializeInstance can be overriden to perform any initialization logic. Since with XAML instantiation using a static Instance can create your object more than once, the base class is written so these two methods are only called on the first instantiation of the singleton. Additionally, they automatically perform forking logic for instantiation code running in the designer versus instantiation code at runtime.
Your comments and advice are, as always, welcome.
public abstract class SerializableViewModelBase<T> : INotifyPropertyChanged where T : SerializableViewModelBase<T>, new() { private static T _instance; private static object _lockObject = new object(); /// <summary> /// Initializes a new instance of /// the <see cref="SerializableViewModelBase<T>"/> /// class. /// </summary> public SerializableViewModelBase() { lock (_lockObject) { if (IsInstanceEmpty) { _instance = (T)this; Initialize(); } } } /// <summary> /// Gets a value indicating whether /// the singleton instance T is empty. /// </summary> /// <value> /// <c>true</c> if this instance is empty; /// otherwise, <c>false</c>. /// </value> protected static bool IsInstanceEmpty { get { return _instance == null; } } /// <summary> /// Override to perform runtime intialization. /// </summary> virtual protected void InitializeInstance() { } /// <summary> /// Override to perform initialization for /// the designer. /// </summary> virtual protected void InitializeDesigner() { } private void Initialize() { if (DesignerProperties.IsInDesignTool) InitializeDesigner(); else InitializeInstance(); } /// <summary> /// Gets the singleton instance of type T. /// </summary> /// <value>The instance.</value> protected static T Singleton { get { lock (_lockObject) { if (IsInstanceEmpty) new T(); } return _instance; } } /// <summary> /// Gets the singleton instance of type T. /// </summary> /// <value>The instance.</value> public T Instance { get { return Singleton; } } private string _title; /// <summary> /// Gets or sets the Title. /// </summary> /// <value>The title.</value> public string Title { get { return _title; } set { _title = value; OnPropertyChanged("Title"); } } /// <summary> /// Occurs when a property value changes. /// </summary> public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (null != PropertyChanged) PropertyChanged(this , new PropertyChangedEventArgs(propertyName)); } /// <summary> /// Backs up the class instance. /// </summary> /// <param name="store">The data store.</param> /// <returns></returns> public static bool Backup(IDataStorage store) { _instance.BackupMore(store); return store.Backup(typeof(T).Name, _instance); } /// <summary> /// Restores the class instance. /// </summary> /// <param name="store">The data store.</param> public static void Restore(IDataStorage store) { _instance = store.Restore<T>(typeof(T).Name); _instance.RestoreMore(store); } /// <summary> /// Override this method to backup additional state. /// </summary> /// <param name="store">The store.</param> protected virtual void BackupMore(IDataStorage store) { } /// <summary> /// Override this method to restore additional state. /// </summary> /// <param name="store">The store.</param> protected virtual void RestoreMore(IDataStorage store) { } }
This works well, although I have a strange issue where each time I deactivate/activate, my ViewModel’s collection object doubles itself with duplicate data. My ViewModel itself contains an ObservableCollection<MyItem> _items and a single object: MyItem _selectedItem.
I create a class variable of the ViewModel in my view which uses the static "Instance" propery (in the derived class as described). I’m having trouble tracking this down because it occurs somewhere between when I call Backup and Restore. And advice?
http://www.elmfx.com/bbs/boke.asp?pandora11.showtopic.71150.html
Very good articles
Ok,I'll bookmark it