WINUI+MVVM中每次实例化ObservableCollection做过滤是否合规?
关于CRUD ViewModel中过滤操作的实践建议
直接new ObservableCollection的优缺点
优点
- 实现简单:不用处理集合内项的增删逻辑,直接替换整个集合就能完成过滤,快速实现功能。
缺点
- 性能与GC压力:频繁创建新集合会增加垃圾回收负担,数据量较大时,销毁旧集合、创建新集合的开销会逐渐显现,甚至导致UI短暂卡顿。
- 丢失UI状态:替换集合后,Grid的选中项、滚动位置等状态会被重置,用户体验较差。
- 编辑冲突风险:如果用户正在编辑某条记录,替换集合会直接中断编辑流程,未保存的修改可能丢失。
这种做法不属于良好实践,仅适合数据量极小、对体验要求不高的场景。
推荐的替代方案
方案1:复用现有ObservableCollection,手动更新内容
保留同一个集合实例,通过清空、添加过滤后的项实现过滤,减少对象创建开销。数据量较大时,建议用批量操作(比如实现AddRange扩展方法)减少PropertyChanged事件触发次数,避免UI频繁刷新。
修改后的DoSearch示例:
private async Task DoSearch(string textToSearch) { var filteredPeople = await _context.People.Where(x => x.Cognome.Contains(textToSearch)).ToListAsync(); // 清空现有集合 people.Clear(); // 批量添加过滤后的项 foreach (var person in filteredPeople) { people.Add(person); } // ObservableCollection的增删会自动通知UI,无需手动触发OnPropertyChanged }
方案2:使用ICollectionView实现UI层面过滤(推荐)
利用WPF/UWP提供的ICollectionView(或CollectionViewSource),在原集合基础上做过滤,无需修改集合本身。这种方式既能保留原集合的所有数据,又能实现动态过滤,同时保留UI状态。
步骤:
- 在ViewModel中添加
ICollectionView属性:
private ICollectionView _peopleView; public ICollectionView PeopleView { get => _peopleView; set => SetProperty(ref _peopleView, value); }
- 在构造函数中初始化View:
people = new ObservableCollection<Person>(_context.People); PeopleView = CollectionViewSource.GetDefaultView(people); // 初始化过滤规则 PeopleView.Filter = obj => { if (string.IsNullOrEmpty(_searchText)) return true; var person = obj as Person; return person?.Cognome.Contains(_searchText) ?? false; };
- 添加搜索文本属性,修改时刷新过滤:
private string _searchText = string.Empty; public string SearchText { get => _searchText; set { if (SetProperty(ref _searchText, value)) { PeopleView.Refresh(); } } }
- 修改
DoSearch(或直接绑定SearchText到输入框,无需命令):
private async Task DoSearch(string textToSearch) { SearchText = textToSearch; // 无需修改集合,View会自动过滤 }
针对“Grid中创建新记录并过滤后编辑保存”的适配
如果用ICollectionView方案:
- 新增记录时直接添加到
people集合,只要符合当前过滤条件,会自动显示在Grid中;编辑后调用SaveCommand即可保存到数据库,完全不影响过滤状态。 - 若新增记录不符合当前过滤条件,会保留在原集合中但不显示,保存后依然存在于数据库,后续搜索时会正常展示。
如果用复用集合的方案:
- 新增记录时,先添加到
_context.People,再判断是否符合当前过滤条件,符合则添加到people集合,不符合则仅保留在上下文,保存后下次搜索会显示。
额外代码优化建议
- 在
DoSearch中,应通过People属性的SetProperty赋值,而非直接修改字段后手动触发OnPropertyChanged,符合MVVM规范:
// 原写法 people = new ObservableCollection<Person>(...); OnPropertyChanged(nameof(People)); // 优化后 People = new ObservableCollection<Person>(await _context.People.Where(...).ToListAsync());
内容的提问来源于stack exchange,提问作者Luca C.
相关产品推荐
相关产品推荐

