Observing changes to a List by adding events

In an attempt to get more C# and .NET content up I’m putting up some snippets I’ve put together in response to questions on some C# user support groups. Many of them are not particularly advanced but they are quite useful.

GitHub has the latest version of ObservableList</a> </p> This sample shows how to observe events on an generic IList. It does this by way of implementing the IList interface over the top of something that already supports IList to do the actual work and highlights how useful publishing the interface, IList, separate from the actual concrete class List can be for reuse. ```csharp using System; using System.Collections; using System.Collections.Generic; public class ObservableList : IList { private IList internalList; public class ListChangedEventArgs : EventArgs { public int index; public T item; public ListChangedEventArgs(int index, T item) { this.index = index; this.item = item; } } public delegate void ListChangedEventHandler(object source, ListChangedEventArgs e); public delegate void ListClearedEventHandler(object source, EventArgs e); public event ListChangedEventHandler ListChanged; public event ListClearedEventHandler ListCleared; public ObservableList() { internalList = new List(); } public ObservableList(IList list) { internalList = list; } public ObservableList(IEnumerable collection) { internalList = new List(collection); } protected virtual void OnListChanged(ListChangedEventArgs e) { if (ListChanged != null) ListChanged(this, e); } protected virtual void OnListCleared(EventArgs e) { if (ListCleared != null) ListCleared(this, e); } public int IndexOf(T item) { return internalList.IndexOf(item); } public void Insert(int index, T item) { internalList.Insert(index, item); OnListChanged(new ListChangedEventArgs(index, item)); } public void RemoveAt(int index) { T item = internalList[index]; internalList.Remove(item); OnListChanged(new ListChangedEventArgs(index, item)); } public T this[int index] { get { return internalList[index]; } set { internalList[index] = value; OnListChanged(new ListChangedEventArgs(index, value)); } } public void Add(T item) { internalList.Add(item); OnListChanged(new ListChangedEventArgs(internalList.IndexOf(item), item)); } public void Clear() { internalList.Clear(); OnListCleared(new EventArgs()); } public bool Contains(T item) { return internalList.Contains(item); } public void CopyTo(T[] array, int arrayIndex) { internalList.CopyTo(array, arrayIndex); } public int Count { get { return internalList.Count; } } public bool IsReadOnly { get { return internalList.IsReadOnly; } } public bool Remove(T item) { lock(this) { int index = internalList.IndexOf(item); if (internalList.Remove(item)) { OnListChanged(new ListChangedEventArgs(index, item)); return true; } else return false; } } public IEnumerator GetEnumerator() { return internalList.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable) internalList).GetEnumerator(); } } ``` _[)amien_

8 responses

  1. Avatar for Will Gant

    How does this compare to using BindingList<T>? It already had list changed events built in.

    Will Gant – June 14th, 2006
  2. Avatar for Steve

    I smell a Decorator pattern :)

    Steve June 15th, 2006
  3. Avatar for Damien Guard

    BindingList does indeed have events but it’s primary purpose is for data binding and the events and sorting are exposed as part of that. I was going to put that it was an illustration of the decorator pattern but couldn’t find a decent decorator reference online to link to and didn’t feel like trying to cover that myself.

    Damien Guard June 15th, 2006
  4. Avatar for Steve Harman

    Damien, This is good stuff, as always! Oh, and I even found you a link to a pretty decent high-level explaination of the Decorator Pattern. Gotta’ love Wikipedia!

    Steve Harman June 15th, 2006
  5. Avatar for Jean-Paul S. Boodhoo

    Hey Damien, In case you are looking for a good example of the Decorator pattern, you can check out the article I wrote.

    Jean-Paul S. Boodhoo June 16th, 2006
  6. Avatar for Damien Guard

    Good stuff guys.

    Damien Guard August 2nd, 2007
  7. Avatar for Mo Samara

    Nice one Damien, your CopyTo implementation should be internalList.CopyTo though, other wise you will get the nice StackOverflow exception.

    Mo Samara – February 19th, 2011
  8. Avatar for Sailor

    Great post. Thank you for this. One note that took me several hours to realize. The RemoveAt(int index) method is broken. Passing any index, it will remove the item starting at the beginning index of 0. I remember there being a bug in .Net 2.0 regarding this and I think they fixed in later releases but since Unity only supports 2.0, felt it was worthwhile to mention. If you find your list removing the wrong items when you call RemoveAt(), try replacing the RemoveAt method with the following:

     public void RemoveAt(int index) { T item = internalList[index]; internalList.RemoveAt(index); OnListChanged(new ListChangedEventArgs(index, item)); } 

    Thanks again. Works great!

    Sailor – July 12th, 2014