More change notification for Dependency Properties


In an earlier post I wrote a little on change notification in relation to dependency properties that you do not own, like those found on a TextBlock or Label. In that post I walked you though using a DependencyPropertyDescriptor, which in essence enables you to add an event handler to a ValueChanged event for any dependency property.

Here's a code example to help illustrate the approach:

DependencyPropertyDescriptor descriptor =
    (UIElement.VisibilityProperty, typeof(UIElement));

  (this.labelShowHide, new EventHandler(VisibilityChanged));

The example code above creates a instance of a descriptor for the Visibility dependency property owned by the UIElement class, and then adds an event handler called VisibilityChanged. As I pointed out in the previous post there are two issues with this approach:

  • The event handler delegate is of type EventHandler and therefore only passes a simple EventArgs, losing the rich information provided by a typical dependency property change notification via the DependencyPropertyChangedEventArgs, which includes data such as the old and new values of the property.
  • The descriptor approach has a potential memory leak if you're not careful. This is because a reference to your event handler forever roots your class, in a Garbage Collector sense, because the reference is ultimately stored a static Hashtable.

I also think that there is a third, less concrete, issue with this approach: I don't like it. It just feels all wrong; hacking into the hooks provided for the tooling, which does all work fine, but it left me feeling a little dirty ;) There has to be a better way! And there is...

I discovered the proper way to do this kind of change notification a short while ago; however, this is the first opportunity I've had to write up - primarily for me to remember it in the future. The answer is actually quite simple, and related to the approach you would take extending an existing control by using inheritance... a Metadata override!

    new FrameworkPropertyMetadata(
        new PropertyChangedCallback(DetailedPropertyChanged)));

The example code above replaces the property metadata for the Opacity dependency property for all TextBlock control instances in scope. The new metadata provides an event handler for change notification, and unlike the descriptor approach the callback is a true dependency property changed event handler:

private void DetailedPropertyChanged(
    DependencyObject d,
    DependencyPropertyChangedEventArgs e)
        "{0}: old value was '{1}' and the new value is '{2}'",

Providing access to the old and new values, information about the source and the dependency property that has actually been changed. You now have all the information you could possibly need to handle the changed property; this approach has none of the drawbacks of the previous descriptor approach.

The only thing to be aware of when using this approach is that it impacts the overridden dependency property for all instances of the target type that are in scope; but you have a reference to the target object and property in the event handler, so this is easily coded for via a switch or if statement - it's just something to be aware of.

Here's a simple application that helps to illustrate the approach described here. If you have any comments or questions please leave a comment.


Joe said...

Is there a way to use the reference to the dependency property in e.Property to modify the value of the property on the dependencyObject d? For example, catching invalid values like this:
if(e.NewValue < 0)
d.( = 0

(I know this won't compile, but I think it conveys what I'm trying to do.)

Joe said...

I'm running into problems using your method. See this forum post: