本篇我们着重介绍ViewModelBase,演示Set和RaisePropertyChanged方法的使用,以及就Cleanup方法释放资源展开讨论。
ICleanup |
接口。实现该接口的ViewModel需要在Cleanup方法里释放资源,特别是-= event |
ObservableObject |
该类实现了INotifyPropertyChanged接口,定义了一个可通知的对象基类,供ViewModelBase继承 |
ViewModelBase |
继承自ObservableObject, ICleanup。将作为MvvmLight框架下使用的ViewModel的基类。主要提供Set和RaisePropertyChanged供外部使用。同时会在Cleanup方法里,Unregister该实例的所有的MvvmLight Messenger(在GalaSoft.MvvmLight.Messaging命名空间定义) |
以上是第一篇里给出的表格,ViewModelBase是MvvmLight里非常重要的一个基类,理论上使用MvvmLight你所有的ViewModel都需要继承该类(当然你也可以不继承,那你还用啥MvvmLight?啥?只用RelayCommand?给跪了……)
我们先看一下最基本的Set和RaisePropertyChanged方法的使用:
private string title; public string Title { get { return title; } set { Set(ref title , value); } } private string text; public string Text { get { return text; } set { text = value; RaisePropertyChanged("Text"); RaisePropertyChanged("TitleAndText"); } } public string TitleAndText { get { return title + text; } }
Set方法会再属性赋值时自动为你调用RaisePropertyChanged进行通知。当然你也可以手动调用RaisePropertyChanged方法。
MvvmLight的源代码如下,将可复用的逻辑提取封装,减少了我们搬砖时的工作量:
protected bool Set<T>( ref T field, T newValue = default(T), bool broadcast = false, [CallerMemberName] string propertyName = null) { if (EqualityComparer<T>.Default.Equals(field, newValue)) { return false; } #if !PORTABLE RaisePropertyChanging(propertyName); #endif var oldValue = field; field = newValue; RaisePropertyChanged(propertyName, oldValue, field, broadcast); return true; }
Cleanup是一个非常重要的方法。当前Page在OnNavigatedFrom时,应该要释放不再需要的资源,特别是-= event,Unregister掉MvvmLight的Messenger。
在继承ViewModelBase的子类ViewModel里调用base.Cleanup();会自动释放掉当前ViewModel注册的Messenger
ViewModelBase里的Cleanup方法:
public virtual void Cleanup() { MessengerInstance.Unregister(this); }
所以一般ViewModel的OnNavigatedFrom方法看上去都是这个样子:
public void OnNavigatedFrom(object obj) { base.Cleanup(); this.xxxxEvent -= xxxxHandler; }
什么什么,你说ViewModel是没有OnNavigatedFrom方法的?确实是没有的,但是我们这里给需要处理导航事件的ViewModel都实现了INavigable接口:
public interface INavigable { void OnNavigatedFrom(object obj); void OnNavigatedTo(object obj); }
然后呢,override需要处理导航事件的Page的相应方法,调用ViewModel里的NavigatedFrom、NavigatedTo方法,传递参数,把处理的逻辑放到ViewModel中:
public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); var navigable = DataContext as INavigable; navigable.OnNavigatedTo(e.Parameter); } protected override void OnNavigatedFrom(NavigationEventArgs e) { base.OnNavigatedFrom(e); var navigable = DataContext as INavigable; navigable.OnNavigatedFrom(e.Parameter); } }
看到这里,是不是觉得释放资源什么的也是一件非常的简单的事情呢?但是骚年!Too young too simple, sometimes naive!仅仅这样就够了吗?我们需要再回到Cleanup方法,既然ViewModel可以通过OnNavigatedFrom来释放资源,但如果ViewModel并没有,或者说不需要导航事件,又该如何处理呢。例如某个MainPage对应的MainViewModel中存在一个ContactViewModel的列表:
public class MainViewModel : ViewModelBase, INavigable { ObservableCollection<ContactViewModel> ContactList { get; set; }
而ContactViewModel仅仅是对Contact数据对象做的封装,并不存在导航事件。这时候,如果不需要ContactList常驻内存,MainViewModel的OnNavigatedFrom的方法就长成这样了:
public void OnNavigatedFrom(object obj) { base.Cleanup(); this.xxxxEvent -= xxxxHandler; foreach (var contact in ContactList) { contact.Cleanup(); } ContactList.Clear(); }
没有错哦,仍然是继承了ViewModelBase的ContactViewModel自己来释放内部的资源,但是Cleanup的调用是由外部引用ContactList的MainViewModel来发起的。
本篇就ViewModelBase的继承使用展开了讨论,介绍了一点俺平时使用的经验,包括如何使用导航事件和释放资源。还希望能给萌新们启发,老司机们轻拍。
另外MvvmLight框架使用入门系列可能会暂停一下(反正也没人看……),因为俺接下来要开始搞Win10的Universal App了,挖咔咔!