16

何かが追加、挿入、または削除されるたびにイベントを発生させるリストを継承するクラスEventListを作成しました。

public class EventList<T> : List<T>
{
    public event ListChangedEventDelegate ListChanged;
    public delegate void ListChangedEventDelegate();

    public new void Add(T item)
    {
        base.Add(item);
        if (ListChanged != null
            && ListChanged.GetInvocationList().Any())
        {
            ListChanged();
        }
    }
    ...
}

現時点では、次のようなプロパティとして使用しています。

public EventList List
{
    get { return m_List; }
    set
    {
        m_List.ListChanged -= List_ListChanged;

        m_List = value;

        m_List.ListChanged += List_ListChanged;
        List_ListChanged();
    }
}

今私の問題は、新しいオブジェクトが参照された場合、またはそれを防ぐことができるので、セッターでイベントの配線を行う必要がないということです。

もちろん、プロパティを「プライベートセット」に変更することもできますが、クラスを変数としても使用できるようにしたいと思います。

4

5 に答える 5

26

クラス内にコレクションクラスの新しいインスタンスを作成することはめったにありません。新しいリストを作成する代わりに、一度インスタンス化してクリアします。(そして、すでにINotifyCollectionChangedインターフェイスが継承されているため、ObservableCollectionを使用します)

private readonly ObservableCollection<T> list;
public ctor() {
    list = new ObservableCollection<T>();
    list.CollectionChanged += listChanged;
}

public ObservableCollection<T> List { get { return list; } }

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

private void listChanged(object sender, NotifyCollectionChangedEventArgs args) {
   // list changed
}

このように、イベントを1回フックするだけで、プロパティのsetアクセサーの前のリストとnullまたは等しいかどうかをチェックする代わりに、clearメソッドを呼び出すことで「リセット」できます。


C#6での変更により、バッキングフィールドなしでコンストラクターからgetプロパティを割り当てることができます(バッキングフィールドは暗黙的です)

したがって、上記のコードは次のように簡略化できます。

public ctor() {
    List = new ObservableCollection<T>();
    List.CollectionChanged += OnListChanged;
}

public ObservableCollection<T> List { get; }

public void Clear()
{
    List.Clear();
}

private void OnListChanged(object sender, NotifyCollectionChangedEventArgs args)
{
   // react to list changed
}
于 2012-10-08T15:56:53.720 に答える
12

ObservableCollectionは、CollectionChangedイベントを含むリストです

ObservableCollection.CollectionChangedイベント

イベントハンドラーを接続する方法については、Patrickからの回答を参照してください。+1

何を探しているのかわかりませんが、これを、追加、削除、および変更時に発生する1つのイベントを含むコレクションに使用します。

public class ObservableCollection<T>: INotifyPropertyChanged
{
    private BindingList<T> ts = new BindingList<T>();

    public event PropertyChangedEventHandler PropertyChanged;

    // This method is called by the Set accessor of each property. 
    // The CallerMemberName attribute that is applied to the optional propertyName 
    // parameter causes the property name of the caller to be substituted as an argument. 
    private void NotifyPropertyChanged( String propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public BindingList<T> Ts
    {
        get { return ts; }
        set
        {
            if (value != ts)
            {
                Ts = value;
                if (Ts != null)
                {
                    ts.ListChanged += delegate(object sender, ListChangedEventArgs args)
                    {
                        OnListChanged(this);
                    };
                }
                NotifyPropertyChanged("Ts");
            }
        }
    }

    private static void OnListChanged(ObservableCollection<T> vm)
    {
        // this will fire on add, remove, and change
        // if want to prevent an insert this in not the right spot for that 
        // the OPs use of word prevent is not clear 
        // -1 don't be a hater
        vm.NotifyPropertyChanged("Ts");
    }

    public ObservableCollection()
    {
        ts.ListChanged += delegate(object sender, ListChangedEventArgs args)
        {
            OnListChanged(this);
        };
    }
}
于 2012-10-08T15:48:24.820 に答える
5

Observable Collectionに変換したくない、または変換できない場合は、次のことを試してください。

public class EventList<T> : IList<T> /* NOTE: Changed your List<T> to IList<T> */
{
  private List<T> list; // initialize this in your constructor.
  public event ListChangedEventDelegate ListChanged;
  public delegate void ListChangedEventDelegate();

