绑定CollectionView的ObservableCollection连续Add操作触发异常原因及解决
Collection was modified: enumeration operation may not execute 异常原因及解决方法
问题描述
我编写了如下C#代码:
private void UpdateItems(FocusedItemData data) { items.Clear(); var newItems = new List<ItemViewModel>(); // (...) foreach (var item in newItems) items.Add(item); // (...) }
其中items是绑定到CollectionView的ObservableCollection<ItemViewModel>。在调用第二次items.Add时,触发InvalidOperationException,错误信息为:
Collection was modified: enumeration operation may not execute.
异常原因
核心原因是绑定的CollectionView正在枚举ObservableCollection的过程中,集合结构被修改:
ObservableCollection每次执行Add/Clear都会触发CollectionChanged事件,绑定它的CollectionView会立即响应该事件,重新枚举集合以更新UI或执行过滤、排序逻辑。- 第一次
Add触发事件后,CollectionView可能仍在遍历items集合,此时第二次Add直接改变了集合元素数量,导致正在进行的枚举操作失效,进而抛出该异常。
解决方法
方法1:用CollectionView的DeferRefresh批量更新
通过DeferRefresh()创建延迟更新范围,该范围内的所有集合修改只会触发一次CollectionView更新,避免中间的枚举冲突:
private void UpdateItems(FocusedItemData data) { using (itemsView.DeferRefresh()) // itemsView为绑定的CollectionView实例 { items.Clear(); var newItems = new List<ItemViewModel>(); // (...) foreach (var item in newItems) items.Add(item); } }
方法2:直接替换ObservableCollection实例
如果items属性实现了INotifyPropertyChanged,可以直接替换整个集合实例,避免逐行修改带来的多次变更通知:
private void UpdateItems(FocusedItemData data) { var newItems = new List<ItemViewModel>(); // (...) items = new ObservableCollection<ItemViewModel>(newItems); OnPropertyChanged(nameof(items)); // 触发属性变更通知 }
方法3:为ObservableCollection实现AddRange扩展
自定义AddRange扩展方法,批量添加元素时仅触发一次CollectionChanged事件:
public static class ObservableCollectionExtensions { public static void AddRange<T>(this ObservableCollection<T> collection, IEnumerable<T> items) { foreach (var item in items) collection.Items.Add(item); collection.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } }
使用方式:
private void UpdateItems(FocusedItemData data) { items.Clear(); var newItems = new List<ItemViewModel>(); // (...) items.AddRange(newItems); }
内容的提问来源于stack exchange,提问作者Spook
相关产品推荐
相关产品推荐

