Introduction
This tip describes how to make use of ObservableCollection
to reflect changes occurring in the properties of its items.
Background
ObservableCollection
is a part of the life of the WPF community. Most of us are aware that this collection listens to changes occurring in the collection, like an item being added or removed from it.
However, many beginners stumble when they are required to listen to changes occurring in the items of the ObservableCollection
. It comes to them as a surprise that their changes do not reflect even after they implement the most famous INotifyPropertyChanged
interface on their classes.
Community has solved this issue already. However, I feel that the solution is scattered in bits and pieces. So, here I have tried to put it in a structured way.
Using the Code
Create a class with the given code and make sure you are referring to this namespace. Or you may like to modify the namespace as per your naming conventions.
I call this class ItemsChangeObservableCollection
.
It is inherited from ObservableCollection
. So the usage of this class is just like ObservableCollection
, however, it only allows classes that implement the INotifyPropertyChanged
interface.
This class is simple but powerful. It simply registers to the PropertyChanged
event of the item and calls OnCollectionChanged
of the ObservableCollection
when the item raises the PropertyChanged
event.
As usual, one should be very careful about the memory leaks when there are event subscriptions involved. So, make sure to call the Clear()
method once you are done with your collection allowing GC
to collect it.
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Collections;
namespace VJCollections
{
public class ItemsChangeObservableCollection<T> :
ObservableCollection<T> where T : INotifyPropertyChanged
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
RegisterPropertyChanged(e.NewItems);
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
UnRegisterPropertyChanged(e.OldItems);
}
else if (e.Action == NotifyCollectionChangedAction.Replace)
{
UnRegisterPropertyChanged(e.OldItems);
RegisterPropertyChanged(e.NewItems);
}
base.OnCollectionChanged(e);
}
protected override void ClearItems()
{
UnRegisterPropertyChanged(this);
base.ClearItems();
}
private void RegisterPropertyChanged(IList items)
{
foreach (INotifyPropertyChanged item in items)
{
if (item != null)
{
item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
private void UnRegisterPropertyChanged(IList items)
{
foreach (INotifyPropertyChanged item in items)
{
if (item != null)
{
item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
}