Archive for the ‘Observer’ Category

Lists that notify when items are added & removed

April 11, 2010

I often find myself wishing that collections such as List<T> could issue notifications when changes are made to the collection.

This could be particularly useful when you expose a collection as a property from a class. It is often desirable to perform some action on the class if the client changes the collection.

Averager averager = new Averager();
IList<double> numbers = averager.Numbers;

numbers.Add(10);
numbers.Add(5);

Assert.IsTrue(averager.Average == 7.5);

numbers.Add(32);

Assert.IsTrue(averager.Average == 15.666666666666666);

In the code above you can see that Averager passes out a standard list. We can modify the list externally and yet the averager object is kept up to date.

public class Averager {
    public Averager() {
        numbers = new ListNotifyChanged<double>();
        numbers.Changed += numbers_Changed;
    }

    public double Average { get; private set; }

    ListNotifyChanged<double> numbers = null;
    public IList<double> Numbers { get { return numbers; } }

    void numbers_Changed( object sender, ListNotifyChanged<double>.ChangedEventArgs e ) {
        Average = numbers.Average();
    }
}

The ListNofityChanged<T> class is a collection class that closely resembles the List<T> class. However, it fires change notifications events when items are added or removed from the list.

Here is the full implementation of ListNofityChanged<T>

/// <summary>
/// A generic list that will notify when there are
/// changes made to the list.
/// </summary>
public class ListNotifyChanged<T> : IList<T> {
    IList<T> list = new List<T>();

    public ListNotifyChanged() { }

    public ListNotifyChanged( int capacity ) {
        list = new List<T>(capacity);
    }

    public ListNotifyChanged( IList<T> list ) {
        this.list = list;
    }

    public ListNotifyChanged( IEnumerable<T> collection ) {
        list = new List<T>(collection);
    }

    public event EventHandler<ListChangedEventArgs> Changed;
    public event EventHandler<EventArgs> Cleared;

    protected virtual void OnChanged( ListChangedEventArgs e ) {
        if (Changed != null) Changed(this, e);
    }

    protected virtual void OnCleared() {
        if (Cleared != null) Cleared(this, new EventArgs());
    }

    #region IList<T> Members

    public T this[int index] {
        get { return list[index]; }
        set {
            list[index] = value;
            OnChanged(new ListChangedEventArgs(index, value));
        }
    }

    public int IndexOf( T item ) {
        return list.IndexOf(item);
    }

    public void Insert( int index, T item ) {
        list.Insert(index, item);
        OnChanged(new ListChangedEventArgs(index, item));
    }

    public void RemoveAt( int index ) {
        T item = list[index];
        list.Remove(item);
        OnChanged(new ListChangedEventArgs(index, item));
    }

    #endregion

    #region ICollection<T> Members
    public void Add( T item ) {
        list.Add(item);
        OnChanged(new ListChangedEventArgs(list.IndexOf(item), item));
    }

    public void Clear() {
        list.Clear();
        OnCleared();
    }

    public bool Contains( T item ) {
        return list.Contains(item);
    }

    public void CopyTo( T[] array, int arrayIndex ) {
        list.CopyTo(array, arrayIndex);
    }

    public int Count {
        get { return list.Count; }
    }

    public bool IsReadOnly {
        get { return list.IsReadOnly; }
    }

    public bool Remove( T item ) {
        int index = list.IndexOf(item);
        if (list.Remove(item)) {
            OnChanged(new ListChangedEventArgs(index, item));
            return true;
        }
        else
            return false;
    }
    #endregion

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator() {
        return list.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

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

    #endregion

    public class ListChangedEventArgs : EventArgs {
        public int index;
        public T item;
        public ListChangedEventArgs( int index, T item ) {
            this.index = index;
            this.item = item;
        }
    }
}

Advertisements