【问题标题】:List<T> firing Event on ChangeList<T> 在更改时触发事件
【发布时间】:2019-02-07 18:56:29
【问题描述】:

我创建了一个继承 List 的类 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();
    }
}

现在我的问题是,如果一个新对象被引用或阻止它,我是否可以以某种方式处理,所以我不必在 setter 中进行事件连接?

当然,我可以将属性更改为“私有集”,但我也希望能够将类用作变量。

【问题讨论】:

  • 为什么不使用 ObservableCollection
  • 不清楚你在问什么。您能否提供一个示例说明您希望阻止什么或允许什么。
  • 请发布您的 List_ListChanged()。你要预防什么?
  • 这能回答你的问题吗? How to handle add to list event?

标签: c# .net


【解决方案1】:

你很少在一个类中创建一个集合类的新实例。实例化一次并清除它,而不是创建一个新列表。 (并使用 ObservableCollection,因为它已经继承了 INotifyCollectionChanged 接口)

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
}

这样你只需要连接一次事件,并且可以通过调用 clear 方法来“重置它”,而不是检查属性的 set 访问器中的前一个列表是否为 null 或相等。


通过 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
}

【讨论】:

  • 这就是我想到的将设置器设为私有并将其用作自动属性的方式。 public ObservableCollection&lt;T&gt; List { get; private set; }我会去的。
  • @Martin:将其设为只读并单独在构造函数中设置会添加一个约束,因为您无法从其他任何地方设置它。自动属性很好,但是当一个字段只能设置一次时,我将其设为只读并使用支持字段。
【解决方案2】:

ObservableCollection 是一个带有 CollectionChanged 事件的列表

ObservableCollection.CollectionChanged Event

有关如何连接事件处理程序,请参阅 Patrick 的回答。 +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);
        };
    }
}

【讨论】:

  • 这是真的,但它并没有说明他应该如何做他的事件接线。
  • 学到了一些新东西。再见事件列表,你好 ObservaleCollection!
【解决方案3】:

如果您不想或无法转换为 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() 例程。

【讨论】:

  • 注意:不要忘记实现IList&lt;T&gt; 接口的其余部分!
  • 定义 Add as new 将使((IList)list).Add(item) 不会触发 notify 方法。
  • 因为我已将notify() 调用添加到该添加属性。
  • 另外,我不确定为什么 OP 将 Add 定义为 new - 我只是使用了他所拥有的。我想他有他的理由。
  • 它不会如果您使用基类,如示例dotnetpad.net/ViewPaste/IYP6mJpXqUmpISCDe28YKA 所示。不过我不是在责怪你,只是想记下它,在方法上使用 new 时要小心。您甚至不需要它,因为您使用的是 IList 接口而不是 List 类,因此问题在您的示例中自行消失;)
【解决方案4】:

当有人从 IList.add(object) 调用通用方法时,我有一个解决方案。以便您也收到通知。

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

【讨论】:

    【解决方案5】:

    如果 ObservableCollection 不是您的解决方案,您可以尝试:

    A) 实现一个自定义 EventArgs,它在触发事件时将包含新的 Count 属性。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多