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?
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.
Related
I have a class that implements INotifyPropertyChanged and also has its own concept of properties, a little bit like this:
class sealed MyClass : INotifyPropertyChanged
{
private Dictionary<string, object> _properties;
public object GetProperty(string name)
{
return _properties[name];
}
public object SomeProperty
{
get
{
return GetProperty("SomeProperty");
}
}
}
Note that some common properties also have C# accessors.
I want to raise an event to notify others that a property (whose value is accessed through the GetProperty method) has changed even if this property does not have a corresponding C# accessor.
Assuming that there is no risk of a clash (i.e. inadvertantly raising a property changed event for a C# property whose value has not changed - note that this class is sealed), is there any reason why I can't just use the INotifyPropertyChanged.PropertyChanged event to notify others, or should I add my own event for this purpose?
I would suggest that you add your own. Although there's no specific reason that the code would break, INotifyPropertyChanged has a specific intended usage, and one that is understood by a lot of potential consumers. I'd not personally write code that contradicts that, just in case.
As far as I know, there's no harm in raising PropertyChanged for a property that isn't there, but I don't think it's very useful either. INotifyPropertyChanged is mostly used by data binding systems (in WinForms, WPF, Silverlight...), and these system don't know how to access your custom "properties".
Now, what you could do is implement ICustomTypeDescriptor to provide access to your custom properties in a standard way. This works with Windows Forms and WPF, I'm not sure about other frameworks.
Of course, if your intention is not to use this for data binding, you can still use this event to notify consumers of your class that values have changed.
Technically, it depends on who has subscribed to PropertyChanged and what they do in response to the PropertyChanged event. If they don't try to read a .NET "property", you're fine.
But, that really introduces a coupling and the use of INPC is trying to decouple. INPC at the general case assumes PropertyChanged means a .NET property (of a given name) has changed and is expected to be the case for all places that use INPC. i.e. you implement INPC because you want to use your object in any place that accepts INPC. If your object can't do that, then it violates the implied contract of INPC. If you have a PropertyChanged handler that does something different than read a property and you're re-using INPC just because it's there then maybe writing you own interface is a good idea.
This is not so much a question, and more something that struck me as odd: the ObservableCollection class is generic, but the CollectionChanged event that it raises when changed to the collection occur is not. That means within the event handler, you have to cast all the objects in the NewItems/OldItems collections you get from the event args object to the proper item type yourself.
But shouldn't it have been simply possible to make that event generic? Something like
public virtual event NotifyCollectionChangedEventHandler<T> CollectionChanged;
with
public delegate void NotifyCollectionChangedEventHandler(object sender, NotifyCollectionChangedEventArgs<T> e);
and then
public class NotifyCollectionChangedEventArgs<T> {
// ...
public IList<T> NewItems { get; }
public IList<T> OldItems { get; }
After all, the type parameter T is already determined by the declaration of the ObservableCollection in question, which means you should never be able to add any objects that are not of type T (or compatible to it) to the collection anyway. So all of this should be perfectly type safe, and save us the trouble of having to cast the objects inside the event handler into whatever type we know they should have anyway.
Is there a reason for not doing it this way that I am missing? I.e. is this a conscious design desicion, or just an oversight?
The INotifyCollectionChanged interface, along with INotifyPropertyChanged, were designed exclusively for use by the WPF framework. Since WPF is loosely typed, I'm guessing that generics weren't part of that design. I don't think INotifyCollectionChanged was designed to be consumed directly. Whereas, ObservableCollection<T> is designed to be consumed by C# code, which is strongly typed, therefore generics featured in the design.
I know the class implementing an interface must implement all its method. But what does event inside the interface mean?
It means that the type must implement the event - so that clients can subscribe to those events.
Think of events as pairs of methods (add/remove) just as properties have get/set. Just as you can have properties in interfaces, you can have events: the implementation has to provide the appropriate add/remove methods and the metadata to tie them to the event. In C# this can be done using field-like events:
public event EventHandler EventFromInterface;
or with explicit add/remove methods:
public event EventHandler EventFromInterface
{
add { ... }
remove { ... }
}
It means anything implementing that interface must raise that event. Pretty much the same as a Method or Property within an interface.
There are many questions about events in interfaces. Here are a couple:
Raising events in Interfaces
C# - Events and Interfaces
As interfaces are used to enforce a contract for implementation, this makes no sense to me, because it doesn't enforce the actual raising of the event. The specific implementation can always have the event, but not raise it anywhere. Consider the following:
public interface INotifyPropertyChanged
{
event PropertyChangedEventHandler PropertyChanged;
}
public class SomeClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string SomeProperty
{
get { return this.someField; }
set { this.someField = value; }
}
private string someField;
}
The thing above will compile and work, but even if I subscribe to the PropertyChanged event, nothing will happen. What would be a way to enforce that an event is actually raised, and if not, why have events in interfaces in the first place?
The interface contract defines that the event must be implemented, and may be subscribed to. Actually raising it is left up to the concrete implementation. There is no way to enforce that an event is raised. What if the circumstances required to trigger the event never occur?
You misunderstood the concept of an interface: It explicitely declares an object's behaviour, but it does not enforce it in any way. The programmer is still fully responsible for implementing the interface behaviour correctly. This is nothing a compiler could do.
It's the same as with other contracts, when the involved parties just refuse to adhere to its content...
Thomas
The same can be said for methods: the interface doesn't enforce that they do anything useful, they could all just return null/0/void. Similarly, the implementation is free to raise/not raise an eevent whenever it wants.
If you want to ensure that certain behaviors are executed, then implement them as a abstract base class. Make the public methods public and non-virtual, and then have them call virtual protected methods and raise events. This is the template-method pattern, and it lets you have more control over sub-class behavior.
Another situation in which PropertyChanged will never be raised is the circumstance that the property is never changed! Do you want to be warned about that too?
But seriously, interfaces are for enforcing a contract of supported behaviour - if you want to enforce actual behaviour (ie - IF this happens THEN that happens), use (automated) testing.
Integration tests or unit tests are the only way that I see you could do what you want. You are then making sure in your implementation the behaviour is what you expect.
Nothing's really enforced in an interface, you can just put empty method stubs in after all (as long as you return something of the correct type).
The best way to ensure things like this are behaving correctly is to have plenty of tests.
One thing you might consider is code contracts, which let you specify conditions which have to be true at certain times, and these are actually enforced. However, AFAIK this doesn't work with events.
My take is that this provides a means of subscribing to an event when all you have reference to is the interface an object is using. Otherwise you would not be able to subscribe to the event at all. You are correct that it does not guarantee that the event is used.
INotifyPropertyChanged x = new SomeClass();
x.PropertyChanged += MyHandler; //you get the idea
If the event were not defined on INotifyPropertyChanged you would not be able to add your handler.
The FrameworkElement object has DataContextChanged event. However, there is no OnDataContextChanged method that can be overridden.
Any ideas why?
If a method is virtual, then the user has the option to either augment the base functionalty by calling the base class method or replace the base class functionality by failing to call the base class method. For OnEvent() methods, if you don't call the base class method then the event will not be raised (that's the responsibility of the base class method.) If the base class performs some kind of state management inside of the OnEvent method, this means that the derived class can accidentally invalidate the state of the object if the user chooses to omit a call to the base class method. Documentation can specify "please always call the base class method", but there's no way to enforce it.
When I see an event that doesn't have a virtual OnEvent() method, I usually assume the method performs some kind of internal state management and the designers of the class want to guarantee their state management runs. This isn't the case in FrameworkElement, and it's not the only event that doesn't follow the pattern, so I'm curious what the reasoning is.
I dug around in Reflector to see if I could discover a reason. There is an OnDataContextChanged() method, but it's a dependency property change handler and doesn't follow the standard event pattern. This is probably the reason for not making it protected virtual. It's non-standard, so it would be confusing. It's static, so you wouldn't be able to override it anyway. Since it's called automatically by the dependency property framework and you are unable to override it, I believe we have the reason why it's private instead of static virtual.
You could use a different pattern to expose the normal event pattern:
class FrameworkElement
{
// difference: use DataContextPropertyChanged as the change callback
public static readonly DependencyProperty DataContextProperty = ...
protected virtual void OnDataContextChanged(...)
{
// raise the DataContextChanged event
}
private static void DataContextPropertyChanged(...)
{
((FrameworkElement)d).OnDataContextChanged(...);
}
}
My guess why they didn't do this? Usually you call OnEvent() to raise the event. The event is automatically raised when DataContext changes, and it doesn't make sense for you to raise it at any other time.
Good question.
I'm just guessing, but looking in Reflector I'd say it's just laziness, perhaps with a pinch of (unfounded?) performance concerns. FrameworkElement has a generic EventHandlersStore which is responsible for maintaining event information (delegates) for a whole bunch of events. The add and remove logic in the CLR events (such as DataContextChanged) simple call into the EventHandlersStore with the appropriate key.
There is a generic RaiseDependencyPropertyChanged method that is called to raise all different sorts of events. There is also a private OnDataContextChanged method that calls the RaiseDependencyPropertyChanged method. However, it is static and registered as part of the d-prop metadata.
So, in short, I see no technical reason not to include an overridable OnDataContextChanged method. Just looks like a short-cut in implementation to me.
Is this merely academic, or are you trying to achieve something here?
Silverlight Note:
At of Silverlight Beta 4 there IS no DataContextChanged event (well its not public at least).
The Microsoft Connect bug report has been marked as 'Fixed' but with no indication of what that actually means.
In the meantime you need a workaround such as this one from CodeProject - which is very simple and should be easy to switch out if Microsoft ever actually makes the event public.
Dependency properties usually don't have corresponding virtual methods for raising the event because it's expected that the change events will be managed by the dependecy property system itself.
What you can override however, to handle any dependency property changing is DependencyObject.OnPropertyChanged like so:
class MyClass : FrameworkElement {
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) {
base.OnPropertyChanged(e);
if (e.Property == FrameworkElement.DataContextProperty) {
// do something with e.NewValue/e.OldValue
}
}
}