Why is ReadOnlyObservableCollection.CollectionChanged not public?

C#.NetCollections

C# Problem Overview


Why is ReadOnlyObservableCollection.CollectionChanged protected and not public (as the corresponding ObservableCollection.CollectionChanged is)?

What is the use of a collection implementing INotifyCollectionChanged if I can't access the CollectionChanged event?

C# Solutions


Solution 1 - C#

Here's the solution: CollectionChanged events on ReadOnlyObservableCollection

You have to cast the collection to INotifyCollectionChanged.

Solution 2 - C#

I've found a way for you of how to do this:

ObservableCollection<string> obsCollection = new ObservableCollection<string>();
INotifyCollectionChanged collection = new ReadOnlyObservableCollection<string>(obsCollection);
collection.CollectionChanged += new NotifyCollectionChangedEventHandler(collection_CollectionChanged);

You just need to refer to your collection explicitly by INotifyCollectionChanged interface.

Solution 3 - C#

I know this post is old, however, people should take their time to understand the patterns used in .NET before commenting. A read only collection is a wrapper on an existing collection that prevents consumers from modifying it directly, look at ReadOnlyCollection and you will see that it is a wrapper on a IList<T> which may or may not be mutable. Immutable collections are a different matter and are covered by the new [immutable collections library] 1

In other words, read only is not the same as immutable!!!!

That aside, ReadOnlyObservableCollection should implicitly implement INotifyCollectionChanged.

Solution 4 - C#

You might vote for the bug entry on Microsoft Connect that describes this issue: https://connect.microsoft.com/VisualStudio/feedback/details/641395/readonlyobservablecollection-t-collectionchanged-event-should-be-public

Update:

The Connect portal has been shutdown by Microsoft. So the link above does not work anymore.

My Win Application Framework (WAF) library provides a solution: ReadOnlyObservableList class:

public class ReadOnlyObservableList<T> 
        : ReadOnlyObservableCollection<T>, IReadOnlyObservableList<T>
{
    public ReadOnlyObservableList(ObservableCollection<T> list)
        : base(list)
    {
    }

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

    public new event PropertyChangedEventHandler PropertyChanged
    {
        add { base.PropertyChanged += value; }
        remove { base.PropertyChanged -= value; }
    }
}

Solution 5 - C#

There are definitely good reasons for wanting to subscribe to collection changed notifications on a ReadOnlyObservableCollection. So, as an alternative to merely casting your collection as INotifyCollectionChanged, if you happen to be subclassing ReadOnlyObservableCollection, then the following provides a more syntactically convenient way to access the a CollectionChanged event:

	public class ReadOnlyObservableCollectionWithCollectionChangeNotifications<T> : ReadOnlyObservableCollection<T>
{
	public ReadOnlyObservableCollectionWithCollectionChangeNotifications(ObservableCollection<T> list)
		: base(list)
	{
	}

	event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged2
	{
		add { CollectionChanged += value; }
		remove { CollectionChanged -= value; }
	}
}

This has worked well for me before.

Solution 6 - C#

As answered already, you have two options: you can either cast the ReadOnlyObservableCollection<T> to the interface INotifyCollectionChanged to access the explicitly implemented CollectionChanged event, or you can create your own wrapper class that does that once in the constructor and just hooks up the events of the wrapped ReadOnlyObservableCollection<T>.

Some additional insights into why this issue has not been fixed yet:

As you can see from the source code, ReadOnlyObservableCollection<T> is a public, non-sealed (i. e. inheritable) class, where the events are marked protected virtual.

That is, there might be compiled programs with classes that are derived from ReadOnlyObservableCollection<T>, with overridden event definitions but protected visibility. Those programs would contain invalid code once the event's visiblity is changed to public in the base class, because it is not allowed to restrict the visibility of an event in derived classes.

So unfortunately, making protected virtual events public later on is a binary-breaking change, and hence it will not be done without very good reasoning, which I am afraid "I have to cast the object once to attach handlers" simply isn't.

Source: GitHub comment by Nick Guerrera, August 19th, 2015

