如何基于过滤属性实时汇总ObservableCollection中元素的指定属性值?
符合ReactiveUI/Dynamic Data规范的实时总和计算实现
需求场景
开发基于ReactiveUI/Dynamic Data框架的MVVM架构UI系统,展示Record列表:
- 每个Record包含
Name、Id、Size、ShouldBeIncluded(布尔值,标记是否纳入统计)属性 - 用户可通过复选框修改
ShouldBeIncluded值 - 需要实时更新
Total属性,始终为ShouldBeIncluded=true的所有Record的Size之和
规范实现代码
using DynamicData; using DynamicData.Binding; using ReactiveUI; using ReactiveUI.Fody.Helpers; using System.Diagnostics; using System.Reactive.Disposables; namespace ConsoleApp1; // 数据模型类 public class Record : ReactiveObject { public string Id { get; set; } = string.Empty; public string Name { get; set; } = string.Empty; [Reactive] public int Size { get; set; } = 0; [Reactive] public bool ShouldBeIncluded { get; set; } = false; public Record(string id, string name, int size) { Id = id; Name = name; Size = size; } } // 视图模型类 public class TestViewModel : ReactiveObject, IDisposable { private readonly SourceCache<Record, string> _recordCache; private readonly ReadOnlyObservableCollection<Record> _records; private readonly ObservableAsPropertyHelper<int> _total; private readonly CompositeDisposable _disposables = new(); // 供UI绑定的只读列表 public ReadOnlyObservableCollection<Record> Records => _records; // 实时计算的总和,自动更新 public int Total => _total.Value; public TestViewModel() { // 使用SourceCache作为数据源,比ObservableCollection更适配Dynamic Data场景 _recordCache = new SourceCache<Record, string>(x => x.Id); _recordCache.Connect() // 监听每个Record的ShouldBeIncluded和Size属性变化 .AutoRefresh(x => x.ShouldBeIncluded) .AutoRefresh(x => x.Size) // 过滤出需要纳入统计的项 .Filter(x => x.ShouldBeIncluded) // 提取Size值用于求和 .Transform(x => x.Size) // 计算总和,初始值为0 .Sum() // 把结果绑定到_total属性,自动通知UI更新 .ToProperty(this, x => x.Total, out _total, initialValue: 0) .DisposeWith(_disposables); // 将缓存数据转为只读ObservableCollection供UI绑定 _recordCache.Connect() .Bind(out _records) .Subscribe() .DisposeWith(_disposables); } // 添加记录的方法 public void AddRecord(Record record) { _recordCache.AddOrUpdate(record); } public void Dispose() { _disposables.Dispose(); _recordCache.Dispose(); } } public class Program { public static void Main(string[] args) { using var viewModel = new TestViewModel(); // 添加10条记录,每条Size为5 for (var i = 0; i < 10; i++) { viewModel.AddRecord(new Record(i.ToString(), $"Record {i}", 5)); } // 标记3条记录为纳入统计 viewModel.Records[0].ShouldBeIncluded = true; viewModel.Records[3].ShouldBeIncluded = true; viewModel.Records[5].ShouldBeIncluded = true; // 验证总和是否为15 Debug.Assert(viewModel.Total == 15); } }
关键实现说明
- 使用
SourceCache替代ObservableCollection:Dynamic Data推荐用SourceCache做数据源,提供更高效的变更跟踪和查询能力 AutoRefresh:监听Record的ShouldBeIncluded和Size属性变化,确保属性变更时自动触发重新计算Filter:筛选出ShouldBeIncluded=true的记录Transform:提取需要求和的Size属性Sum():Dynamic Data内置聚合操作,实时计算总和ToProperty:将Observable结果转为ObservableAsPropertyHelper,自动实现INotifyPropertyChanged,符合MVVM规范,无需手动触发属性变更通知- 使用
CompositeDisposable管理订阅生命周期,避免内存泄漏
内容的提问来源于stack exchange,提问作者ProbablePrime
相关产品推荐
相关产品推荐

