ICleanup和IDisposable
MVVM Light中的ViewModelBase同时执行ICleanup接口和IDisposable接口。前者是来自MVVM Light类库本身,后者则是.NET的产物。两者是为了完成同样类型的事件,而且IDisposable的Dispose是不推荐使用的(被标记有Obsolete特性),当然用户仍然可以通过重写Dispose方法。
至于为什么推荐用ICleanup那还执行IDisposable?Laurent Bugnion先生曾经在StackOverflow中解释过这个问题,地址:
http://stackoverflow.com/questions/2963151/cleanup-vs-disposebool-in-mvvm-light
简单地将就是:本来最初只有IDisposable,但是.NET中IDisposable的用途通常代表着执行完后则可以被垃圾回收的,但是ViewModel中的资源清理逻辑并不一定代表着ViewModel马上要等待着GC销毁,也许仅仅是数据存储、关闭文件流等操作。因此ICleanup诞生了。
ViewModelBase执行ICleanup是直接定义一个public virtual的Cleanup方法。并且取消当前对象在默认Messenger注册的任何消息。如源代码:
Messenger.Default.Unregister(this);
Dispose方法则是.NET中Dispose方法执行的模式,public Dispose方法会调用protected Dispose方法,且disposing参数为True。
属性改变处理:PropertyChanged和Broadcast
ViewModelBase同时执行INotifyPropertyChanged,因此定义PropertyChanged事件。同时定义RaisePropertyChanged(等于OnPropertyChanged)方法来执行事件,参数是属性名称。RaisePropertyChanged会把属性名称包装在PropertyChangedEventArgs中(在System.ComponentModel)然后执行PropertyChanged事件。这个.NET事件定义的套路,不需要多解释了。
ViewModelBase同时定义了另一个RaisePropertyChanged方法。该方法的参数是:
(string propertyName, T oldValue, T newValue, bool broadcast)
如果broadcast为false的话,那么和上一个RaisePropertyChanged的执行是一样的,如果broadcast为true,那么该属性改变事件不仅会调用普通的RaisePropertyChanged方法,同时还会调用另一个Broadcast方法来把这个属性改变的事件(或者说是消息),通过MVVM Light特有的消息模式来传播。
Broadcast方法首先会把信息包装在PropertyChangedMessage<T>类型中,此类型继承自PropertyChangedMessageBase,一个非泛型的执行,他们都属于MessageBase:MVVM Light中的基础消息类型。
接着Broadcast方法判断当前ViewModelBase是否已经绑定一个IMessenger,ViewModelBase的一个构造函数运行传入一个自定义的IMessenger类型。如果没有绑定的话,使用默认Messenger(通过Messenger.Default属性)。最后使用IMessenger的Send方法发送消息。
另外ViewModelBase定义了MessengerInstance属性(继承类可见可改写,类型IMessenger)可以修改绑定的ViewModelBase中的默认IMessenger。
当用户定义自己的属性成员时,可以根据需求选择哪个RaisePropertyChanged方法。同时在使用MVVM Light的mvvminpc Code Snippet时,会直接生成类似如下完整代码:
/// <summary>
/// The <see cref="MyProperty" /> property's name.
/// </summary>
public const string MyPropertyPropertyName = "MyProperty";
private bool _myProperty = false;
/// <summary>
/// Gets the MyProperty property.
/// TODO Update documentation:
/// Changes to that property's value raise the PropertyChanged event.
/// This property's value is broadcasted by the Messenger's default instance when it c
/// </summary>
public bool MyProperty
{
get
{
return _myProperty;
}
set
{
if (_myProperty == value)
{
return;
}
var oldValue = _myProperty;
_myProperty = value;
// Remove one of the two calls below
throw new NotImplementedException();
// Update bindings, no broadcast
RaisePropertyChanged(MyPropertyPropertyName);
// Update bindings and broadcast change using GalaSoft.MvvmLight.Messenging
RaisePropertyChanged(MyPropertyPropertyName, oldValue, value, true);
}
}
在下面,用户自己选择调用哪种方法,当然别忘了把throw new NotImplementedException();这句也删了。
判断成员
ViewModelBase同时提供许多判断类型的成员。
比如VerityPropertyName方法,首先它带有Conditional("DEBUG")特性,因此在Release模式下不会被编译进去。使用它可以判断当前ViewModel是否存在指定名称的属性。如果没有,ArgumentException异常抛出。
同时还有静态的IsInDesignModeStatic属性和非静态的IsInDesignMode属性来判断执行环境是否是设计模式(比如Visual Studio设计器或者Blend)。用户可以根据不同模式来执行不用的代码,比如在设计模式下考虑快速生成一些数据做演示。