Why is ReadOnlyObservableCollection.CollectionChanged not public? - c#

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?

Here's the solution: CollectionChanged events on ReadOnlyObservableCollection
You have to cast the collection to INotifyCollectionChanged.

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.

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
In other words, read only is not the same as immutable!!!!
That aside, ReadOnlyObservableCollection should implicitly implement INotifyCollectionChanged.

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.

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; }
}
}

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

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
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.

Related

How can an interface implementation be protected?

I just came across one thing in the .NET framework (v4.0) that I cannot understand:
There is the class SortDescriptionCollection
namespace System.ComponentModel
{
public class SortDescriptionCollection : Collection<SortDescription>, INotifyCollectionChanged
{
....
protected event NotifyCollectionChangedEventHandler CollectionChanged;
....
}
}
implementing the Interface INotifyCollectionChanged:
public interface INotifyCollectionChanged
{
event NotifyCollectionChangedEventHandler CollectionChanged;
}
I wanted to use the event on the class but I cannot because it's protected.
That's not the big problem because I can cast the implementation to the Interface and then use it.
But how can that be built? If I try to do with
class MyDerivedType : INotifyCollectionChanged
{
protected event NotifyCollectionChangedEventHandler CollectionChanged;
}
so the compiler says:
'MyDerivedType' does not implement interface member
'System.Collections.Specialized.INotifyCollectionChanged.CollectionChanged'.
'MyDerivedType' cannot implement an interface member because it is not public.
EDIT:
I don't think it's a duplicate. I was not asking how to compile the code above, it was the question how the .NET framework seemed to could do that (and obviously it couldn't)
It's an explicitly implemented interface event:
event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
{
add { CollectionChanged += value; }
remove { CollectionChanged -= value; }
}
So it satisfies the requirements of INotifyCollectionChanged, but it isn't publicly visible on the class itself - perfectly valid.
This usually signalizes intent - this is not supposed to be a part of the public interface of the class. However, if you absolutely do want to access this from the outside, you can just do a cast to the interface:
((INotifyCollectionChanged)myCollection).CollectionChanged
Just have a look, imagine that you've implemented the interface:
SortDescriptionCollection my = new SortDescriptionCollection();
// you can't do this (it should not compile)
// since "CollectionChanged" is protected
my.CollectionChanged += (sender, e) => Console.Write("hello from event!");
// however, since SortDescriptionCollection implements SortDescriptionCollection
// you can cast to the interface
INotifyCollectionChanged hack = my as SortDescriptionCollection;
// wow! you can access "protected" event as if it's public one!
// So isolation is violated
hack.CollectionChanged += (sender, e) => Console.Write("hello from hacked event!");
So this isolation violation is the reason why you can't use protected methods,
events, properties when they're exposed via interfaces

Trigger InotifyPropertyChanged/CollectionChanged on ObservableCollection

I've tried looking at other topics on this but I haven't found a working implementation to my question. Basically, I have an ObservableCollection called "FruitBasket" that contains different kinds of fruit. FruitBasket itself contains ObservableCollections for each respective type of fruit that passes through so that they can be used as ItemSources for ListViews (Denoted by their names "AppleContainer" and "OrangeContainer"), each displaying one kind of fruit. Because the fruit classes themselves implement INotifyPropertyChanged, modifying their values triggers updates to the ListView controls just fine, however, FruitBasket has a "TotalWeight" property derived from the weights of all the other fruits in the collections. I want "TotalWeight" to update the Label control in the UI without me having to refresh the UI. Triggerering a notification on a property change of the actual ObservableCollection itself, and not simply its constituent members is more difficult and I haven't found any solutions that work so far (or that I've implemented correctly).
public class FruitBasket : ObservableCollection<IFruit>
{
private decimal _totalWeight;
public FruitBasket()
{
this.Add(new OrangeContainer(this));
this.Add(new AppleContainer(this));
}
public OrangeContainer Oranges
{
get { return (OrangeContainer)this.Items[0]; }
}
public AppleContainer Apples
{
get { return (AppleContainer)this.Items[1]; }
}
public decimal TotalWeight
{
get { return _totalWeight; }
set { _totalWeight = value; }
}
internal void UpdateWeight(IFruit caller)
{
_totalWeight = 0;
foreach (Orange orng in (OrangeContainer)this.Items[0])
{
_totalWeight += orng.Weight;
}
foreach (Apple appl in (AppleContainer)this.Items[1])
{
_totalWeight += appl.Weight;
}
}
You need to call INotifyPropertyChanged.PropertyChanged event of your FruitBasket whenever items are added, removed or Weight property of any item has changed.
Let's split it into two tasks:
TotalWeight should be recalculated when items are added, removed, or items' weight is changed. We need to handle those events.
Raise FruitBasket.PropertyChanged event
I have splitted these two tasks into two classes in order to follow Single Responsibility Principle:
1) - this handles items' PropertyChanged events:
public abstract class ExtendedObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
protected override void ClearItems()
{
foreach (var item in Items) item.PropertyChanged -= ItemPropertyChanged;
base.ClearItems();
}
protected override void InsertItem(int index, T item)
{
item.PropertyChanged += ItemPropertyChanged;
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
this[index].PropertyChanged -= ItemPropertyChanged;
base.RemoveItem(index);
}
protected override void SetItem(int index, T item)
{
this[index].PropertyChanged -= ItemPropertyChanged;
item.PropertyChanged += ItemPropertyChanged;
base.SetItem(index, item);
}
abstract void ItemPropertyChanged(object sender, PropertyChangedEventArgs e);
}
2) - this recalculates TotalWeight when necessary
public class FruitBasket : ExtendedObservableCollection<IFruit>
{
protected override void ItemPropertyChanged(object sender, PropertyChangedEventArgs e){
UpdateWeight();
OnPropertyChanged("TotalWeight")
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
UpdateWeight();
OnPropertyChanged("TotalWeight")
base.OnCollectionChanged(e);
}
}
Of course your Fruit should implement INotifyPropertyChanged interface. You will find plenty of examples how to do it. It is very simple.
I found the root(s) of the problem(s). I'll start with the most obvious:
I wasn't as diligent in assigning datacontext in the UI for the Fruit Basket observable collection object itself as I was the for the members of its collection (OrangeContainer and AppleContainer). In the initialization of the UI window, assigning datacontext to the ListView objects is second nature. I wasn't quite matching the right node's datacontext in the XAML to the Fruit Basket object in the initialization method in the code behind (I really should have checked that earlier).
Because of the misaligned assignments of datacontext/binding, between the XAML and initialization method, the propertychanged event was never firing for my fruit basket observable collection like it was for the Apple and Orange objects inside the OrangeContainer and AppleContainer collections that were members of FruitBasket. So, in the Orange class declaration we'd have this:
public class Orange : INotifyPropertyChanged, IFruit
And the implementation like so
public event PropertyChangedEventHandler PropertyChanged;
public void PropChange(string prop)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
And when the PropChange method was called in the Weight property setter, this.PropertyChanged would not be null, and everything would work fine.
The FruitBasket class was a bit more tricky. Because of the aformentioned issue of improper matching in the UI Code, this.PropertyChanged would return null every time I tried to notify a change in property. However, it got a bit more confusing because unlike the Orange or Apple classes, it inherits ObservableCollection (ObservableCollection in the declaration if we want to be specific). I know ObservableCollection is really just a Collection class that implements INotifyPropertyChanged and INotifyCollectionChanged interfaces. It's really nice to see the plumbing now that .NET is open source (praise the lord)
http://referencesource.microsoft.com/#System/compmod/system/collections/objectmodel/observablecollection.cs
In any case, implementing this became more confusing, because I kept seeing this:
Warning 1 'TestingObsColNotify.FruitBasket.PropertyChanged' hides inherited member 'System.Collections.ObjectModel.ObservableCollection.PropertyChanged'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword. C:\Testing VS Project\TestingObsColNotify\TestingObsColNotify\FruitBasket.cs 60 50 TestingObsColNotify
I still see this, but my implementation works, because while it was a result of the inheritance from INotifyProperty changed via ObservableCollection as seen in my original class declaration
public class FruitBasket : ObservableCollection<IFruit>
This was just shy of the last element need to make everything work, which was adding the INotifyPropertyChanged to the class itself like so:
public class FruitBasket : ObservableCollection<IFruit>, INotifyPropertyChanged
It seems a bit redundant and inelegant but I didn't get very far trying to override and wrestle with the inheritance of INotifyPropertyChanged from ObservableCollection (Or as best as I can understand).
So there we have it, everything works now, sans MVVM. I'll certainly move on to that pattern later, but it's nice to have resolved this issue instead of lazily just re-assigning the contents of controls in the code behind methods on the UI side of things.
Thank you to those who came in here and contributed, I appreciate you taking the time to respond.
If using binding, Add the interface INotifyPropertyChanged to your class. If you have ReSharper installed, accept the recommendation to implement the interface. Then, whenever you want to update any text box, call PropertyChanged with the name of the property TotalWeight, see https://softwareengineering.stackexchange.com/questions/228067/where-do-put-inotifypropertychanged-interface-in-model-or-viewmodel. Whenever you update any of the ObservableCollections, manually update the TotalWeight, then call the aforementioned PropertyChanged to tell the UI to update itself. I've used this technique to push updates from the ViewModel into the View (i.e. from the class into the XAML) for some fairly complex scenarios, it works very well.
I'd also recommend following the learning curve for MVVM, projects written in that way tend to be more scalable, are easier to maintain, and just easier to work with.

