ObservableCollection that also monitors changes on

2019-01-01 09:05发布

Is there a collection (BCL or other) that has the following characteristics:

Sends event if collection is changed AND sends event if any of the elements in the collection sends a PropertyChanged event. Sort of an ObservableCollection<T> where T: INotifyPropertyChanged and the collection is also monitoring the elements for changes.

I could wrap an observable collection my self and do the event subscribe/unsubscribe when elements in the collection are added/removed but I was just wondering if any existing collections did this already?

8条回答
听够珍惜
2楼-- · 2019-01-01 09:15

Check out the C5 Generic Collection Library. All of its collections contain events that you can use to attach callbacks for when items are added, removed, inserted, cleared, or when the collection changes.

I am working for some extensions to that libary here that in the near future should allow for "preview" events that could allow you to cancel an add or change.

查看更多
临风纵饮
3楼-- · 2019-01-01 09:17

@soren.enemaerke: I would have made this comment on your answer post, but I can't (I don't know why, maybe because I don't have many rep points). Anyway, I just thought that I'd mention that in your code you posted I don't think that the Unsubscribe would work correctly because it is creating a new lambda inline and then trying to remove the event handler for it.

I would change the add/remove event handler lines to something like:

element.PropertyChanged += ContainedElementChanged;

and

element.PropertyChanged -= ContainedElementChanged;

And then change the ContainedElementChanged method signature to:

private void ContainedElementChanged(object sender, PropertyChangedEventArgs e)

This would recognise that the remove is for the same handler as the add and then remove it correctly. Hope this helps somebody :)

查看更多
素衣白纱
4楼-- · 2019-01-01 09:24

@soren.enemaerke Made this a reply in order to post proper code as the comments section on your answer would render it unreadable. The only issue I've had with the solution is that the particular element which triggers the PropertyChanged event is lost and you have no way of knowing in the PropertyChanged call.

col.PropertyChanged += (s, e) => { Trace.WriteLine("Changed " + e.PropertyName)

To fix this I've created a new class PropertyChangedEventArgsEx and changed the ContainedElementChanged method within your class.

new class

public class PropertyChangedEventArgsEx : PropertyChangedEventArgs
{
    public object Sender { get; private set; }

    public PropertyChangedEventArgsEx(string propertyName, object sender) 
        : base(propertyName)
    {
        this.Sender = sender;
    }
}

changes to your class

 private void ContainedElementChanged(object sender, PropertyChangedEventArgs e)
    {
        var ex = new PropertyChangedEventArgsEx(e.PropertyName, sender);
        OnPropertyChanged(ex);
    }

After this you can get the actual Sender element in col.PropertyChanged += (s, e) by casting e to PropertyChangedEventArgsEx

((INotifyPropertyChanged)col).PropertyChanged += (s, e) =>
        {
            var argsEx = (PropertyChangedEventArgsEx)e;
            Trace.WriteLine(argsEx.Sender.ToString());
        };

Again, you should note the the s here is the collection of elements, not the actual element that triggered the event. Hence the new Sender property in the PropertyChangedEventArgsEx class.

查看更多
步步皆殇っ
5楼-- · 2019-01-01 09:30

The simplest way to do it is just do

using System.ComponentModel;
public class Example
{
    BindingList<Foo> _collection;

    public Example()
    {
        _collection = new BindingList<Foo>();
        _collection.ListChanged += Collection_ListChanged;
    }

    void Collection_ListChanged(object sender, ListChangedEventArgs e)
    {
        MessageBox.Show(e.ListChangedType.ToString());
    }

}

The BindingList class as been in .net sence 2.0. It will fire it's ListChanged event any time a item in the collection fires INotifyPropertyChanged.

查看更多
只靠听说
6楼-- · 2019-01-01 09:34

Rxx 2.0 contains operators that along with this conversion operator for ObservableCollection<T> makes it easy to achieve your goal.

ObservableCollection<MyClass> collection = ...;

var changes = collection.AsCollectionNotifications<MyClass>();
var itemChanges = changes.PropertyChanges();
var deepItemChanges = changes.PropertyChanges(
  item => item.ChildItems.AsCollectionNotifications<MyChildClass>());

The following property changed notification patterns are supported for MyClass and MyChildClass:

  • INotifyPropertyChanged
  • [Property]Changed event pattern (legacy, for use by Component Model)
  • WPF dependency properties
查看更多
初与友歌
7楼-- · 2019-01-01 09:39

I would use ReactiveUI's ReactiveCollection:

reactiveCollection.Changed.Subscribe(_ => ...);
查看更多
登录 后发表回答