• 转载:WPF MVVM之INotifyPropertyChanged接口的几种实现方式


    原文地址:http://www.cnblogs.com/xiwang/ 

    序言

           借助WPF/Sliverlight强大的数据绑定功能,可以比实现比MFC,WinForm更加优雅轻松的数据绑定。但是在使用WPF/Silverlight绑定时,有件事情是很苦恼的:当ViewModel对象放生改变,需要通知UI。我们可以让VM对象实现INotifyPropertyChanged接口,通过事件来通知UI。但问题就出现这里……

    一,描述问题

            情形:现在需要将一个Person对象的Name熟悉双向绑定到UI中的TextBox,的确这是一件很简单的事情,但还是描述下:

            XAML:

    <TextBox Text="{Binding Name,Mode=TwoWay}"/>

           C# Code:

    复制代码
    public class Person : INotifyPropertyChanged
        {
            private string m_Name;
            public string Name
            {
                get { return m_Name; }
                set 
                {
                    if (m_Name == value) return; 
                    m_Name = value;
                    this.Notify("Name");
                }
            }
    
            public Person()
            {
                this.m_Name = "墨梅,在这里......";
            }
    
    
            public event PropertyChangedEventHandler PropertyChanged;
            public void Notify(string propertyName)
            {
                PropertyChangedEventHandler handler = this.PropertyChanged;
                if (handler != null)
                    handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    复制代码

           是的,这就可以实现了。但是这里一个问题困惑我,曾经就在this.Notify("Name"),将参数写错,UI迟迟得不到响应。这个错误很难发现!!!也很难跟踪,但是这个细微的错误可以导致一个很严重的运行时错误。这的确是一件很苦恼的事情。

    二解决问题

    方法一:添加验证

    复制代码
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected virtual void OnPropertyChanged(string propertyName)
    {
        this.VerifyPropertyName(propertyName);
    
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
        }
    }
    
    [Conditional("DEBUG")]
    [DebuggerStepThrough]
    public void VerifyPropertyName(string propertyName)
    {
        // Verify that the property name matches a real,  
        // public, instance property on this object.
        if (TypeDescriptor.GetProperties(this)[propertyName] == null)
        {
            string msg = "Invalid property name: " + propertyName;
    
            if (this.ThrowOnInvalidPropertyName)
                throw new Exception(msg);
            else
                Debug.Fail(msg);
        }
    }
    复制代码

         这里对验证事件参数使用条件编译[Conditional(“DEBUG”)],在release版本中这个函数是不会调用的,比使用#if 等有更明显有优势。

         这个方法虽然可以达到目的,但是还是那么的别扭,必须到运行时才能知道是否有错误,所以还是不怎么好。

    方法二,使用Lambda表达式,静态扩展语法

    复制代码
    public static class NotificationExtensions
        {
            public static void Notify(this PropertyChangedEventHandler eventHandler, Expression<Func<object>> expression)
            {
                if( null == eventHandler )
                {
                    return;
                }
                var lambda = expression as LambdaExpression;
                MemberExpression memberExpression;
                if (lambda.Body is UnaryExpression)
                {
                    var unaryExpression = lambda.Body as UnaryExpression;
                    memberExpression = unaryExpression.Operand as MemberExpression;
                }
                else
                {
                    memberExpression = lambda.Body as MemberExpression;
                }
                var constantExpression = memberExpression.Expression as ConstantExpression;
                var propertyInfo = memberExpression.Member as PropertyInfo;
                
                foreach (var del in eventHandler.GetInvocationList())
                {
                    del.DynamicInvoke(new object[] {constantExpression.Value, new PropertyChangedEventArgs(propertyInfo.Name)});
                }
            }
       }
    复制代码

    这里用使用的静态扩展语法,我还是比较喜欢这个的,但是并不是所有人都喜欢哦。如何使用呢:

    复制代码
    public class Employee : INotifyPropertyChanged
    
    {
    
        public event PropertyChangedEventHandler PropertyChanged;
    
    
    
        private string _firstName;
    
        public string FirstName 
    
        {
    
           get { return this._firstName; }
    
           set
    
           {
    
              this._firstName = value;
    
              this.PropertyChanged.Notify(()=>this.FirstName);
    
           }
    
        }
    
    }
    复制代码

    这里还可以添加一个很实用的扩展:

    复制代码
    public static void SubscribeToChange<T>(this T objectThatNotifies, Expression<Func<object>> expression, PropertyChangedEventHandler<T> handler)
                where T : INotifyPropertyChanged
            {
                objectThatNotifies.PropertyChanged +=
                    (s, e) =>
                        {
                            var lambda = expression as LambdaExpression;
                            MemberExpression memberExpression;
                            if (lambda.Body is UnaryExpression)
                            {
                                var unaryExpression = lambda.Body as UnaryExpression;
                                memberExpression = unaryExpression.Operand as MemberExpression;
                            }
                            else
                            {
                                memberExpression = lambda.Body as MemberExpression;
                            }
                            var propertyInfo = memberExpression.Member as PropertyInfo;
    
                            if(e.PropertyName.Equals(propertyInfo.Name))
                            {
                                handler(objectThatNotifies);
                            }
                        };
            }
    复制代码

    通过上面的代码,可以订阅熟悉改变事件,如:

    复制代码
    myObject.SubscripeToChange(()=>myObject.SomeProperty,SomeProperty_Changed); 
     And then your handler would look like this:
    
    private void SomeProperty_Changed(MyObject myObject)
    {
        /* ... implement something here */
    }
    复制代码

    方法三,net4.5,框架提供的解决方法

    复制代码
    private string m_myProperty;
    public string MyProperty
    {
        get { return m_myProperty; }
        set
        {
            m_myProperty = value;
            OnPropertyChanged();
        }
    }
    
    private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
    {
        // ... do stuff here ...
    }
    复制代码

    属性CallerMemberName的解决办法和方法二是基本相同的,不同的是这个在net框架中解决的。更多信息可以查看CallerMemberName,net4.5还提供了

    CallerFilePath,CallerLineNumber,这几很有用的语法

    方法四,这个也不错哦

    复制代码
    public static class SymbolExtensions
        {
            public static string GetPropertySymbol<T,R>(this T obj, Expression<Func<T,R>> expr)
            {
                return ((MemberExpression)expr.Body).Member.Name;
            }
        }
     public class ConversionOptions : INotifyPropertyChanged
        {
            private string _outputPath;
            public string OutputPath
            {
                get { return _outputPath;}
                set
                {
                    _outputPath = value;
                    OnPropertyChanged(o => o.OutputPath);
                }
            }
    
            private string _blogName;
            public string BlogName
            {
                get { return _blogName;}
                set
                {
                    _blogName = value;
                    OnPropertyChanged(o => o.BlogName);
                }
            }
    
            private string _secretWord;
            public string SecretWord
            {
                get { return _secretWord; }
                set
                {
                    _secretWord = value;
                    OnPropertyChanged(o => o.SecretWord);
                }
            }
    
    
            protected virtual void OnPropertyChanged<R>(Expression<Func<ConversionOptions, R>> propertyExpr)
            {
                OnPropertyChanged(this.GetPropertySymbol(propertyExpr));
            }
    
            protected virtual void OnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
    
           
            public event PropertyChangedEventHandler PropertyChanged;
        }
    复制代码

    注释:这里还有更多参考信息,您可以在这里了解更加清楚:

    wpf MVVM

    ingebrigtsen

    MSDN

    dorony blogs

  • 相关阅读:
    《三极管应用分析精粹》终审完成,很快就要印刷了!
    关于SPAPI注册,SP-API注册,SPAPI申请,SP-API申请,开发人员资料注册,amazon亚马逊开发人员资料申请注册,amazon亚马逊销售合作伙伴 API申请注册,SP-API申请注册,amazon亚马逊Selling Partner API申请注册详细指导
    日照的那片海
    Cesium地下模式应用示例
    nginx-1.12.2解决跨域问题nginx.conf设置参考记录
    产品功能被像素级抄袭了。我们拿什么来保护原创的产品设计?
    网线的特征阻抗是多少?协议转换器上连接2m线,其非平衡阻抗是多少欧姆?
    ArrayList、LinkedList、HashSet、HashMap、Iterator
    java基础(枚举、包)
    微服务架构、ELK、ETL
  • 原文地址:https://www.cnblogs.com/Huaran1chendu/p/5049871.html
Copyright © 2020-2023  润新知