Observing changes to a List<T> by adding events

June 2006 – February 2011 .NET (, , , ) • 5,604 views • 7 responses

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

This sample shows how to observe events on an generic IList<T>. It does this by way of implementing the IList<T> interface over the top of something that already supports IList<T> to do the actual work and highlights how useful publishing the interface, IList<T>, separate from the actual concrete class List<T> can be for reuse.

using System;
using System.Collections;
using System.Collections.Generic;

public class ObservableList<T> : IList<T> {
  private IList<T> 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<T>();
  }

  public ObservableList(IList<T> list) {
    internalList = list;
  }

  public ObservableList(IEnumerable<T> collection) {
    internalList = new List<T>(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));
  }

  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 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<T> GetEnumerator() {
    return internalList.GetEnumerator();
  }

  IEnumerator IEnumerable.GetEnumerator() {
    return ((IEnumerable) internalList).GetEnumerator();
  }
}

[)amien

7 responses  

  1. Will Gant on June 14th, 2006

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

  2. Steve on June 15th, 2006

    I smell a Decorator pattern :)

  3. Damien Guard on June 15th, 2006

    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.

    [)amien

  4. Steve Harman on June 15th, 2006

    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!

  5. Jean-Paul S. Boodhoo on June 16th, 2006

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

  6. Damien Guard on August 2nd, 2007

    Good stuff guys.

    [)amien

  7. Mo Samara on February 19th, 2011

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

Leave your response

  1. (kept private)