web-dev-qa-db-ja.com

リストへの追加イベントを処理する方法は?

このようなリストがあります:

List<Controls> list = new List<Controls>

このリストに新しいポジションを追加する方法は?

私がする時:

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

私は私のオブジェクトでこのようなことをしたいと思います:

myList.AddingEvent += HandleAddingEvent

そして、私のHandleAddingEventデリゲートで、このリストに位置を追加します。新しい位置イベントの追加をどのように処理すればよいですか?このイベントを利用可能にするにはどうすればよいですか?

39

Listを継承して、独自のハンドラーを追加できます。

_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...");
        }
    }
}
_

警告

  1. リストにオブジェクトを追加するすべてのメソッドを再実装する必要があることに注意してください。この実装では、AddRange()はこのイベントを起動しません。

  2. 私たちはオーバーロードしませんでしたメソッド。オリジナルのものを隠しました。このクラスが_List<T>_で囲まれているときにオブジェクトをAdd()すると、イベントは発生しません

_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

あなたが探しているものは、すでに ObservableCollection(T) クラスのAPIの一部であると信じています。例:

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

必要なのは、コレクションで発生するあらゆる種類の変更のイベントを持つクラスです。これに最適なクラスはBindingList<T>。あらゆる種類の突然変異のイベントがあり、それを使用してイベントリストを変更できます。

26
JaredPar

List<T>でこれを行うことはできません。ただし、ObservableCollection<T>を使用して実行できます。 ObservableCollection<T> Class を参照してください。

16
John Saunders

明確にするために:標準機能のみを監視する必要がある場合は、 ObservableCollection(T) または他の既存のクラスを使用する必要があります。すでに手に入れたものを再構築しないでください。

..しかし、特別なイベントが必要で、さらに深くする必要がある場合は、リストから派生させないでください!リストから派生した場合、すべての追加を確認するためにAdd()をオーバーロードすることはできません。

例:

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

Add()をオーバーライドすることはできません。これは、基本クラスの機能を調整することができるためです( リスコフ置換の原理 )。

しかし、いつものように、それを機能させる必要があります。ただし、独自のリストを作成する場合は、インターフェイス_IList<T>_を実装してリストを作成する必要があります。

追加前後のイベントを実装する例:

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

これで、必要なメソッドがすべて揃い、あまり実装する必要がなくなりました。コードの主な変更点は、変数が_IList<T>_、_List<T>_などの代わりに_ObservableCollection<T>_になることです。

そして今、大きな驚き:それらはすべて_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>();
_

次のポイントに進みます。クラスの代わりにインターフェイスを使用します。コードは、実装の詳細に決して依存しないでください!

2
kara

1つの簡単な解決策は、プロジェクトのリストにAddメソッドを導入し、そこでイベントを処理することです。イベントハンドラーの必要性に応えるものではありませんが、一部の小規模プロジェクトには役立ちます。

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

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

の代わりに

list.Add(item);
2
Ahmad

Ahmadの拡張メソッドの使用を便乗させるには、パブリックgetメソッドとパブリック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);
    }
}

ただし、このクラスは、手動で公開するList<>メソッドへのアクセスのみを提供します。したがって、多くの機能が必要な場合には役に立たない可能性があります。

1
jayflo

標準のコレクションをそのまま使用してこれを行うことはできません-それらは変更通知をサポートしていません。既存のコレクションタイプを継承または集約して独自のクラスを構築するか、または BindingList<T>IBindingList を実装し、 ListChanged イベントによる変更通知をサポートします。

1

イベントを追加する必要はありません。メソッドを追加するだけです。

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