Dec 9, 2014

How to Listen to Property Changes of Items of an ObservableCollection

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
{
    /// <summary>
    ///     This class adds the ability to refresh the list when any property of
    ///     the objects changes in the list which implements the INotifyPropertyChanged. 
    /// </summary>
    /// <typeparam name="T">
    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));
        }
    }
}

0 comments:

Post a Comment

Nam Le © 2014 - Designed by Templateism.com, Distributed By Templatelib