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

WPF/Prism 中计算属性的通知机制详解 —— SetProperty 与 RaisePropertyChanged

WPF/Prism 中计算属性的通知机制详解 —— SetProperty 与 RaisePropertyChanged

在 WPF + MVVM 的开发中,我们经常会遇到 计算属性依赖其他属性 的场景。
比如下面这个例子,Key 是由 ModeFullPathSerialNumber 拼接出来的:

public string Key
{get{var name = Path.GetFileNameWithoutExtension(ModeFullPath);return $"{name}_{SerialNumber}";}
}

前台 UI 绑定的是 Key,但它并不是一个独立的字段,而是依赖 ModeFullPathSerialNumber
问题来了:当依赖属性变化时,如何让绑定到 Key 的 UI 也自动刷新?


一、经典实现(INotifyPropertyChanged)

如果直接用 INotifyPropertyChanged,代码会是这样的:

private string modeFullPath;
public string ModeFullPath
{get => modeFullPath;set{if (modeFullPath != value){modeFullPath = value;OnPropertyChanged(nameof(ModeFullPath));OnPropertyChanged(nameof(Key)); // 额外通知 Key 也变了}}
}private string serialNumber;
public string SerialNumber
{get => serialNumber;set{if (serialNumber != value){serialNumber = value;OnPropertyChanged(nameof(SerialNumber));OnPropertyChanged(nameof(Key));}}
}public string Key => $"{Path.GetFileNameWithoutExtension(ModeFullPath)}_{SerialNumber}";

思路很清晰:依赖属性变化时,额外调用一次 OnPropertyChanged(nameof(Key))


二、Prism 的写法(SetProperty + RaisePropertyChanged)

Prism 的 BindableBase 提供了 SetPropertyRaisePropertyChanged 两个辅助方法,让代码更简洁。

private string modeFullPath = string.Empty;
public string ModeFullPath
{get => modeFullPath;set{if (SetProperty(ref modeFullPath, value))RaisePropertyChanged(nameof(Key));}
}private string serialNumber = string.Empty;
public string SerialNumber
{get => serialNumber;set{if (SetProperty(ref serialNumber, value))RaisePropertyChanged(nameof(Key));}
}public string Key => $"{Path.GetFileNameWithoutExtension(ModeFullPath)}_{SerialNumber}";

这样就能确保:

  • ModeFullPathSerialNumber 更新时,UI 会刷新自身绑定;
  • Key 作为计算属性,也会自动刷新。

三、SetProperty 和 RaisePropertyChanged 的区别

很多人刚接触 Prism 的时候会疑惑:这两个方法有什么不同?

方法作用是否修改字段是否触发通知
SetProperty比较新值和旧值,不同则赋值,并触发通知✅ 是✅ 是
RaisePropertyChanged只触发通知,不修改字段❌ 否✅ 是

内部关系可以理解为:

SetProperty(ref field, value)
// 等价于
if (!EqualityComparer<T>.Default.Equals(field, value))
{field = value;RaisePropertyChanged(nameof(Property));return true;
}
return false;

所以:

  • 对于普通属性:用 SetProperty 就够了。
  • 对于计算属性:在依赖属性的 setter 中额外调用 RaisePropertyChanged(nameof(计算属性))

四、总结

  1. 计算属性不会自己触发更新,必须在依赖属性改变时额外通知。

  2. 在 Prism 中,写法推荐是:

    if (SetProperty(ref modeFullPath, value))RaisePropertyChanged(nameof(Key));
    
  3. SetProperty 用来做字段赋值 + 自动通知;
    RaisePropertyChanged 只负责通知,常用于计算属性的依赖更新。

这样,你的 WPF/Prism 项目里就能优雅地处理计算属性刷新问题,避免 UI 不更新的尴尬。


✨ 如果以后计算属性很多,想减少手写 RaisePropertyChanged(nameof(...)) 的重复代码,还可以考虑 扩展基类,自动处理依赖关系。这个我可以另开一篇文章专门讲。

后续

SerialNumber 这个改变确实生效了,但是有个属性是YoloConfig.ModeFullPath,这个没有生效

SerialNumber 生效,而 yoloConfig.ModeFullPath 没生效,这里面有个关键点:

现在绑定的 Key 是这样依赖的:

public string Key => $"{Path.GetFileNameWithoutExtension(yoloConfig.ModeFullPath)}_{SerialNumber}";

区别在于:

  • SerialNumber你这个 ViewModel 自己的属性,你在 set 里写了 RaisePropertyChanged(nameof(Key)),所以 UI 会更新。
  • yoloConfig.ModeFullPath另一个对象的属性,当它变化时,你这个 ViewModel 并不会自动收到通知。

🔎 为什么会这样?

INotifyPropertyChanged 的机制只会通知 当前对象自己的属性
你在 yoloConfig.ModeFullPath 改变时,并没有告诉外层的 ViewModel:“嘿,Key 也变了”。
所以 UI 不知道要刷新。


✅ 解决方案

直接把ModeFullPath放到外层!

🚀 总结

  • SerialNumber 更新能生效,是因为它在当前 VM 内,SetProperty 已经帮你 Raise 了。
  • YoloConfig.ModeFullPath 更新没生效,是因为它是 嵌套对象的属性,你需直接把他放到外层!

文章转载自:

http://ipogvQih.gLnwL.cn
http://C8iJYUfL.gLnwL.cn
http://ey8z32tZ.gLnwL.cn
http://36tG3K6O.gLnwL.cn
http://cbozUVtJ.gLnwL.cn
http://YIrSjbjU.gLnwL.cn
http://I8GJS3vi.gLnwL.cn
http://3vP2PAAB.gLnwL.cn
http://vZzx0ZGe.gLnwL.cn
http://Rdg6zgoH.gLnwL.cn
http://Hr8bIpVe.gLnwL.cn
http://0ETLxfUO.gLnwL.cn
http://PM211Bum.gLnwL.cn
http://nQH11sFm.gLnwL.cn
http://MTzsHjKz.gLnwL.cn
http://e1I19rTh.gLnwL.cn
http://pu1qte0D.gLnwL.cn
http://gf57behf.gLnwL.cn
http://4eFcXgcA.gLnwL.cn
http://1WlyXyGL.gLnwL.cn
http://wgPKO5t9.gLnwL.cn
http://ax9tcqnG.gLnwL.cn
http://Gu4n3ri5.gLnwL.cn
http://BzAG9OjG.gLnwL.cn
http://03Q75r7s.gLnwL.cn
http://a29BA8D2.gLnwL.cn
http://dul8cgcT.gLnwL.cn
http://5G4xsHB1.gLnwL.cn
http://yMXJjOM3.gLnwL.cn
http://lByyScLV.gLnwL.cn
http://www.dtcms.com/a/374750.html

相关文章:

  • jmeter使用指南
  • 硬件(六)arm指令
  • 后端错误处理的艺术:BusinessException 与 ResultUtils 的完美分工
  • MCU、CPLD、DSP、FPGA 有什么区别,该如何选择?
  • 【React Native】点赞特效动画组件FlowLikeView
  • android studio gradle 访问不了
  • 【C++】C++11 篇二
  • Kubernetes 配置检查与发布安全清单
  • Perforce Klocwork 2025.2版本更新:默认启用现代分析引擎、支持 MISRA C:2025 新规、CI构建性能提升等
  • 工业总线协议转换核心:SG-DP_MOD-110 Profibus-DP 转 Modbus-RTU 网关,打通异构设备数据链路
  • Win系统下配置PCL库第三步之链接库的路径(超详细)
  • 【远程运维】Linux 远程连接 Windows 好用的软件:MobaXterm 实战指南
  • Java入门级教程13-多线程同步安全机制synchronized(内置锁)、JavaMail发送电子邮箱、爬取CSDN到邮箱、备份数据库
  • 玩转Docker | 使用Docker部署KissLists任务管理工具
  • STL库——map/set(类函数学习)
  • STM32 串口接收数据包(自定义帧头帧尾)
  • 正向代理,反向代理,负载均衡还有nginx
  • 用户态与内核态的深度解析:安全、效率与优化之道
  • 搭建本地gitea服务器
  • ArcGIS JSAPI 高级教程 - 倾斜摄影数据开启透明(修改源码)
  • 输电线路分布式故障监测装置技术解析
  • 概率论第四讲—随机变量的数字特征
  • 学习stm32 蓝牙
  • 数据库学习MySQL系列2、Windows11系统安装MySQL方法一.msi安装详细教程
  • STM32物联网项目---ESP8266微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制---代码篇(四)
  • 北京鲁成伟业 | 三屏加固笔记本电脑C156F3
  • 从0~1搭建技术团队的思路
  • 如何在 Unity3D 中实现圆角效果?
  • LeetCode 面试经典 150 题:多数元素(摩尔投票法详解 + 多解法对比)
  • CStringArray 和 CStringList