【WPF】聊聊WPF中INotifyPropertyChanged [TOC]

简介: 【WPF】聊聊WPF中INotifyPropertyChanged [TOC]

一、INotifyPropertyChanged接口

在Windows Presentation Foundation(WPF)中,INotifyPropertyChanged是一个核心接口,用于实现类与视图之间的数据双向绑定。当实体类的某个属性值发生变化时,通过实现此接口可以立即通知绑定到属性的所有UI控件进行更新,ICommand主要针对的是关联到任何实现了ICommand接口的对象的方法。


在C#中,CallerMemberName是.NET框架提供的一个编译器特性(Compiler Feature),它允许你获取调用当前方法的成员名称,而无需硬编码该名称。这对于实现INotifyPropertyChanged接口特别有用,因为它可以减少手动输入属性名的工作量,提高代码的健壮性和可维护性。


不管是ICommand还是INotifyPropertyChanged都必须首先将ViewMode的实现设置为控件或整个界面的DataContext。如:


this.DataContext = new MainViewModel();


DataContext是UI层与数据逻辑层的桥梁


二、DataContext

DataContext是一个非常关键的概念,它是实现数据绑定的基础。DataContext是所有WPF控件都具有的一个依赖属性。它属于System.Windows.FrameworkElement类。这意味着所有继承自该类的控件都可以使用DataContext。

2.1/DataContext作用

DataContext作为一个容器,提供了UI层和数据层之间的连接点。在MVVM(Model-View-ViewModel)架构模式中,通常将ViewModel设置为控件或整个界面的DataContext,这样UI控件可以通过绑定直接访问ViewModel中的数据和命令。


2.2/DataContext特性

继承性:DataContext具有继承特性,子控件如果没有明确设置自身的DataContext,则会从其元素继承DataContext的值。这意味着在整个控件树中可以共享同一个数据上下文对象。

数据绑定:在WPF中,当你写一个数据绑定表达式如{Binding Path=PropertyName}时,默认情况下,Binding将查找当前元素的DataContext,并在其中寻找指定路径的属性。

  • 实现数据驱动视图:通过将业务逻辑对象或ViewModel对象设置为控件或整个界面的DataContext,WPF可以自动根据这些对象中的数据变化更新相关联的用户界面元素。

2.3/DataContext实例


<Window>
  <Window.DataContext>
     <local:MyViewModel/>
  </Window.DataContext>
  <StackPanel>
      <TextBox Text="{Binding Name}" />
      <Button Command="{Binding SaveCommand}" Content="Save" />
  </StackPanel>
</Window>

上面的示例,Window的DataContext被设置为了MyViewModel实例,因为TextBox和Button都可以通过数据绑定访问到MyViewModel中的Name属性和SaveCommand命令。


三、INotifyPropertyChanged接口的几种实现方式

3.1/简单INotifyPropertyChanged绑定

  1. 定义实例类并继承接口INotifyPropertyChanged


public class Person:INotifyPropertyChanged
{
    private string m_Name="默认值是XXXX";
    public string Name 
    {
        get =>m_Name;
        set
        {
           if (m_Name == value) return;
           m_Name = value;
           this.Notify("Name");
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    public void Notify(string propertyName) 
    {
       PropertyChangedEventHandler handler = this.PropertyChanged;
       if(handler!=null)
         handler(this, new PropertyChangedEventArgs(propertyName));
    }
}
  1. 界面绑定ViewModel


<Window x:Class="WpfToolkitApp.WinMvvmFirst"
        ...
        xmlns:vm="clr-namespace:WpfToolkitApp.Model">
       <Window.DataContext>
          <vm:Person></vm:Person>
       </Window.DataContext>
       <Grid>
          <Label Content="名称" HorizontalAlignment="Left" Margin="21,104,0,0" VerticalAlignment="Top"/>
          <TextBox HorizontalAlignment="Left" Margin="60,104,0,0" TextWrapping="Wrap"
              Text="{Binding Name}" 
              VerticalAlignment="Top" 
              Width="221" 
              Height="25"/>
        </Grid>       
</Window>
  1. 界面构造函数绑定DataContext


public partial class WinMvvmFirst : Window
{
    Person person = null;
    public WinMvvmFirst()
    {
        InitializeComponent();
        person = base.DataContext as Person;
    }
}

3.2/使用Lambda表达式,静态扩展语法


   public static class NotificationExtensions
    {
        public static void Notify(this PropertyChangedEventHandler eventHandler, Expression<Func<object>> expression) 
        {
            if (eventHandler == null) 
            {
                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 _trueName;
    public string TrueName
    {
        get{return this._trueName}
        set
        {
            this._trueName = value;
            this.PropertyChanged.Notify(()=>this.TrueName);
        }
    }
    
}

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


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);
         }
      };
}

