zoukankan      html  css  js  c++  java
  • [WPF/Silverlight]让INotifyPropertyChanged的实现更优雅一些

    我想大部分的WPF和SL开发者都应该对INotifyPropertyChanged这个接口再熟悉不过了。当我们向UI传递属性变化的通知并更新客户端UI时就必须应用到它。(这里插一句,当一个集合中的项改变时我们则需要使用ObservableCollection<T>泛型集合)

    假设我们有一个叫做MenuButtonModel的类,其中一个属性为HasFocus,那么通常情况下我们会这样实现INotifyPropertyChanged接口

        public class MenuButtonModel : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            private bool? _hasFocus;
            public bool? HasFocus
            {
                get { return _hasFocus; }
                set
                {
                    _hasFocus = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this,new PropertyChangedEventArgs("HasFocus"))
                    }
                }
            }
        }

    当这个类的属性比较多的时候(如多了一个IconPath属性),我们又将NotifyPropertyChanged独立出来,如:

    public class MenuButtonModel : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
            public void NotifyPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
    
            private bool? _hasFocus;
            public bool? HasFocus
            {
                get { return _hasFocus; }
                set
                {
                    _hasFocus = value;
                    NotifyPropertyChanged("HasFocus");
                }
            }
    
            private string _iconPath;
            public string IconPath
            {
                get { return _iconPath; }
                set
                { 
                    _iconPath = value;
                    NotifyPropertyChanged("IconPath");
                }
            }
        }

    当我们的类越来越多时,写这样的代码又显得有些疲于奔命。于是乎大家想到了为这些类建立一个共同的基类。所有该基类的派生类自然都实现了IPropertyChanged接口。如下例中的PropertyChangedBase类:

        public class PropertyChangedBase : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            public void NotifyPropertyChanged(string propertyName)
            {
                if (this.PropertyChanged != null)
                {
                    this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }

    然后我们改动MenuButton类,使其继承PropertyChangedBase:

    public class MenuButtonModel : PropertyChangedBase
        {
            private bool? _hasFocus;
            public bool? HasFocus
            {
                get { return _hasFocus; }
                set
                {
                    _hasFocus = value;
                    NotifyPropertyChanged("HasFocus");
                }
            }
    
            private string _iconPath;
            public string IconPath
            {
                get { return _iconPath; }
                set
                { 
                    _iconPath = value;
                    NotifyPropertyChanged("IconPath");
                }
            }

    现在看起来好多了。不过做为有洁癖的程序员,这样的方法显然不会满足俺们的要求,因为我们仍然需要硬编码一个Name(如:IconPath),这很不爽。即使写错了,编译器也不会发现这个潜在的错误。从成千上万个类中寻找一个硬编码问题会让人抓狂。老赵说过要尽量使用强类型,这绝对是至理名言。。。,那么表达式树正式登场!我们需要稍微的改动一下PropertyChangedBase类(这里注意需要using System.Linq.Expressions):

    public class PropertyChangedBase : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            public void NotifyPropertyChanged<T>(Expression<Func<T>> propertyName)
            {
                if (this.PropertyChanged != null)
                {
                    var memberExpression = propertyName.Body as MemberExpression;
                    if (memberExpression != null)
                    {
                        this.PropertyChanged(this, new PropertyChangedEventArgs(memberExpression.Member.Name));
                    }
                }
            }
        }

    经过如上改动,我们就可以在派生类中使用优雅的强类型啦~,如下:

    public class MenuButtonModel : PropertyChangedBase
        {
            private bool? _hasFocus;
            public bool? HasFocus
            {
                get { return _hasFocus; }
                set
                {
                    _hasFocus = value;
                    NotifyPropertyChanged<bool?>(() => this.HasFocus);
                }
            }
    
            private string _iconPath;
            public string IconPath
            {
                get { return _iconPath; }
                set
                {
                    _iconPath = value;
                    NotifyPropertyChanged<string>(() => this.IconPath);
                }
            }
        }

    看起来还不错。不过<bool?>,<string>依然不是很爽,如果可以this.NotifyPropertyChanged( p => p.HasFocus),那多帅啊。要实现这个目的,我们需要使用扩展方法。搞一个静态类出来,如下:

    public static class PropertyChangedBaseEx
        {
            public static void NotifyPropertyChanged<T, TProperty>(this T propertyChangedBase, Expression<Func<T, TProperty>> expression) where T : PropertyChangedBase
            {
                var memberExpression = expression.Body as MemberExpression;
                if (memberExpression != null)
                {
                    string propertyName = memberExpression.Member.Name;
                    propertyChangedBase.NotifyPropertyChanged(propertyName);
                }
                else
                    throw new NotImplementedException();
            }
        }

    然后我们就可以在类中用优雅的语法实现PropertyChanged了。

    全部代码如下:

    MenuButton.cs

    public class MenuButtonModel : PropertyChangedBase
        {
            private bool? _hasFocus;
            public bool? HasFocus
            {
                get { return _hasFocus; }
                set
                {
                    _hasFocus = value;
                    this.NotifyPropertyChanged(p => p.HasFocus);
                }
            }
    
            private string _iconPath;
            public string IconPath
            {
                get { return _iconPath; }
                set
                {
                    _iconPath = value;
                    this.NotifyPropertyChanged(p => p.IconPath);
                }
            }
        }

    PropertyChangedBase.cs以及PropertyChangedBaseEx.cs

    public class PropertyChangedBase : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            public void NotifyPropertyChanged(string propertyName)
            {
                if (this.PropertyChanged != null)
                {
                    this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    
        public static class PropertyChangedBaseEx
        {
            public static void NotifyPropertyChanged<T, TProperty>(this T propertyChangedBase, Expression<Func<T, TProperty>> expression) where T : PropertyChangedBase
            {
                var memberExpression = expression.Body as MemberExpression;
                if (memberExpression != null)
                {
                    string propertyName = memberExpression.Member.Name;
                    propertyChangedBase.NotifyPropertyChanged(propertyName);
                }
                else
                    throw new NotImplementedException();
            }
        }

    需要注意的是,因为是扩展方法,所有this关键字不能省略。

    ok,就到这里,have fun~

  • 相关阅读:
    Beacon技术是什么?
    exclude kernel or other packages from getting updated
    (OK) running CORE & docker on Fedora 23 server
    (OK) dnf
    (OK) dnf
    rpm
    dnf
    dnf install -y kernel-4.2.3-300.fc23
    paper4—Multi-MPE_Trigger_Algorithm—testing
    paper4—Multi-MPE_Trigger_Algorithm
  • 原文地址:https://www.cnblogs.com/024hi/p/Let_Implementation_Of_INotifyPropertChanged_More_Graceful.html
Copyright © 2011-2022 走看看