StyleCop and MultipleGenericInterfaces and CA1033

I have an interface IBakeable<T> which has an event OnCooked
A couple of data classes Pie and Bread
And I have a classes which implements IBakeable<Pie> and IBakeable<Bread>
In order to implement this I think I have to implement the interfaces explicitly
public class BaseHeatSource: IBakeable<Pie>, IBakeable<Bread>
{
private event EventHandler OnPieCooked;
event EventHandler IBakeable<Pie>.OnCooked
{
add {OnPieCooked+= value;}
remove {OnPieCooked-= value;}
}
private event EventHandler OnBreadCooked;
event EventHandler IBakeable<Bread>.OnCooked
{
add {OnBreadCooked+= value;}
remove {OnBreadCooked-= value;}
}
}
And the class is inherited
public class Oven: BaseHeatSource
{
}
public class Fire: BaseHeatSource
{
}
Now I get the equivalent of:
CA1033 Interface methods should be callable by child types
Make
'BaseHeatSource' sealed (a breaking change if this class has
previously shipped), implement the method non-explicitly, or implement
a new method that exposes the functionality of
'IBakeable.OnCooked.add(EventHandler)' and is visible to derived
classes.
Msdn states:
If the derived type re-implements (explicitly) the inherited
interface method, the base implementation can no longer be accessed.
The call through the current instance reference will invoke the
derived implementation; this causes recursion and an eventual stack
overflow.
Note that adding
protected void AddOnBreadCookedHandler(EventHandler handler)
{
this.OnBreadCooked += handler;
}
does not resolve the rule.
Do I have to suppress this rule? or is there a way of fixing it?
Solved this via Composition...
Created an abstract Bakeable<T> class as
public abstract Bakeable<T> : IBakeable<T>
{
event EventHandler OnCooked;
public Cooked(object sender)
{
var handler = this.OnCooked;
if (handler != null)
{
handler(sender, new EventArgs());
}
}
}
Created an IBakeable<Pie> property which is the Bakeable<Pie> class
for firing the event called BakeablePie.Cooked;
for attaching to the event it simply became oven.BakeablePie.OnCooked += customerWantsPie.Eat
I'm really not sure how this helps me in regard to Hans' comment in order to cook more things I need to add more properties