3.3/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")
{
    
}

属性CallerMemberName的解决办法和方法二是基本相同的,不同的这个在net框架中解决的。更多信息可以查看CallerMemberName,Net4.5还提供了CallerFilePath、CallerLineNumber,稍微会详细讲解。


四、INotifyPropertyChanged总结

INotifyPropertyChanged接口用于向客户端发出某一属性值已更改的通知。在应用有两种方式OneTime模式、OneWay模式和TwoWay模式。

  • OneTime模式

OneTime模式是一个初始化一次绑定。不常用。

  • OneWay模式
  • 绑定源的每一次变化都会通知绑定目标,但是绑定目标的改变不会改变绑定源。当绑定源的数据实体类没有实现INotifyPropertyChanged接口时,当改变了数据源,发现绑定目录的UI上的相应的数据不会立即变化。
  • TwoWay模式

TwoWay模式下,当绑定源的数据实体类没有实现INotifyPropertyChanged接口时,空间的更改会让数据源立即发改变,但是改变数据源,绑定目标控件却不会立即发送改变,所以当我们需要数据源改变时相对应的UI立即改变时,需要实现INotifyPropertyChanged接口。

————————————————


                           版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

                     

原文链接:https://blog.csdn.net/songjianlong/article/details/138584695

目录
相关文章
WPF学习—INotifyPropertyChanged Interface
WPF学习—INotifyPropertyChanged Interface
WPF学习—INotifyPropertyChanged Interface
|
前端开发 C# 安全
[译]WPF MVVM 架构 Step By Step(5)(添加actions和INotifyPropertyChanged接口)
原文:[译]WPF MVVM 架构 Step By Step(5)(添加actions和INotifyPropertyChanged接口)   应用不只是包含textboxs和labels,还包含actions,如按钮和鼠标事件等。
856 0
|
前端开发 C# .NET
WPF MVVM 写一个健壮的INotifyPropertyChanged基类
当我们用MVVM的时候要实现INotifyPropertyChanged,如果你是基于.net4.5以下的framework(.net4.5已有新特性我这里就不说了) 你很可能会这么写 public class MyModel : INotifyPropertyChanged {...
942 0
|
6月前
|
C# 开发者 Windows
基于Material Design风格开源、易用、强大的WPF UI控件库
基于Material Design风格开源、易用、强大的WPF UI控件库
378 0
|
6月前
|
C#
浅谈WPF之装饰器实现控件锚点
使用过visio的都知道,在绘制流程图时,当选择或鼠标移动到控件时,都会在控件的四周出现锚点,以便于修改大小,移动位置,或连接线等,那此功能是如何实现的呢?在WPF开发中,想要在控件四周实现锚点,可以通过装饰器来实现,今天通过一个简单的小例子,简述如何在WPF开发中,应用装饰器,仅供学习分享使用,如有不足之处,还请指正。
137 1
|
3月前
|
开发框架 缓存 前端开发
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
|
3月前
|
C# 开发者 Windows
一款基于Fluent设计风格、现代化的WPF UI控件库
一款基于Fluent设计风格、现代化的WPF UI控件库
|
3月前
|
C# Windows
WPF中如何使用HandyCotrol控件库
WPF中如何使用HandyCotrol控件库
184 1