The FrameworkElement object has DataContextChanged event. However, there is no OnDataContextChanged method that can be overridden.
Any ideas why?
The FrameworkElement object has DataContextChanged event. However, there is no OnDataContextChanged method that can be overridden.
Any ideas why?
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?
HTH, Kent
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.
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
}
}
}