Search code examples
c#observablecollectionweak-events

CollectionChangedEventManager not forwarding event for custom collection


I have a custom INotifyCollectionChanged class, which essentially just wraps around the standard ObservableCollection. Whenever something is added/removed, the CollectionChanged event is raised as expected. However, when I try to listen to this event using a WeakEventListener, the listener never receives the event. Why is this happening and how do I fix this?

In below sample, I'd expect a NotImplementedException to be thrown, but the test case succeeds (which clearly indicates that the event is truly raised). If you change the collection to be an ObservableCollection instead of a Wrapper, the exception does get thrown as expected.

public class Test : IWeakEventListener
{
    private class Wrapper : INotifyCollectionChanged
    {
        private readonly ObservableCollection<string> _internal 
                                     = new ObservableCollection<string>();

        public void Add(string s)
        {
            _internal.Add(s);
        }

        public event NotifyCollectionChangedEventHandler CollectionChanged
        {
            add { _internal.CollectionChanged += value; }
            remove { _internal.CollectionChanged -= value; }
        }
    }

    public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
    {
        throw new NotImplementedException();
    }

    [Test]
    public void CustomCollectionTest()
    {
        //change to new ObservableCollection<string>() and the exception gets thrown
        var collection = new Wrapper(); 
        var raised = false;
        collection.CollectionChanged += (o, e) => raised = true;
        CollectionChangedEventManager.AddListener(collection, this);
        collection.Add("foobar");
        Assert.True(raised);
    }
}

Possibly related but still unanswered:
Why WeakEventManager does not fire an event when the sender is not the nominal?


Solution

  • As to why, the issue is the same as in this question. Essentially, the source registered with the event manager has to be the same as the sender of the event.

    As a workaround for this limitation, I just have to make sure that the Wrapper sends the event, rather than directly using the event on the wrapped collection.

    private class Wrapper : INotifyCollectionChanged
    {
        private readonly ObservableCollection<string> _internal 
                                     = new ObservableCollection<string>();
    
        public Wrapper()
        {
            _internal.CollectionChanged += OnInternalChanged;
        }
    
        public void Add(string s)
        {
            _internal.Add(s);
        }
    
        private void OnInternalChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            var toRaise = CollectionChanged;
            if (toRaise != null)
                toRaise(this, e);
        }
    
        public event NotifyCollectionChangedEventHandler CollectionChanged;
    }