zoukankan      html  css  js  c++  java
  • WPF系列之三:实现类型安全的INotifyPropertyChanged接口,可以不用“Magic string” 么?

    通常实现INotifyPropertyChanged接口很简单,为你的类只实现一个PropertyChanged 的Event就可以了。

    例如实现一个简单的ViewModel1类:

     public class ViewModel1 : INotifyPropertyChanged
        {
            private string _data;
            public string Data
            {
    
                get { return _data; }
                set
                {
                    if (_data == value)
                        return;
                    _data = value;
                    // Type un-safe PropertyChanged raise
                    PropertyChanged(this, new PropertyChangedEventArgs("Data"));
                }
            }
            #region Implementation of INotifyPropertyChanged
            public event PropertyChangedEventHandler PropertyChanged = null;
            #endregion
        }
    ViewModel1

    上面是一个标准的实现一个可绑定的属性的方式,问题是其中使用了一个“Data”的字符串来表明发生变化的属性名。问题在于如果有人改变了属性的名字而忘记改变这里的字符串的话

    ,即使编译不会产生问题,但binding也会失败掉。最好的方法当然是当有人改变属性的名字时,编译器可以告诉我们这个地方也需要相应改变。

    例如实现下面这样:

     private string _data;
            public string Data
            {
    
                get { return _data; }
                set
                {
                    PropertyChanged.ChangeAndNotify(ref _data, value, () => Data);
                }
            }
    类型安全的通知更新

    让我们利用扩展方法来扩展PropertyChangedEventHandler实现这个目标:

       public static class PropertyChangedEventHandlerExtentions
        {
            public static bool ChangeAndNotify<T>(this PropertyChangedEventHandler handler, ref T field, T value, Expression<Func<T>> memberExpression)
            {
                if (memberExpression == null)
                {
                    throw new ArgumentNullException("memberExpression");
                }
                // Retreive lambda body
                var body = memberExpression.Body as MemberExpression;
                if (body == null)
                {
                    throw new ArgumentException("Lambda must return a property.");
                }
                if (EqualityComparer<T>.Default.Equals(field, value))
                {
                    //值没有发生变化,不通知更新
                    return false;
                }
                // Extract the right part (after "=>")
                var vmExpression = body.Expression as ConstantExpression;
                if (vmExpression != null)
                {
                    // Create a reference to the calling object to pass it as the sender
                    LambdaExpression lambda = Expression.Lambda(vmExpression);
                    Delegate vmFunc = lambda.Compile();
                    object sender = vmFunc.DynamicInvoke();
    
                    if (handler != null)
                    {
                        //body.Member.Name,Extract the name of the property to raise a change on
                        handler(sender, new PropertyChangedEventArgs(body.Member.Name));
                    }
                }
                field = value;
                return true;
            }
        }
    PropertyChangedEventHandlerExtentions

    方法的核心思想就是1.利用扩展方法来扩展delegate:PropertyChangedEventHandler,2.用引用传值把原来的值传入扩展方法用来做比较,3.用lambda expression表达式树来传递待更新的属性名称来达到类型安全的目的,并且传递了调用这个lambda的对象实例(sender)。

    如果上面的方法比较难理解,我觉得下面的方法也是可以接受的:

    1.依然传递Magic string,但是可以简化值比较的代码:

    public class Data : INotifyPropertyChanged
    {
        // boiler-plate
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
        protected bool SetField<T>(ref T field, T value, string propertyName)
        {
            if (EqualityComparer<T>.Default.Equals(field, value)) return false;
            field = value;
            OnPropertyChanged(propertyName);
            return true;
        }
    
        // props
        private string name;
        public string Name
        {
            get { return name; }
            set { SetField(ref name, value, "Name"); }
        }
    }
    View Code

    2.不用传递Magic string,也不用ExtentionMethod,只利用lambda expression来取得属性名:

     public class Data2 : INotifyPropertyChanged
        {
            // boiler-plate
            public event PropertyChangedEventHandler PropertyChanged;
            protected virtual void OnPropertyChanged(string propertyName)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
            }
            protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
            {
                if (selectorExpression == null)
                    throw new ArgumentNullException("selectorExpression");
                MemberExpression body = selectorExpression.Body as MemberExpression;
                if (body == null)
                    throw new ArgumentException("The body must be a member expression");
                OnPropertyChanged(body.Member.Name);
            }
            protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
            {
                if (EqualityComparer<T>.Default.Equals(field, value)) return false;
                field = value;
                OnPropertyChanged(selectorExpression);
                return true;
            }
            // props
            private string name;
            public string Name
            {
                get { return name; }
                set { SetField(ref name, value, () => Name); }
            }
        }
    View Code

    完毕。

    参考文章:

    http://www.wpftutorial.net/INotifyPropertyChanged.html

    http://blog.decarufel.net/2009/07/how-to-use-inotifypropertychanged-type_22.html

    http://stackoverflow.com/questions/1315621/implementing-inotifypropertychanged-does-a-better-way-exist

    作者:Andy Zeng

    欢迎任何形式的转载,但请务必注明出处。

    http://www.cnblogs.com/andyzeng/p/3710421.html

  • 相关阅读:
    Android中fragment之间和Activity的传值、切换
    javascript--经典实例锦集
    Android中ListView动态加载数据
    Android开发之调用系统图库及相机
    Android 相机开发详解
    RTSP实例解析
    【Android UI设计与开发】第18期:滑动菜单栏(三)SlidingMenu动画效果的实现
    js 第二小步
    JavaScript初步+基本函数
    javaweb实战开始
  • 原文地址:https://www.cnblogs.com/andyzeng/p/3710421.html
Copyright © 2011-2022 走看看