代码改变世界

MvvmLight学习心得三

2013-01-05 21:16  爱研究源码的javaer  阅读(740)  评论(0编辑  收藏  举报

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学习的博文,园子里已经有很多人写过了,可以参考lifepoemzhili的。

这个类还有两个重要的泛型方法,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的简单使用