WPF MVVM之INotifyPropertyChanged接口的几种实现方式

这篇具有很好参考价值的文章主要介绍了WPF MVVM之INotifyPropertyChanged接口的几种实现方式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

序言

       借助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;
    }

注释:这里还有更多参考信息,您可以在这里了解更加清楚:文章来源地址https://www.toymoban.com/news/detail-601535.html

到了这里,关于WPF MVVM之INotifyPropertyChanged接口的几种实现方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java发送http请求的几种方式,调用第三方接口的方法:HttpUtil、HttpURLConnection等

    参考:https://blog.csdn.net/yubin1285570923/article/details/126225347 put请求 post带请求头 get、delete类似… 使用JDK原生提供的net,无需其他jar包,代码如下: 需要用到commons-httpclient-3.1.jar,maven依赖如下: 看一下我实际应用的例子 需要用到httpclient-4.5.6.jar,maven依赖如下: RestTemplate 是由

    2024年01月18日
    浏览(52)
  • 实现跨域的几种方式

    前后端的分离导致了跨域的产生  跨域的三要素:协议 域名 端口 三者有一个不同即产生跨域 例如: http ://www.csdn.com https ://www.csdn.com 由于协议不同,端口不同而产生跨域 注:http的默认端口80,https的默认端口443 跨域的解决方案 前端:webpack proxy,jsonp,ngix反向代理,webpac

    2024年02月13日
    浏览(44)
  • 前端实现动画的几种方式简介

    这里只是做简要介绍,属于知识的拓展。每种方案的更详细的使用方式需要各位自行了解。 大体上技术方案分为:CSS 动画、SVG 动画、CSS + SVG、JS 控制的逐帧动画、GIF 图。 CSS 实现动画有两种方式,一种是使用 trasition ;另一种是使用 animation 。 默认情况下,当 CSS 中的属性值

    2024年04月22日
    浏览(45)
  • 线程间实现通信的几种方式

    线程间通信的模型有两种:共享内存和消息传递,下面介绍的都是围绕这两个来实现 有两个线程A和B,B线程向一个集合里面依次添加元素“abc”字符串,一共添加10次,当添加到第五次的时候,希望线程A能够收到线程B的通知,然后B线程执行相关的业务操作 Object类提供了线程

    2024年02月15日
    浏览(66)
  • Java实现异步的几种方式

    普通线程实现异步,但频繁创建、销毁线程比较耗资源,所以一般交给线程池执行 结果: Future异步 和普通线程实现异步区别不大,只是使用Future是要获取执行后的返回值 结果: Spring的@Async异步 使用@Async注解实现异步的前提是需要在启动类上标注@EnableAsync来开启异步配置

    2024年02月04日
    浏览(66)
  • 单例模式的几种实现方式

    在Java中,实现单例模式主要有几种方式:懒汉式、饿汉式、双重检查锁定、静态内部类和枚举。每种方式都有其特点和适用场景。 1. 饿汉式(线程安全) 饿汉式是最简单的一种实现方式,通过静态初始化实例,保证了线程安全。但它不是懒加载模式,无法在实际使用时才创

    2024年02月20日
    浏览(49)
  • Android 截屏实现的几种方式

    image.png image.png 1、View 截屏 View 截图是将当前 View 界面截取下来,而对于屏幕上其他信息比如:状态栏或其他应用的界面将无法截取。 1.1 截取除了导航栏之外的屏幕 1.2 截取某个控件或者区域 2、WebView 截屏 WebView 截屏有四种方式 2.1 使用 capturePicture() 方法(已废弃) private

    2024年02月06日
    浏览(40)
  • JavaScript里实现继承的几种方式

    JavaScript 中的继承可以通过以下几种方式来实现: 1、原型链继承 :通过将子类的原型对象指向父类的实例来实现继承。这种方式的优点是实现简单,缺点是父类的私有属性和方法子类是不能访问的。   2、借用构造函数继承 :通过在子类的构造函数中调用父类的构造函数来

    2023年04月23日
    浏览(50)
  • 游戏中模型动画的几种实现方式

    游戏内动画的实现方式一般有这几种: 骨骼动画 顶点动画 材质动画 CPU蒙皮动画 骨骼动画是一种基于骨骼系统的动画技术,它通过对骨骼进行变换来控制模型的姿态和动作。 在骨骼动画中,模型通常被分解成多个部分,每个部分都与一个或多个骨骼相连,通过对骨骼进行旋

    2024年02月05日
    浏览(61)
  • 分布式锁的几种实现方式:

    redis是基于单线程,在某个时刻只会有一个线程执行命令,可以利用set原子性的操作,配合set nx(RedisStringCommands.SetOption.SET_IF_ABSENT) ,这样,当多个线程或多个节点尝试获取锁时,只有一个可以成功,其他的会因为锁已存在而获取失败。这种方式通过 Redis 来实现分布式锁,

    2024年01月18日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包