如何避免在replace所有元素或添加元素集时多次触发ObservableCollection.CollectionChanged

我有ObservableCollection<T>集合,我想用新的元素集合来replace所有元素,我可以这样做:

 collection.Clear(); 

要么:

 collection.ClearItems(); 

(顺便说一句,这两种方法有什么区别?)

我也可以使用foreachcollection.Add一个接一个地添加,但是会多次触发

添加元素集合时也是如此。

编辑:

我在这里find了一个好的库: 增强的ObservableCollection能够延迟或禁用通知,但它似乎不支持silverlight。

ColinE对他所有的信息都是正确的。 我只想添加我用于此特定情况的ObservableCollection子类。

 public class SmartCollection<T> : ObservableCollection<T> { public SmartCollection() : base() { } public SmartCollection(IEnumerable<T> collection) : base(collection) { } public SmartCollection(List<T> list) : base(list) { } public void AddRange(IEnumerable<T> range) { foreach (var item in range) { Items.Add(item); } this.OnPropertyChanged(new PropertyChangedEventArgs("Count")); this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } public void Reset(IEnumerable<T> range) { this.Items.Clear(); AddRange(range); } } 

您可以通过inheritanceObservableCollection并实现您自己的ReplaceAll方法来实现此目的。 这个方法的实现将取代内部Items属性中的所有项目,然后触发一个CollectionChanged事件。 同样,你可以添加一个AddRange方法。 为了实现这个,请参阅这个问题的答案:

ObservableCollection不支持AddRange方法,所以我得到通知每个项目添加,除了INotifyCollectionChanging?

Collection.ClearCollection.ClearItems之间的区别在于Clear是一个公共API方法,而ClearItems是受保护的,它是一个扩展点,允许您扩展/修改Clear的行为。

以下是我为其他人参考的内容:

 // http://stackoverflow.com/questions/13302933/how-to-avoid-firing-observablecollection-collectionchanged-multiple-times-when-r // http://stackoverflow.com/questions/670577/observablecollection-doesnt-support-addrange-method-so-i-get-notified-for-each public class ObservableCollectionFast<T> : ObservableCollection<T> { public ObservableCollectionFast() : base() { } public ObservableCollectionFast(IEnumerable<T> collection) : base(collection) { } public ObservableCollectionFast(List<T> list) : base(list) { } public virtual void AddRange(IEnumerable<T> collection) { if (collection.IsNullOrEmpty()) return; foreach (T item in collection) { this.Items.Add(item); } this.OnPropertyChanged(new PropertyChangedEventArgs("Count")); this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); // Cannot use NotifyCollectionChangedAction.Add, because Constructor supports only the 'Reset' action. } public virtual void RemoveRange(IEnumerable<T> collection) { if (collection.IsNullOrEmpty()) return; bool removed = false; foreach (T item in collection) { if (this.Items.Remove(item)) removed = true; } if (removed) { this.OnPropertyChanged(new PropertyChangedEventArgs("Count")); this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); // Cannot use NotifyCollectionChangedAction.Remove, because Constructor supports only the 'Reset' action. } } public virtual void Reset(T item) { this.Reset(new List<T>() { item }); } public virtual void Reset(IEnumerable<T> collection) { if (collection.IsNullOrEmpty() && this.Items.IsNullOrEmpty()) return; // Step 0: Check if collection is exactly same as this.Items if (IEnumerableUtils.Equals<T>(collection, this.Items)) return; int count = this.Count; // Step 1: Clear the old items this.Items.Clear(); // Step 2: Add new items if (!collection.IsNullOrEmpty()) { foreach (T item in collection) { this.Items.Add(item); } } // Step 3: Don't forget the event if (this.Count != count) this.OnPropertyChanged(new PropertyChangedEventArgs("Count")); this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } } 

我不能评论以前的答案,所以我在这里添加一个RemoveRange适应上面的SmartCollection实现不会抛出一个C#InvalidOperationException:集合被修改。 它使用谓词来检查项目是否应该被删除,在我的情况下,比创build符合删除标准的项目子集更优化。

 public void RemoveRange(Predicate<T> remove) { // iterates backwards so can remove multiple items without invalidating indexes for (var i = Items.Count-1; i > -1; i--) { if (remove(Items[i])) Items.RemoveAt(i); } this.OnPropertyChanged(new PropertyChangedEventArgs("Count")); this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } 

例:

 LogEntries.RemoveRange(i => closeFileIndexes.Contains(i.fileIndex)); 

在过去的几年中,我使用更通用的解决scheme,通过创build批量更改操作并通知复位操作的观察者来消除太多的ObservableCollection通知:

 public class ExtendedObservableCollection<T>: ObservableCollection<T> { public ExtendedObservableCollection() { } public ExtendedObservableCollection(IEnumerable<T> items) : base(items) { } public void Execute(Action<IList<T>> itemsAction) { itemsAction(Items); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } } 

使用它很简单:

 var collection = new ExtendedObservableCollection<string>(new[] { "Test", "Items", "Here" }); collection.Execute(items => { items.RemoveAt(1); items.Insert(1, "Elements"); items.Add("and there"); }); 

调用执行会生成一个单一的通知,但有一个缺点 – 列表将在UI中作为一个整体进行更新,而不仅仅是修改的元素。 这使得它完美的items.Clear()后面items.AddRange(newItems)。