How to handle add to list event?

Viewed 80256

I have a list like this:

List<Controls> list = new List<Controls>

How to handle adding new position to this list?

When I do:

myObject.myList.Add(new Control());

I would like to do something like this in my object:

myList.AddingEvent += HandleAddingEvent

And then in my HandleAddingEvent delegate handling adding position to this list. How should I handle adding new position event? How can I make this event available?

10 Answers

To be clear: If you only need to observe the standard-functionalities you should use ObservableCollection(T) or other existing classes. Never rebuild something you already got.

..But.. If you need special events and have to go deeper, you should not derive from List! If you derive from List you can not overloead Add() in order to see every add.

Example:

public class MyList<T> : List<T>
{
    public void Add(T item) // Will show us compiler-warning, because we hide the base-mothod which still is accessible!
    {
        throw new Exception();
    }
}

public static void Main(string[] args)
{
    MyList<int> myList = new MyList<int>(); // Create a List which throws exception when calling "Add()"
    List<int> list = myList; // implicit Cast to Base-class, but still the same object

    list.Add(1);              // Will NOT throw the Exception!
    myList.Add(1);            // Will throw the Exception!
}

It's not allowed to override Add(), because you could mees up the functionalities of the base class (Liskov substitution principle).

But as always we need to make it work. But if you want to build your own list, you should to it by implementing the an interface: IList<T>.

Example which implements a before- and after-add event:

public class MyList<T> : IList<T>
{
    private List<T> _list = new List<T>();

    public event EventHandler BeforeAdd;
    public event EventHandler AfterAdd;

    public void Add(T item)
    {
        // Here we can do what ever we want, buffering multiple events etc..
        BeforeAdd?.Invoke(this, null);
        _list.Add(item);
        AfterAdd?.Invoke(this, null);
    }

    #region Forwarding to List<T>
    public T this[int index] { get => _list[index]; set => _list[index] = value; }
    public int Count => _list.Count;
    public bool IsReadOnly => false;
    public void Clear() => _list.Clear();
    public bool Contains(T item) => _list.Contains(item);
    public void CopyTo(T[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);
    public IEnumerator<T> GetEnumerator() => _list.GetEnumerator();
    public int IndexOf(T item) => _list.IndexOf(item);
    public void Insert(int index, T item) => _list.Insert(index, item);
    public bool Remove(T item) => _list.Remove(item);
    public void RemoveAt(int index) => _list.RemoveAt(index);
    IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator();
    #endregion
}

Now we've got all methods we want and didn't have to implement much. The main change in our code is, that our variables will be IList<T> instead of List<T>, ObservableCollection<T> or what ever.

And now the big wow: All of those implement IList<T>:

IList<int> list1 = new ObservableCollection<int>();
IList<int> list2 = new List<int>();
IList<int> list3 = new int[10];
IList<int> list4 = new MyList<int>();

Which brings us to the next point: Use Interfaces instead of classes. Your code should never depend on implementation-details!

//List overrider class

public class ListWithEvents<T> : List<T>
    {

        public delegate void AfterChangeHandler();

        public AfterChangeHandler OnChangeEvent;        

        public Boolean HasAddedItems = false;
        public Boolean HasRemovedItems = false;

        public bool HasChanges
        {
            get => HasAddedItems || HasRemovedItems;
            set
            {                
                HasAddedItems = value;
                HasRemovedItems = value; 
            }
        }

        public new void Add(T item)
        {
            base.Add(item);
            HasAddedItems = true;
            if(OnChangeEvent != null) OnChangeEvent();
            OnChange();
        }

        public new void AddRange(IEnumerable<T> collection)
        {
            base.AddRange(collection);
            HasAddedItems = true;
            if (OnChangeEvent != null) OnChangeEvent();
            OnChange();
        }

        public new void Insert(int index,T item)
        {
            base.Insert(index, item);
            HasAddedItems = true;
            if (OnChangeEvent != null) OnChangeEvent();
            OnChange();
        }

        public new void InsertRange(int index, IEnumerable<T> collection)
        {
            base.InsertRange(index, collection);
            HasAddedItems = true;
            if (OnChangeEvent != null) OnChangeEvent();
            OnChange();
        }


        public new void Remove(T item)
        {
            base.Remove(item);
            HasRemovedItems = true;
            if (OnChangeEvent != null) OnChangeEvent();
            OnChange();
        }

        public new void RemoveAt(int index)
        {
            HasRemovedItems = true;
            if (OnChangeEvent != null) OnChangeEvent();
            OnChange();
        }

        public new void RemoveRange(int index,int count)
        {
            HasRemovedItems = true;
            if (OnChangeEvent != null) OnChangeEvent();
            OnChange();
        }

        public new void Clear()
        {
            base.Clear();
            HasRemovedItems = true;
            if (OnChangeEvent != null) OnChangeEvent();
            OnChange();
        }

        public virtual void OnChange()
        {

        }
  
    }
Related