  private void notify()
  {
      if (ListChanged != null
          && ListChanged.GetInvocationList().Any())
      {
        ListChanged();
      }
  }

  public new void Add(T item)
  {
      list.Add(item);
      notify();
  }

  public List<T> Items {
    get { return list; } 
    set {
      list = value; 
      notify();
    }
  }
  ...
}

これで、プロパティについて、コードを次のように減らすことができるはずです。

public EventList List
{
  get { return m_List.Items; }
  set
  {
      //m_List.ListChanged -= List_ListChanged;

      m_List.Items = value;

      //m_List.ListChanged += List_ListChanged;
      //List_ListChanged();
  }
}

なんで?EventList.Itemsに何かを設定すると、プライベートnotify()ルーチンが呼び出されます。

于 2012-10-08T16:01:46.560 に答える
0

誰かがIList.add(object)からGenericメソッドを呼び出したときの解決策があります。あなたも通知を受けるように。

using System;
using System.Collections;
using System.Collections.Generic;

namespace YourNamespace
{
    public class ObjectDoesNotMatchTargetBaseTypeException : Exception
    {
        public ObjectDoesNotMatchTargetBaseTypeException(Type targetType, object actualObject)
            : base(string.Format("Expected base type ({0}) does not match actual objects type ({1}).",
                targetType, actualObject.GetType()))
        {
        }
    }

    /// <summary>
    /// Allows you to react, when items were added or removed to a generic List.
    /// </summary>
    public abstract class NoisyList<TItemType> : List<TItemType>, IList
    {
        #region Public Methods
        /******************************************/
        int IList.Add(object item)
        {
            CheckTargetType(item);
            Add((TItemType)item);
            return Count - 1;
        }

        void IList.Remove(object item)
        {
            CheckTargetType(item);
            Remove((TItemType)item);
        }

        public new void Add(TItemType item)
        {
            base.Add(item);
            OnItemAdded(item);
        }

        public new bool Remove(TItemType item)
        {
            var result = base.Remove(item);
            OnItemRemoved(item);
            return result;
        }
        #endregion

        # region Private Methods
        /******************************************/
        private static void CheckTargetType(object item)
        {
            var targetType = typeof(TItemType);
            if (item.GetType().IsSubclassOf(targetType))
                throw new ObjectDoesNotMatchTargetBaseTypeException(targetType, item);
        }
        #endregion

        #region Abstract Methods
        /******************************************/
        protected abstract void OnItemAdded(TItemType addedItem);

        protected abstract void OnItemRemoved(TItemType removedItem);
        #endregion
    }
}
于 2015-05-19T13:05:48.653 に答える
0

ObservableCollectionが解決策ではない場合は、次のことを試すことができます。

A)イベントが発生するときに新しいCount属性を含むカスタムEventArgsを実装します。

public class ChangeListCountEventArgs : EventArgs
{
    public int NewCount
    {
        get;
        set;
    }

    public ChangeListCountEventArgs(int newCount)
    {
        NewCount = newCount;
    }
}

B)Listから継承するカスタムListを実装し、必要に応じてCount属性とコンストラクターを再定義します。

public class CustomList<T> : List<T>
{
    public event EventHandler<ChangeListCountEventArgs> ListCountChanged;

    public new int Count
    {
        get
        {
            ListCountChanged?.Invoke(this, new ChangeListCountEventArgs(base.Count));
            return base.Count;
        }
    }

    public CustomList()
    { }

    public CustomList(List<T> list) : base(list)
    { }

    public CustomList(CustomList<T> list) : base(list)
    { }
}

C)最後にあなたのイベントを購読します:

var myList = new CustomList<YourObject>();
myList.ListCountChanged += (obj, e) => 
{
    // get the count thanks to e.NewCount
};
于 2018-01-03T13:48:03.260 に答える