OK so I want to subclass ObservableCollection
to add a property to it. Unfortunately the PropertyChanged
event is protected. Basically I want to subclass it to have a SelectedItem
that I can bind to for lists in my MVVM WPF app.
Here's the skeleton of my class:
public class SelectableList<T> : ObservableCollection<T>
{
public T SelectedItem {get;set;}
}
But I cannot do the following:
SelectableList<int> intList = new SelectableList<int>();
intList.PropertyChanged += new PropertyChangedEventHandler(intList_Changed);
because of access restrictions. This causes me to ask a deeper question. How come the UI can get notified of PropertyChanged
events(e.g. Count property) and I can't do it in code-behind?
My head is spinning, can someone please enlighten me?
SelectableList<int> intList = new SelectableList<int>();
((INotifyPropertyChanged)intList).PropertyChanged +=
new PropertyChangedEventHandler(intList_Changed);
ObservableCollection implements INotifyPropertyChanged explicitly, which means you have to cast the instance to the interface before you can access the interface's methods, properties and events. As to why this is done, I don't know. The Binding markup extension doesn't "know" ObservableCollections or any other type. It checks types to see if they implement or extend specific interfaces/base classes (INPC, INCC, DependencyObject, etc) and so doesn't care if the interface is implemented explicitly.
ObservableCollection (int .NET 3.5) appears to implement the PropertyChanged event in an interesting way.
protected event PropertyChangedEventHandler PropertyChanged;
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged;
This means that the protected PropertyChanged event is likely only meant to be used for internal implementation. The other INotifyPropertyChanged.PropertyChanged event is the one that actually fulfills the implementation of the INotifyPropertyChanged interface as an explicit interface. Strangely I do not see any place within the ObservableCollection where the INotifyPropertyChanged.PropertyChanged is actually raised. This may signal that this was a bug in .NET 3.5 although I haven't tested to confirm whether for example a property changed event is raised for Count when an item is added to a collection but that appears to be how it is supposed to work.
In the .NET 4.0 implementation it appears that the INotifyPropertyChanged.PropertyChanged event instead hooks to the same private delegate used by the protected PropertyChanged event which may have been a bug fix. It is also possible this is just due to differences in how auto event implementations are handled in .NET 4.0.
Correction: I have verified that the INotifyPropertyChanged.PropertyChanged event is raised by ObservableCollection so the assumptions I made above based on results from using Reflector to look at the ObservableCollection implementation must be inaccurate. My guess is that reflector is doing something strange bug I have no proof of that yet.
So to get your example to work you would need to write for this to work would look like the example below just as Will has demonstrated in his answer.
SelectableList<int> intList = new SelectableList<int>();
((INotifyPropertyChanged)intList).PropertyChanged +=
new PropertyChangedEventHandler(intList_Changed);
Interesting right? Using explicit interfaces is mainly used to avoid inevitable collisions in members required for a given interface but they can be used to in a sense hide the existence of a member.
If you would like to raise property change events for your own custom properties that you introduce in your subclass look into overriding and/or calling the protected OnPropertyChanged method that ObservableCollection also implements. This technique is a well adopted standard and allows subclasses to raise events or handle events without having access to the underlying event delegate. It is generally preferred to use this technique too by the way instead of having a subclass hook event handlers to it's own base classes events. For more examples look at how events in various controls are implemented in WinForms and WPF.
I tried to add a new property in
public class ResultCollection<T> : ObservableCollection<T>
{
Boolean _val;
public Boolean Val
{
get
{
return _val;
}
set
{
_val= value;
OnPropertyChanged(new PropertyChangedEventArgs("Val"));
}
}
}
I really didn't notice that PropertyChanged is defined as protected. Finally moved Val property to ViewModel.
The UI can and does get notified. This is a restriction JUST with ObservableCollection, which defines the PropertyChanged event as protected.
FWIW, I think you're better off leaving ObservableCollection alone and just adding another property to your VM.