冗余PropertyChanged事件优化咨询:无法修改用户控件时的高效方案
解决方案:避免不必要的PropertyChanged事件触发
针对你的问题,我来一步步分析和给出实用方案:
问题1:是否存在比临时Setter判断更优的方案?
因为你无法修改那个UserControl的行为——它每次关闭编辑模式都会强制调用Setter,所以没有特别完美的“绕过”方案。毕竟控件的逻辑是固定的,我们只能在ViewModel层面做拦截。
你当前的临时方案(在Setter里判断值是否变化)是最直接且可控的思路。其他替代方案比如尝试通过反射或事件拦截来过滤PropertyChanged事件,但这样反而会增加复杂度,不如直接在Setter层面处理来得可靠。
问题2:如果没有更优方案,如何避免属性代码重复?
当有大量属性时,逐个写判断逻辑确实很繁琐,这里有三个实用的方案帮你减少重复代码:
1. 封装通用的SetProperty基类
写一个ViewModel基类,把值判断和PropertyChanged触发的逻辑封装成泛型方法,所有ViewModel都继承这个基类,属性直接调用该方法即可:
using System.ComponentModel; using System.Runtime.CompilerServices; using System.Collections.Generic; public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; // 通用的属性设置方法,自动处理值判断和事件触发 protected bool SetProperty<T>(ref T backingField, T value, [CallerMemberName] string propertyName = null) { // 使用默认相等比较器判断值是否变化,支持引用类型和值类型 if (EqualityComparer<T>.Default.Equals(backingField, value)) return false; backingField = value; OnPropertyChanged(propertyName); return true; } protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
然后你的属性就可以简化成:
public class YourViewModel : ViewModelBase { private string _foo; public string Foo { get => _foo; set => SetProperty(ref _foo, value); } private int _bar; public int Bar { get => _bar; set => SetProperty(ref _bar, value); } }
这样每个属性只需要一行代码,不用重复写判断逻辑。
2. 使用Fody PropertyChanged(AOP编译时织入)
这是一个非常流行的工具,它会在编译阶段自动帮你生成属性的Setter逻辑,包括值判断和PropertyChanged触发,你只需要写简单的自动属性即可:
- 安装NuGet包:
Install-Package PropertyChanged.Fody - 在ViewModel类上添加
[AddINotifyPropertyChangedInterface]属性:
using PropertyChanged; [AddINotifyPropertyChangedInterface] public class YourViewModel { // 直接写自动属性,Fody会自动处理背后的字段、值判断和事件触发 public string Foo { get; set; } public int Bar { get; set; } public bool IsActive { get; set; } }
编译后,Fody会自动给每个属性生成带值判断的Setter逻辑,完全不用手动写字段和判断,非常适合大量属性的场景。
3. T4模板代码生成
如果你不想引入第三方库,可以用T4模板自动生成属性代码。比如创建一个.tt文件,定义好属性列表,模板会自动生成带有判断逻辑的属性代码:
示例T4模板片段:
<#@ template language="C#" #> <#@ output extension=".cs" #> using System.ComponentModel; public class YourViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; <# // 定义需要生成的属性列表 var properties = new[] { new { Type = "string", Name = "Foo" }, new { Type = "int", Name = "Bar" }, new { Type = "bool", Name = "IsActive" } }; foreach (var prop in properties) { var backingField = "_" + prop.Name.ToLower(); #> private <#= prop.Type #> <#= backingField #>; public <#= prop.Type #> <#= prop.Name #> { get => <#= backingField #>; set { if (<#= backingField #> == value) return; <#= backingField #> = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(<#= prop.Name #>))); } } <# } #> }
更新模板后会自动生成所有属性的代码,后续新增属性只需要在模板里添加即可。
内容的提问来源于stack exchange,提问作者Syrlia
相关产品推荐
相关产品推荐

