Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ObservableCollection that also monitors changes on the elements in collection

Tags:

c#

collections

Is there a collection (BCL or other) that has the following characteristics:

Sends event if collection is changed AND sends event if any of the elements in the collection sends a PropertyChanged event. Sort of an ObservableCollection<T> where T: INotifyPropertyChanged and the collection is also monitoring the elements for changes.

I could wrap an observable collection my self and do the event subscribe/unsubscribe when elements in the collection are added/removed but I was just wondering if any existing collections did this already?

like image 387
soren.enemaerke Avatar asked Nov 06 '08 15:11

soren.enemaerke


People also ask

What is an ObservableCollection?

An ObservableCollection is a dynamic collection of objects of a given type. Objects can be added, removed or be updated with an automatic notification of actions. When an object is added to or removed from an observable collection, the UI is automatically updated.

What is ObservableCollection in xamarin forms?

If you want the ListView to automatically update as items are added, removed and changed in the underlying list, you'll need to use an ObservableCollection . ObservableCollection is defined in System.Collections.ObjectModel and is just like List , except that it can notify ListView of any changes: C# Copy.


1 Answers

Made a quick implementation myself:

public class ObservableCollectionEx<T> : ObservableCollection<T> where T : INotifyPropertyChanged {     protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)     {         Unsubscribe(e.OldItems);         Subscribe(e.NewItems);         base.OnCollectionChanged(e);     }      protected override void ClearItems()     {         foreach(T element in this)             element.PropertyChanged -= ContainedElementChanged;          base.ClearItems();     }      private void Subscribe(IList iList)     {         if (iList != null)         {             foreach (T element in iList)                 element.PropertyChanged += ContainedElementChanged;         }     }      private void Unsubscribe(IList iList)     {         if (iList != null)         {             foreach (T element in iList)                 element.PropertyChanged -= ContainedElementChanged;         }     }      private void ContainedElementChanged(object sender, PropertyChangedEventArgs e)     {         OnPropertyChanged(e);     } } 

Admitted, it would be kind of confusing and misleading to have the PropertyChanged fire on the collection when the property that actually changed is on a contained element, but it would fit my specific purpose. It could be extended with a new event that is fired instead inside ContainerElementChanged

Thoughts?

EDIT: Should note that the BCL ObservableCollection only exposes the INotifyPropertyChanged interface through an explicit implementation so you would need to provide a cast in order to attach to the event like so:

ObservableCollectionEx<Element> collection = new ObservableCollectionEx<Element>(); ((INotifyPropertyChanged)collection).PropertyChanged += (x,y) => ReactToChange(); 

EDIT2: Added handling of ClearItems, thanks Josh

EDIT3: Added a correct unsubscribe for PropertyChanged, thanks Mark

EDIT4: Wow, this is really learn-as-you-go :). KP noted that the event was fired with the collection as sender and not with the element when the a contained element changes. He suggested declaring a PropertyChanged event on the class marked with new. This would have a few issues which I'll try to illustrate with the sample below:

  // work on original instance   ObservableCollection<TestObject> col = new ObservableCollectionEx<TestObject>();   ((INotifyPropertyChanged)col).PropertyChanged += (s, e) => { Trace.WriteLine("Changed " + e.PropertyName); };    var test = new TestObject();   col.Add(test); // no event raised   test.Info = "NewValue"; //Info property changed raised    // working on explicit instance   ObservableCollectionEx<TestObject> col = new ObservableCollectionEx<TestObject>();   col.PropertyChanged += (s, e) => { Trace.WriteLine("Changed " + e.PropertyName); };    var test = new TestObject();   col.Add(test); // Count and Item [] property changed raised   test.Info = "NewValue"; //no event raised 

You can see from the sample that 'overriding' the event has the side effect that you need to be extremely careful of which type of variable you use when subscribing to the event since that dictates which events you receive.

like image 72
soren.enemaerke Avatar answered Sep 20 '22 10:09

soren.enemaerke