Tuesday, January 22, 2013

How to Avoid Firing ObservableCollection.CollectionChanged Multiple Times When Replacing All Elements Or Adding a Collection of Elements

How to Avoid Firing ObservableCollection.CollectionChanged Multiple Times When Replacing All Elements Or Adding a Collection of Elements

I have ObservableCollection<T> collection, and I want to replace all elements with a new collection of elements, I could do:

collection.Clear(); // OR: collection.ClearItems(), BTW, what's the difference between these two methods?

Using foreach to collection.Add one by one; // but this will fire multiple times

Same when adding a collection of elements.

EDIT:

I found a good library: http://www.codeproject.com/Articles/249311/Enhanced-ObservableCollection But it seems that it does NOT support silverlight.

Answers & Comments...

Answer: 1

You can achieve this by subclassing ObservableCollection and implementing your own ReplaceAll method. The implementation of this methods would replace all the items within the internal Items property, then fire a CollectionChanged event. Likewise, you can add an AddRange method. For an implementation of this, see the answer to this question:

ObservableCollection Doesn't support AddRange method, so I get notified for each item added, besides what about INotifyCollectionChanging?

The difference between Collection.Clear and Collection.ClearItems is that Clear is a public API method, whereas ClearItems is protected, it is an extension point that allows your to extend / modify the behaviour of Clear.

by : ColinEhttp://stackoverflow.com/users/249933

Answer: 2

ColinE is right with all his informations. I only want to add my subclass of ObservableCollection that i use for this specific case.

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);     } } 
by : Jehofhttp://stackoverflow.com/users/83039

Answer: 3

Here is what I implemented for other folks' reference:

// 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));     } } 
by : Peter Leehttp://stackoverflow.com/users/301336




No comments:

Post a Comment

Send us your comment related to the topic mentioned on the blog