Solution 7 - C#

This was top hit on google so I figured I'd add my solution in case other people look this up.

Using the information above (about needing to cast to INotifyCollectionChanged), I made two extension methods to register and unregister.

My Solution - Extension Methods

public static void RegisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler)
{
    collection.CollectionChanged += handler;
}

public static void UnregisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler)
{
    collection.CollectionChanged -= handler;
}

Example

IThing.cs

public interface IThing
{
    string Name { get; }
    ReadOnlyObservableCollection<int> Values { get; }
}

Using the Extension Methods

public void AddThing(IThing thing)
{
    //...
    thing.Values.RegisterCollectionChanged(this.HandleThingCollectionChanged);
}

public void RemoveThing(IThing thing)
{
    //...
    thing.Values.UnregisterCollectionChanged(this.HandleThingCollectionChanged);
}

OP's Solution

public void AddThing(IThing thing)
{
    //...
    INotifyCollectionChanged thingCollection = thing.Values;
    thingCollection.CollectionChanged += this.HandleThingCollectionChanged;
}

public void RemoveThing(IThing thing)
{
    //...
    INotifyCollectionChanged thingCollection = thing.Values;
    thingCollection.CollectionChanged -= this.HandleThingCollectionChanged;
}

Alternative 2

public void AddThing(IThing thing)
{
    //...
    (thing.Values as INotifyCollectionChanged).CollectionChanged += this.HandleThingCollectionChanged;
}

public void RemoveThing(IThing thing)
{
    //...
    (thing.Values as INotifyCollectionChanged).CollectionChanged -= this.HandleThingCollectionChanged;
}

Solution 8 - C#

Solution

ReadOnlyObservableCollection.CollectionChanged is not exposed (for valid reasons outlined in other answers), so let's make our own wrapper class that exposes it:

/// <summary>A wrapped <see cref="ReadOnlyObservableCollection{T}"/> that exposes the internal <see cref="CollectionChanged"/>"/>.</summary>
public class ObservableReadOnlyCollection<T> : ReadOnlyObservableCollection<T>
{
	public new NotifyCollectionChangedEventHandler CollectionChanged;

	public ObservableReadOnlyCollection(ObservableCollection<T> list) : base(list) { /* nada */ }

	protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args) => 
		CollectionChanged?.Invoke(this, args);
}

Explanation

People have asked why you would want to observe changes to a read-only collection, so I'll explain one of many valid situations; when the read-only collection wraps a private internal collection that can change.

Here's one such scenario:

Suppose you have a service that allows adding and removing items to an internal collection from outside the service. Now suppose you want to expose the values of the collection but you don't want consumers to manipulate the collection directly; so you wrap the internal collection in a ReadOnlyObservableCollection.

> Note that in order to wrap the internal collection with ReadOnlyObservableCollection the internal collection is forced to derive from ObservableCollection by the constructor of ReadOnlyObservableCollection.

Now suppose you want to notify consumers of the service when the internal collection changes (and hence when the exposed ReadOnlyObservableCollection changes). Rather than rolling your own implementation you just want to expose the CollectionChanged of the ReadOnlyObservableCollection. Rather than forcing the consumer to make an assumption about the implementation of the ReadOnlyObservableCollection, you simply swap the ReadOnlyObservableCollection with this custom ObservableReadOnlyCollection, and you're done.

The ObservableReadOnlyCollection hides ReadOnlyObservableCollection.CollectionChanged with it's own, and simply passes on all the collection changed events to any attached event handler.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionOskarView Question on Stackoverflow
Solution 1 - C#NickView Answer on Stackoverflow
Solution 2 - C#RestutaView Answer on Stackoverflow
Solution 3 - C#Jason YoungView Answer on Stackoverflow
Solution 4 - C#jbeView Answer on Stackoverflow
Solution 5 - C#porcusView Answer on Stackoverflow
Solution 6 - C#LWChrisView Answer on Stackoverflow
Solution 7 - C#DanView Answer on Stackoverflow
Solution 8 - C#ZodmanView Answer on Stackoverflow