当前位置: 首页 > news >正文

【WPF】聊聊WPF中INotifyPropertyChanged [TOC]

聊聊WPF中INotifyPropertyChanged

文章目录

  • 聊聊WPF中INotifyPropertyChanged
    • 一、INotifyPropertyChanged接口
    • 二、DataContext
      • 2.1/DataContext作用
      • 2.2/DataContext特性
      • 2.3/DataContext实例
    • 三、INotifyPropertyChanged接口的几种实现方式
      • 3.1/简单INotifyPropertyChanged绑定
      • 3.2/使用Lambda表达式,静态扩展语法
      • 3.3/Net4.5,框架提供的解决方法
    • 四、INotifyPropertyChanged总结

一、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接口。

http://www.lryc.cn/news/344008.html

相关文章:

  • SpringBoot Actuator未授权访问漏洞的解决方法
  • AI大模型探索之路-训练篇18:大语言模型预训练-微调技术之Prompt Tuning
  • Ollamallama
  • 苹果Mac用户下载VS Code(Universal、Intel Chip、Apple Silicon)哪个版本?
  • Linux(Ubuntu)安装CGAL(非root)
  • hadoop学习---基于Hive的教育平台数据仓库分析案例(三)
  • RAFT:引领 Llama 在 RAG 中发展
  • 上海亚商投顾:沪指缩量调整 合成生物概念股持续爆发
  • Maven+Junit5 + Allure +Jenkins 搭建 UI 自动化测试实战
  • docker学习笔记(三)搭建NFS服务实验
  • super关键字
  • 【经典算法】LeetCode 200. 岛屿数量(Java/C/Python3/Go实现含注释说明,中等)
  • Hive SQL-DQL-Select查询语句用法详解
  • 沙盘Sandboxie v5.56.4
  • Arcpy开发记录
  • Android使用itextpdf操作PDF文档
  • llama_index微调BGE模型
  • 什么是限流?常见的限流算法
  • ZL-0895小动物活动记录仪可同时检测8只动物的活动量
  • 注册测绘师的前世今生
  • Python中的异常处理:深入探索try-except-finally结构
  • 【R语言】边缘概率密度图
  • 中国结(科普)
  • 使用Android Studio 搭建AOSP FrameWork 源码阅读开发环境
  • 区块链 | IPFS:CID
  • PostgreSQL(十二)报错:Tried to send an out-of-range integer as a 2-byte value: 51000
  • Linux守护进程
  • HarmonyOS 应用开发——入门
  • 开源免费的发票识别OCR应用:Invoice
  • 关于Docker alpine