web-dev-qa-db-fra.com

Comment gérer l'événement d'ajout à la liste?

J'ai une liste comme celle-ci:

List<Controls> list = new List<Controls>

Comment gérer l'ajout d'une nouvelle position à cette liste?

Quand je fais:

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

Je voudrais faire quelque chose comme ça dans mon objet:

myList.AddingEvent += HandleAddingEvent

Et puis dans mon HandleAddingEvent délégué déléguer l'ajout de position à cette liste. Comment dois-je gérer l'ajout d'un nouvel événement de position? Comment puis-je rendre cet événement disponible?

39
Tomasz Smykowski

Vous pouvez hériter de List et ajouter votre propre gestionnaire, quelque chose comme

using System;
using System.Collections.Generic;

namespace test
{
    class Program
    {

        class MyList<T> : List<T>
        {

            public event EventHandler OnAdd;

            public new void Add(T item) // "new" to avoid compiler-warnings, because we're hiding a method from base-class
            {
                if (null != OnAdd)
                {
                    OnAdd(this, null);
                }
                base.Add(item);
            }
        }

        static void Main(string[] args)
        {
            MyList<int> l = new MyList<int>();
            l.OnAdd += new EventHandler(l_OnAdd);
            l.Add(1);
        }

        static void l_OnAdd(object sender, EventArgs e)
        {
            Console.WriteLine("Element added...");
        }
    }
}

Avertissement

  1. Sachez que vous devez réimplémenter toutes les méthodes qui ajoutent des objets à votre liste. AddRange() ne déclenchera pas cet événement, dans cette implémentation.

  2. Nous n'avons pas surchargé la méthode. Nous avons caché l'original. Si vous Add() un objet alors que cette classe est encadrée dans List<T>, L'événement ne sera pas déclenché !

MyList<int> l = new MyList<int>();
l.OnAdd += new EventHandler(l_OnAdd);
l.Add(1); // Will work

List<int> baseList = l;
baseList.Add(2); // Will NOT work!!!
40
Paolo Tedesco

Je crois que ce que vous recherchez fait déjà partie de l'API dans la classe ObservableCollection (T) . Exemple:

ObservableCollection<int> myList = new ObservableCollection<int>();

myList.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(
    delegate(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)                    
    {
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
        {
            MessageBox.Show("Added value");
        }
    }
);

myList.Add(1);
65
MattH

Vous avez besoin d'une classe qui a des événements pour tout type de modification qui se produit dans la collection. La meilleure classe pour cela est BindingList<T>. Il contient des événements pour chaque type de mutation que vous pouvez ensuite utiliser pour modifier votre liste d'événements.

26
JaredPar

Vous ne pouvez pas faire cela avec List<T>. Cependant, vous pouvez le faire avec ObservableCollection<T>. Voir ObservableCollection<T> Classe .

16
John Saunders

Pour être clair: si vous avez seulement besoin d'observer les fonctionnalités standard, vous devez utiliser ObservableCollection (T) ou d'autres classes existantes. Ne reconstruisez jamais quelque chose que vous avez déjà.

..Mais .. Si vous avez besoin d'événements spéciaux et devez aller plus loin, vous ne devriez pas dériver de List! Si vous dérivez de List, vous ne pouvez pas surcharger Add() pour voir chaque ajout.

Exemple:

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!
}

Il n'est pas autorisé de remplacer Add(), car vous pourriez mees les fonctionnalités de la classe de base ( principe de substitution Liskov ).

Mais comme toujours, nous devons le faire fonctionner. Mais si vous voulez construire votre propre liste, vous devez le faire en implémentant une interface: IList<T>.

Exemple qui implémente un événement avant et après ajout:

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
}

Maintenant, nous avons toutes les méthodes que nous voulons et n'avons pas eu à mettre en œuvre beaucoup. Le principal changement dans notre code est que nos variables seront IList<T> Au lieu de List<T>, ObservableCollection<T> Ou quoi que ce soit.

Et maintenant, le grand wow: tous ceux qui implémentent 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>();

Ce qui nous amène au point suivant: utilisez des interfaces plutôt que des classes. Votre code ne devrait jamais dépendre des détails d'implémentation!

2
kara

Une solution simple consiste à introduire une méthode Add pour la liste dans votre projet et à y gérer l'événement. Il ne répond pas au besoin d'un gestionnaire d'événements mais peut être utile pour certains petits projets.

AddToList(item) // or
AddTo(list,item)
////////////////////////

void AddTo(list,item)
{
    list.Add(item);
    // event handling
}

au lieu de

list.Add(item);
2
Ahmad

Pour tirer parti de l'utilisation par Ahmad de Méthodes d'extension, vous pouvez créer votre propre classe où la liste est privée avec une méthode publique get et une méthode publique add.

public class MyList
{
    private List<SomeClass> PrivateSomeClassList;
    public List<SomeClass> SomeClassList
    {
        get
        {
            return PrivateSomeClassList;
        }
    }

    public void Add(SomeClass obj)
    {
        // do whatever you want
        PrivateSomeClassList.Add(obj);
    }
}

Cependant, cette classe ne donne accès qu'à List<> les méthodes que vous exposez manuellement ... donc peuvent ne pas être utiles dans les cas où vous avez besoin de beaucoup de cette fonctionnalité.

1
jayflo

Vous ne pouvez pas le faire avec des collections standard prêtes à l'emploi - elles ne prennent tout simplement pas en charge les notifications de modification. Vous pouvez créer votre propre classe en héritant ou en agrégeant un type de collection existant ou vous pouvez utiliser BindingList<T> qui implémente IBindingList et prend en charge les notifications de modification via l'événement ListChanged .

1
Daniel Brückner

Pas besoin d'ajouter un événement, il suffit d'ajouter la méthode.

public class mylist:List<string>
{
  public void Add(string p)
  {
     // Do cool stuff here
     base.Add(p);
  }
}
0
CRCarpenter