Custom (derived) List<T>

Feel free to load your guns and take aim, but I want to understand why you shouldn't do this.
I have created a custom class designed to replace any instances of List (which I use to update XML objects behind them):
public class ListwAddRemove<T> : List<T> {
public event EventHandler<ListModifyEventArgs> OnAdd;
public event EventHandler<ListModifyEventArgs> OnRemove;
new public void Add(T o) {
base.Add(o);
if (OnAdd != null) {
OnAdd(this, new ListModifyEventArgs(o));
}
}
new public void Remove(T o) {
base.Remove(o);
if (OnRemove != null) {
OnRemove(this, new ListModifyEventArgs(o));
}
}
}
The idea is whenever I add or remove an item from this list my bound events will fire and I can deal with the XML behind automatically.
This works like a charm, so far so good.
But how do I handle a conversion between object.ToList() and my derived version?
A lot of people are saying you should derive from Collection instead... why?
You should derive from Collection<T> because it's designed to allow you to override InsertItem, and RemoveItem to add custom behavior such as what you're doing (also SetItem, to add custom behavior when changing an existing item).
It can therefore be used as an IList<T>, and any insertion/removal will automatically use the customisation.
In your case, anyone who casts to IList<T> or the base class List<T> will bypass your custom Add/Remove functionality.
Collection<T> also provides a constructor to wrap an existing list. You can expose this from your derived class to wrap a list generated by Enumerable<T>.ToList().
UPDATE
Whats the syntax to expose the constructor please?
Very simple:
public class ListwAddRemove<T> : Collection<T>
{
public ListwAddRemove<T>()
{
}
public ListwAddRemove<T>(IList<T> list) : base(list)
{
}
... implementation of overrides for InsertItem, SetItem, RemoveItem ...
}
Then use it as follows:
IList<SomeType> list = ....ToList();
ListwAddRemove<SomeType> myList = new ListwAddRemove<SomeType>(list);
For one,
void DoSomeAddingToList(List<int> list) {
list.Add(1);
}
var list = new ListwAddRemove<int>();
DoSomeAddingToList(list);
will not trigger the events. That might lead to strange effect, especially if you're not the only one using the class.
List<T> defines a very specific behaviour for Add and Remove (since it's a concrete class), and users might rely on exactly this behaviour.
I think this is generally true for using a new modifier, so this language feature should be used with caution, especially on public methods.
As others have mentioned, implementing IList<T> (using delegation/aggregation) is probably the better choice.

How to implement event

class Foo(){
public List<string> SomeCollection;
}
I need to implement an event which can fires when something added or removed from the Collection. How to do this?
List<T> has no notification support. You could look at BindingList<T>, which has events - or Collection<T>, which can be inherited with override methods.
If you want to expose the event at the Foo level, perhaps something like below - but it may be easier to leave it on the list:
class Foo{
public event EventHandler ListChanged;
private readonly BindingList<string> list;
public Foo() {
list = new BindingList<string>();
list.ListChanged += list_ListChanged;
}
void list_ListChanged(object sender, ListChangedEventArgs e) {
EventHandler handler = ListChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
public IList<string> SomeCollection {get {return list;}}
}
Take a look at the BindingList and ObservableCollection classes (in the System.ComponentModel and System.Collections.ObjectModel namespaces respectively) - either one should do the job well for you.
Note that the two classes generally provide the same functionality, but they do differ slightly. BindingList is typically more suitable for data-binding/UI purposes (hence it's name), since it allows the option to cancel updates and such. However, ObservableCollection is possibly more appropiate in your case, since you're just interested in being notified of changes (it would seem), and the class was designed purely from that perspective. The fact that they exist in very different namespaces sort of hints at this. If you want the precise details on the similarities and differences, I recommend you inspect the linked MSDN docs.
You might take a look at this tutorial on making your own custom events.
You can do this by using an ObservableCollection instead of a List.
basic one...
here is a good link
public class Foo
{
private List<string> _SomeCollection;
public event EventHandler Added;
public void Add(string item)
{
SomCollection.Add(item);
OnAdd();
}
private void OnAdd()
{
if (Added != null)
{
Added.Invoke(this, EventArgs.Empty);
}
}
}

Categories