Cómo evitar disparar ObservableCollection.CollectionChangedChanged Multiple Times al reemplazar todos los elementos o agregar una colección de elementos

Tengo la colección ObservableCollection , y quiero reemplazar todos los elementos con una nueva colección de elementos, podría hacer:

 collection.Clear(); 

O:

 collection.ClearItems(); 

(Por cierto, ¿cuál es la diferencia entre estos dos métodos?)

También podría usar foreach para la collection.Add foreach uno por uno, pero esto disparará varias veces

Lo mismo cuando se agrega una colección de elementos.

EDITAR:

Encontré una buena biblioteca aquí: ObservableCollection mejorado con la capacidad de retrasar o deshabilitar las notificaciones, pero parece que NO es compatible con Silverlight.

ColinE tiene razón con todas sus informaciones. Solo quiero agregar mi subclase de ObservableCollection que uso para este caso específico.

 public class SmartCollection : ObservableCollection { public SmartCollection() : base() { } public SmartCollection(IEnumerable collection) : base(collection) { } public SmartCollection(List list) : base(list) { } public void AddRange(IEnumerable 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 range) { this.Items.Clear(); AddRange(range); } } 

Puede lograr esto al ReplaceAll una subclase de ObservableCollection e implementar su propio método ReplaceAll . La implementación de estos métodos reemplazará todos los elementos dentro de la propiedad de Items internos, luego activará un evento CollectionChanged . Del mismo modo, puede agregar un método AddRange . Para una implementación de esto, vea la respuesta a esta pregunta:

ObservableCollection No es compatible con el método AddRange, por lo que me notifican cada elemento agregado, además de INotifyCollectionChanging?

La diferencia entre Collection.Clear y Collection.ClearItems es que Clear es un método API público, mientras que ClearItems está protegido, es un punto de extensión que le permite extender / modificar el comportamiento de Clear .

Esto es lo que implementé para la referencia de otras personas:

 // 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 : ObservableCollection { public ObservableCollectionFast() : base() { } public ObservableCollectionFast(IEnumerable collection) : base(collection) { } public ObservableCollectionFast(List list) : base(list) { } public virtual void AddRange(IEnumerable 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 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() { item }); } public virtual void Reset(IEnumerable collection) { if (collection.IsNullOrEmpty() && this.Items.IsNullOrEmpty()) return; // Step 0: Check if collection is exactly same as this.Items if (IEnumerableUtils.Equals(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)); } } 

Todavía no puedo comentar las respuestas anteriores, por lo que agrego aquí una adaptación de RemoveRange de las implementaciones de SmartCollection anteriores que no arrojarán una C # InvalidOperationException: la colección se modificó. Utiliza un predicado para comprobar si el elemento debe eliminarse, lo que, en mi caso, es más óptimo que crear un subconjunto de elementos que cumplan los criterios de eliminación.

 public void RemoveRange(Predicate 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)); } 

Ejemplo:

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

En los últimos años, estoy usando una solución más genérica para eliminar demasiadas notificaciones de ObservableCollection creando una operación de cambio de lote y notificando a los observadores con una acción de reinicio:

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

Usarlo es sencillo:

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

Llamar a Execute generará una sola notificación pero con un inconveniente: la lista se actualizará en la UI como un todo, no solo como elementos modificados. Esto lo hace perfecto para los artículos. Borre () seguido de artículos. AgregueRango (nuevos artículos).