ObservableCollection PropertyChanged event
Asked Answered
M

4

11

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 is the UI notified of PropertyChanged events (e.g. Count property)? Note that I cannot do it in a code-behind.

My head is spinning, can someone please enlighten me?

Marindamarinduque answered 16/6, 2009 at 18:58 Comment(0)
D
15
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.

Duna answered 16/6, 2009 at 19:14 Comment(2)
Sorry I must have overlooked your answer before I posted my own.Parricide
@jpierson: lol, I answered 1 year, 9 months, 21 days ago. Ya think you'd notice. (btw, I don't care, it doesn't matter, answer away--you've got some good info in there).Duna
P
11

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.

Parricide answered 29/3, 2011 at 20:59 Comment(1)
Many thanks for this excellent, well-researched answer ! Your answer gave me the 'clue' I needed to get property notifications working with an ObservableCollection<T>. Wish I had more than one vote to award :)Jitter
D
1

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.

Datcha answered 28/12, 2012 at 6:23 Comment(0)
R
0

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.

Reserpine answered 16/6, 2009 at 19:16 Comment(2)
ObservableCollection implements both a publicly accessible explicitly implemented PropertyChanged event along with a protected one. Remember, it would be impossible for a protected member to satisfy the implementation of an interface member so the protected one in this case is a red herring so to speak.Parricide
About adding another property instead of subclassing ObservableCollection. I agree that it's probably more simplistic approach and if you need re-use try the mini-ViewModel pattern instead changing ObservableCollection. I went down the route of creating a SelectableCollection class that does just as the OP is attempting and I can say it simplified a lot of binding situations that I'm not sure how to do otherwise but I've change a few other places in my code recently to move away from this SelectableCollection because it's just easier to make this an extension of the ViewModel.Parricide

© 2022 - 2024 — McMap. All rights reserved.