WPF 实现通知属性的N种方式
我对通知属性有种执念
- 要兼容低版本
- 要写起来短小精悍
- 要最好无依赖
- 哪有那么好的事情!
必备代码
所有衍生的方式都将基于此操作,你可以封装到类中继承
public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName) { PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
原始方式
这种方式,
- 你必须要接受一个不需要的后台变量
- 硬编码的名字写法
你也可以使用nameof进行操作,
但需要注意的是nameof是编译时的动作,不要让混淆器来捣乱
private int myVar; public int MyProperty { get { return myVar; } set { myVar = value; //通知绑定系统这个名字的属性有变化 RaisePropertyChanged("MyProperty"); } }
使用[CallerMemberName]
隐藏属性名字的主动编写
在RaisePropertyChanged
的属性上加上[CallerMemberName]
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
写法就变成了
private int myVar; public int MyProperty { get { return myVar; } set { myVar = value; //在编译时将会为第一个参数自动传入 "MyProperty" RaisePropertyChanged(); //如果没有.NET版本没有[CallerMemberName],也可以使用表达式树来操作,这个操作没有硬编码,是运行时操作 // RaisePropertyChanged(()=>MyProperty); } }
进一步干掉后台变量
通过基类将[CallerMemberName]的名字保存到后台统一字典来进行变量值的管理
这也是我目前使用的方法
public int MyProperty { get { return GetProperty(); } set { SetProperty(value); } }
优化成一行
public int MyProperty { get => GetProperty(); set => SetProperty(value); }
使用Emit动态生成
你的属性这样写
那个virtual表明了它的派生类可以覆盖它
public class MyViewModel : ViewModelBase { public virtual int MyProperty { get; set; } }
现在使用Emit动态创建一个派生类
在实例化后,返回给你基类,看似什么都没有发生,但你的属性却得到了通知能力
动态创建的代码类似下方所示:
public class NullViewModel_EXTEND : MyViewModel { private int myVar; public override int MyProperty { get { return myVar; } set { myVar = value; RaisePropertyChanged("MyProperty"); } } }
由于是动态创建的,可能调试会有些麻烦
此操作和js的代理一样,可以为原有对象注入拦截代码,只不过c#这个操作有亿点点麻烦
实现参考: https://www.codewrecks.com/post/old/2008/08/implement-inotifypropertychanged-with-dynamic-code-generation/
使用后台线程轮训检查属性值是否变化
这是我觉得最离谱的实现,但它能跑,怕什么!
地址: https://github.com/michael-damatov/shuriken
看起来还挺流畅
还有一种没太具体了解的方式
在maui早期mvu模式演示上见过
猜测这个State的参数里面也包含了CallerMemberName
然后重写ToString,重写隐式转换来达到使用起来像正常属性的操作
public class MyPage : View { readonly State<int> clickCount = new State<int> (1); public MyPage() { Body = () => new VStack { new Text (() => $"Click Count: {clickCount}"), new Button("Update Text", () => { clickCount.Value++; } }; } }
下面2种属于无需动手的操作
使用Fody/PropertyChanged插件
此插件会在你的代码编译后对属性进行自动通知注入
你只需要像正常编写属性一样
使用Source Generator自动生成
与Fody类似,可以在编译时为你自动生成通知代码,.NET Community Toolkit
携带此特性
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步