小编典典

ObservableCollection还监视集合中元素的更改

c#

是否存在具有以下特征的集合(BCL或其他集合):

如果集合发生更改,则发送事件;如果集合中的任何元素发送事件,则发送PropertyChanged事件。排序的ObservableCollection<T>位置T: INotifyPropertyChanged和集合还监视元素的更改。

我可以自己包装一个可观察的集合,并在添加/删除集合中的元素时是否进行事件订阅/取消订阅,但是我只是想知道是否有任何现有的集合已经这样做了?


阅读 879

收藏
2020-05-19

共1个答案

小编典典

我自己做了一个快速实施:

public class ObservableCollectionEx<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        Unsubscribe(e.OldItems);
        Subscribe(e.NewItems);
        base.OnCollectionChanged(e);
    }

    protected override void ClearItems()
    {
        foreach(T element in this)
            element.PropertyChanged -= ContainedElementChanged;

        base.ClearItems();
    }

    private void Subscribe(IList iList)
    {
        if (iList != null)
        {
            foreach (T element in iList)
                element.PropertyChanged += ContainedElementChanged;
        }
    }

    private void Unsubscribe(IList iList)
    {
        if (iList != null)
        {
            foreach (T element in iList)
                element.PropertyChanged -= ContainedElementChanged;
        }
    }

    private void ContainedElementChanged(object sender, PropertyChangedEventArgs e)
    {
        OnPropertyChanged(e);
    }
}

诚然,当实际更改的属性位于包含的元素上时,在集合上触发PropertyChanged会造成混淆和误导,但这符合我的特定目的。可以使用在ContainerElementChanged内部触发的新事件来扩展它

有什么想法吗?

编辑:应该注意,BCL
ObservableCollection仅通过显式实现公开INotifyPropertyChanged接口,因此您需要提供强制转换才能附加到事件,如下所示:

ObservableCollectionEx<Element> collection = new ObservableCollectionEx<Element>();
((INotifyPropertyChanged)collection).PropertyChanged += (x,y) => ReactToChange();

EDIT2:添加了对ClearItems的处理,谢谢Josh

EDIT3:为PropertyChanged添加了正确的退订,谢谢Mark

EDIT4:哇,这真的是随学随便:)。KP注意到,当一个包含的元素发生更改时,事件是使用集合作为发送者而不是使用元素触发的。他建议在标有 new
的类上声明一个PropertyChanged事件。这将有一些问题,我将尝试通过以下示例进行说明:

  // work on original instance
  ObservableCollection<TestObject> col = new ObservableCollectionEx<TestObject>();
  ((INotifyPropertyChanged)col).PropertyChanged += (s, e) => { Trace.WriteLine("Changed " + e.PropertyName); };

  var test = new TestObject();
  col.Add(test); // no event raised
  test.Info = "NewValue"; //Info property changed raised

  // working on explicit instance
  ObservableCollectionEx<TestObject> col = new ObservableCollectionEx<TestObject>();
  col.PropertyChanged += (s, e) => { Trace.WriteLine("Changed " + e.PropertyName); };

  var test = new TestObject();
  col.Add(test); // Count and Item [] property changed raised
  test.Info = "NewValue"; //no event raised

从样本中您可以看到,“覆盖”事件具有副作用,因此您在订阅事件时需要格外小心所使用的变量类型,因为它决定了要接收的事件。

2020-05-19