MvvmLight的抽象类ViewModelBase继承了ObservableObject这个类,我们来看看这个类:
/// <summary> /// 一个基类,为了使它的对象属性必须具有可观察性 /// INotifyPropertyChanged,INotifyPropertyChanging /// </summary> public class ObservableObject:INotifyPropertyChanged,INotifyPropertyChanging
可以看见其实它继承了INotifyPropertyChanged,INotifyPropertyChanging这两个.net里的接口,在System.dll程序集中:
#region Assembly System.dll, v2.0.50727 // C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll #endregion namespace System.ComponentModel { // Summary: // Notifies clients that a property value has changed. public interface INotifyPropertyChanged { // Summary: // Occurs when a property value changes. event PropertyChangedEventHandler PropertyChanged; } }
当属性改变时通知客户端。这个类肯定实现了这两个接口中的事件,来看下:
/// <summary> /// 如果需要,唤起PropertyChanged事件,非泛型 /// </summary> /// <remarks>如果参数名在当前类的属性中不一致 /// 在DEBUG条件下抛出异常VerifyPropertyName中</remarks> /// <param name="propertyName">改变的属性名称</param> /// <param name="propertyName"></param> [SuppressMessage("Microsoft.Design","CA1030:UseEventsWhereAppropriate", Justification="This cannot be an event" )] protected virtual void RaisePropertyChanged(string propertyName) { VerifyPropertyName(propertyName); var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } }
还有个就是
protected virtual void RaisePropertyChanging(string propertyName)参数都是string类型的propertyName.
那么更好的肯定是实现这两个方法的泛型版本:
/// <summary> /// 如果需要,唤起PropertyChanged事件,泛型 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="propertyExpression"></param> [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "This cannot be an event")] [SuppressMessage( "Microsoft.Design", "CA1006:GenericMethodsShouldProvideTypeParameter", Justification = "This syntax is more convenient(方便) than other alternatives(选择的余地)")] protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression) { var handler = PropertyChanged; if (handler != null) { var propertyName = GetPropertyName(propertyExpression); handler(this, new PropertyChangedEventArgs(propertyName)); } }
注意到这里有个GetPropertyName方法:
/// <summary> /// 根据表达式提取(Extract)属性名字 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="propertyExpression"></param> /// <returns></returns> protected string GetPropertyName<T>(Expression<Func<T>> propertyExpression) { if (propertyExpression == null) { throw new ArgumentNullException("propertyExpression"); } //关于Linq的文章,可以参考 //http://www.cnblogs.com/lifepoem/archive/2011/12/16/2288017.html //http://www.cnblogs.com/zhili/archive/2012/12/12/LambdaExpression.html var body = propertyExpression.Body as MemberExpression; if (body == null) { throw new ArgumentException("Invalid argument", "propertyExpression"); } var property = body.Member as PropertyInfo; if (property == null) { throw new ArgumentException("Argument is not a property","propertyExpression"); } return property.Name; }
关于Linq学习的博文,园子里已经有很多人写过了,可以参考lifepoem和zhili的。
这个类还有两个重要的泛型方法,Set<T>:
/// <summary> /// 把新值赋给属性,然后唤起PropertyChanged事件 /// </summary> /// <typeparam name="T">属性类型</typeparam> /// <param name="propertyExpression">确认属性改变的表达式,翻译有问题</param> /// <param name="field">保存属性值的变量</param> /// <param name="newValue">改变过后的新值</param> /// <returns>事件只有在两个值不相等的情况下才被唤起</returns> protected bool Set<T>( Expression<Func<T>> propertyExpression, ref T field, T newValue) { if (EqualityComparer<T>.Default.Equals(field,newValue)) { return false; } RaisePropertyChanging(propertyExpression); field = newValue; RaisePropertyChanged(propertyExpression); return true; }
另外一个其实也就是把Expression<Func<T>>参数换成string参数而已
protected bool Set<T>( string propertyName, ref T field, T newValue)
之前我们的例子中的RaisePropertyChanged就是其实就是调用ViewModelBase里的RaisePropertyChanged函数,因为这个函数是虚函数,所以是调用的ViewModelBase中的RaisePropertyChanged实现:
[SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "This cannot be an event")] protected virtual void RaisePropertyChanged<T>(string propertyName, T oldValue, T newValue, bool broadcast) { if (string.IsNullOrEmpty(propertyName)) { throw new ArgumentException("This method cannot be called with an empty string", "propertyName"); } RaisePropertyChanged(propertyName); if (broadcast) { Broadcast(oldValue, newValue, propertyName); } }
最后一个参数如果不为true,而且不注册PropertyChanged事件将不会有任何返回结果。下篇我们介绍NotificationMessage和DialogMessage的简单使用。