Observing changes to a List<T> 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 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) {
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
6 responses
How does this compare to using BindingList<T>? It already had list changed events built in.
I smell a Decorator pattern :)
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
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!
Hey Damien, In case you are looking for a good example of the Decorator pattern, you can check out the article I wrote.
Good stuff guys.
[)amien