Grayson Mitchell
Grayson Mitchell

Reputation: 1187

How to fire an event when collection element object fires an event

I have an observableCollection and have wired up an event in MyObject, is there an cunnign way to grab that event in the collection without having to re-write the collection class?

    public class MyObjectCollection : ObservableCollection<MyObject>
    {
        public MyObjectCollection()
        {
            //some cunning code here
        }
    }

I suspect I have to re-write the ObservableCollection Class, but unfortunatly there is no interface (like there is an IDictionary)

Upvotes: 1

Views: 168

Answers (2)

Dan Bryant
Dan Bryant

Reputation: 27505

Reading this again, I think you're after something like this:

public sealed class CollectionEventMonitor<TItem, TEventArgs> : IDisposable
    where TEventArgs: EventArgs
{
    private readonly INotifyCollectionChanged _collection;
    private readonly Action<TItem, EventHandler<TEventArgs>> _addEvent;
    private readonly Action<TItem, EventHandler<TEventArgs>> _removeEvent;

    public event EventHandler<TEventArgs> ItemFiredEvent;

    public CollectionEventMonitor(INotifyCollectionChanged collection, 
        Action<TItem, EventHandler<TEventArgs>> addEvent, 
        Action<TItem, EventHandler<TEventArgs>> removeEvent)
    {
        _addEvent = addEvent;
        _removeEvent = removeEvent;
        _collection = collection;
        _collection.CollectionChanged += _collection_CollectionChanged;
    }

    void _collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach (var item in e.NewItems.Cast<TItem>())
                    _addEvent(item, OnItemFiredEvent);
                break;
            case NotifyCollectionChangedAction.Remove:
                foreach (var item in e.OldItems.Cast<TItem>())
                    _removeEvent(item, OnItemFiredEvent);
                break;
            case NotifyCollectionChangedAction.Replace:
                foreach (var item in e.OldItems.Cast<TItem>())
                    _removeEvent(item, OnItemFiredEvent);
                foreach (var item in e.NewItems.Cast<TItem>())
                    _addEvent(item, OnItemFiredEvent);
                break;
            case NotifyCollectionChangedAction.Move:
                break;
            case NotifyCollectionChangedAction.Reset:
                foreach (var item in e.OldItems.Cast<TItem>())
                    _removeEvent(item, OnItemFiredEvent);
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }

    private void OnItemFiredEvent(Object item, TEventArgs eventArgs)
    {
        var handler = ItemFiredEvent;
        if (handler != null)
            handler(item, eventArgs);
    }

    public void Dispose()
    {
        _collection.CollectionChanged -= _collection_CollectionChanged;
    }
}

This is rough and untested, but it should give you the basic idea. You use it like this:

        var collection = new ObservableCollection<Foo>();
        var monitor = 
            new CollectionEventMonitor<Foo, EventArgs>(collection,
                (foo,handler) => foo.Bar += handler,
                (foo,handler) => foo.Bar -= handler);

The advantage of this approach is that you can monitor any observable collection for item events, rather than creating a special wrapper collection. You can also create multiple instances of the monitor for the same collection, allowing different contexts to monitor different events of items on the same collection.

Upvotes: 1

JDT
JDT

Reputation: 487

Favor composition over inheritance. Instead of extending ObservableCollection, implement the interfaces you need (probably INotifyCollectionChanged and IList) and keep an ObservableCollection as a private field. That way you can wire your own add and remove calls and do what you want with the objects added to the collection.

Upvotes: 2

Related Questions