.NET MAUI中MVVM工具链:只读属性的PropertyChanged通知传递问题
问题背景
在.NET MAUI应用中,使用Mvvm.CommunityToolkit的源生成功能实现INotifyPropertyChanged,现有代码如下:
[ObservableProperty] [NotifyPropertyChangedFor(nameof(FullName))] private string? firstName; [ObservableProperty] [NotifyPropertyChangedFor(nameof(FullName))] private string? lastName; public string FullName => $"{FirstName} {LastName}"; public int NumberOfXsInName => FullName.Count(x => char.ToLower(x) == 'x');
FullName作为只读属性能正常触发UI绑定更新,但依赖FullName的NumberOfXsInName无法自动收到PropertyChanged通知。若给所有触发FullName更新的属性添加[NotifyPropertyChangedFor],不仅缺乏健壮性,也无法强制子类正确实现;手动在构造函数中订阅事件触发通知又过于繁琐。
实际场景中,需基于依赖数据源和过滤函数的只读属性FilteredCollection做分组,要求分组属性随FilteredCollection更新自动触发通知,需更简洁的实现方案。
解决方案
推荐方案:使用[DependsOn]属性定义依赖链
Mvvm.CommunityToolkit提供的[DependsOn]属性可直接标记只读属性的依赖项,源生成器会自动处理PropertyChanged通知的触发,无需手动添加NotifyPropertyChangedFor或订阅事件。
针对姓名示例的改造
[ObservableProperty] private string? firstName; [ObservableProperty] private string? lastName; // 标记FullName依赖FirstName和LastName [DependsOn(nameof(FirstName), nameof(LastName))] public string FullName => $"{FirstName} {LastName}"; // 标记NumberOfXsInName依赖FullName [DependsOn(nameof(FullName))] public int NumberOfXsInName => FullName.Count(x => char.ToLower(x) == 'x');
此时,FirstName或LastName变化时,会自动触发FullName的通知;FullName变化时,又会自动触发NumberOfXsInName的通知,完全无需手动干预。
针对分组场景的实现
假设存在DataSource和FilterPredicate两个可观察属性,FilteredCollection依赖二者,分组属性又依赖FilteredCollection,代码如下:
[ObservableProperty] private ObservableCollection<Item> dataSource; [ObservableProperty] private Func<Item, bool> filterPredicate; [DependsOn(nameof(DataSource), nameof(FilterPredicate))] public IReadOnlyCollection<Item> FilteredCollection => DataSource.Where(FilterPredicate).ToList(); [DependsOn(nameof(FilteredCollection))] public IEnumerable<IGrouping<string, Item>> GroupedItems => FilteredCollection.GroupBy(item => item.Category);
DataSource或FilterPredicate变化时,FilteredCollection自动触发通知;FilteredCollection更新后,GroupedItems也会自动触发UI绑定更新。
备选方案:利用生成的Partial方法扩展
若需更灵活的逻辑控制,可借助Mvvm.CommunityToolkit生成的On[PropertyName]Changed partial方法,在属性变化时手动触发下游属性的通知:
[ObservableProperty] private string? firstName; [ObservableProperty] private string? lastName; public string FullName => $"{FirstName} {LastName}"; public int NumberOfXsInName => FullName.Count(x => char.ToLower(x) == 'x'); partial void OnFirstNameChanged(string? oldValue, string? newValue) { RaisePropertyChanged(nameof(FullName)); RaisePropertyChanged(nameof(NumberOfXsInName)); } partial void OnLastNameChanged(string? oldValue, string? newValue) { RaisePropertyChanged(nameof(FullName)); RaisePropertyChanged(nameof(NumberOfXsInName)); }
此方式适合需要在属性变化时执行额外逻辑的场景,但相比[DependsOn],代码冗余度更高。
总结
[DependsOn]属性是解决多层依赖只读属性通知问题的最优方案,它清晰定义了属性间的依赖关系,自动处理通知触发,大幅提升代码的健壮性和可维护性,尤其适合复杂的依赖链场景。
内容的提问来源于stack exchange,提问作者Hottemax

