D is for... Dependency Property

DDependency properties came on the scene with .NET 3.0 to support a variety of functionality in Windows Presentation Foundation (WPF), but the construct makes an appearance in Windows Workflow Foundation (WF) as well.

At first blush, it may seem to complicate your orderly world of simple CLR properties, but the dependency property construct is what provides much of the functionality and power of WPF including

  • styling,
  • animation,
  • data binding,
  • property value inheritance,
  • change notification via property triggers, and
  • support for declarative markup, namely XAML.

Dependency properties are not a native .NET construct but rather additional plumbing that the WPF (and WF) runtimes provide over the basic, built-in properties you're familiar with in C# and Visual Basic.

For the rest of this article, let's dissect a representative WPF dependency property, using an example I borrowed from Adam Nathan's Windows Presentation Foundation Unleashed.

    1: public class Button : ButtonBase
    2: {
    3:     public static readonly 
    4:         DependencyProperty IsDefaultProperty;
    5:  
    6:     static Button()
    7:     {
    8:         Button.IsDefaultProperty = DependencyProperty.Register(
    9:             "IsDefault",
   10:             typeof(bool),
   11:             typeof(Button),
   12:             new FrameworkPropertyMetadata(
   13:                 false,
   14:                 new PropertyChangedCallback(OnIsDefaultChanged)));
   15:     }
   16:  
   17:     public bool IsDefault
   18:     {
   19:         get { return (bool)GetValue(Button.IsDefaultProperty); }
   20:         set { SetValue(Button.IsDefaultProperty, value); }
   21:     }
   22:  
   23:     private static void OnIsDefaultChanged(
   24:         DependencyObject o,
   25:         DependencyPropertyChangedEventArgs e)
   26:     {
   27:         // code to handle property change
   28:     }
   29: }

Here we're looking at the IsDefault property of a basic Button control, which you may not even have realized is dependency property!  That's actually the great thing about them: you typically don't have to worry that they are there, since you'll reference them in your code just as you would use a regular .NET property. 

One of the first things to note is that dependency properties (in WPF) can only be declared on classes that inherit from the System.Windows.DependencyObject class.  As you can see from the hierarchy below, Button, as well as all of the FrameworkElement classes, descends from DependencyObject, so it's not a particularly difficult pre-condition to meet.

image

A dependency property is declared as a public, static field with a suffix of "Property" (lines 3-4).  The suffix is a convention; however, it's highly recommended as certain inherent capabilities, as well as tooling support, may break if you deviate from the naming convention. 

This field is then associated with a traditional .NET property (lines 17+) via its getter and setter.   The getter and setter connect with the dependency property by invoking the GetValue and SetValue methods that are inherited from the DependencyObject ancestor class.  It's these two methods that interface with a internal sparse storage system that actually contains the per-instance values of the dependency property (which itself is static!).  By the way, strictly speaking, these traditional .NET property wrappers are not even required, since you can call GetValue/SetValue from anywhere (they're public after all), but they make reading and writing the code easier as well as enable XAML compilation. 

NOTE!   At runtime, these getters and setters are bypassed when you set properties in XAML. You shouldn't include any code other than the GetValue/SetValue calls in them, because it won't get executed for properties that are set declaratively in XAML.

The instantiation of the dependency property (lines 8+) occurs via the static Register method of the DependencyProperty class.  In this case, we're creating a property that belongs to the button.  As you may be aware though, WPF supports the concept of attached properties as well (essentially dependency properties defined on an element, but designed to be used by child elements in the UI hierarchy); for that, there is a RegisterAttached method.

The Register method can have as many as five arguments:

  • Property name (line 9) - the CLR property corresponding to the DependencyProperty field, generally having the same name with the suffix "Property".
  • Property type (line 10) - the type of the property.
  • Owner type (line 11) - the type of the property owner.  A property can actually be owned by multiple types (the AddOwner method of DependencyProperty would be used in that case).
  • Metadata (line 12) - behavior of this dependency property.  For instance,
    • Does a change in the data force the layout to change?
    • Does a change in the data force a repaint?
    • What's the default value? (line 13)
    • What method should be called when the property changes? (Line 14)
    • When should source data be updated in a binding scenario?
    • Is data binding even supported?
  • Validation Callback (not used in sample above) - callback function to provide additional property validation beyond simple type checking.

As a developer consuming classes with dependency properties, you technically don't need to know if something is a dependency property versus a plain CLR property to code to it; however, it certainly helps to understand behavior.  For one thing, you can't bind data to a property that's not a dependency property (luckily most properties that you'd want to bind to are dependency properties).  Another case where it's helpful to know if a property is a dependency property is when dealing with property value inheritance and precedence.

Dependency properties can be 'inheritable', meaning that a UI element will respect the value of a property defined on a parent element.   For instance, if you set the FontSize property on a Window, and place multiple Labels in that window, each Label will reflect the FontSize set on the Window.  If the Window's property is modified, all the Labels will automatically reflect the new value... generally.   I say 'generally' here because property value inheritance is just one aspect (or source) of a value for a given element's property.  Since properties can be affected by many sources - default values, triggers, styles, animations, explicit assignments, etc. - there's a well defined set of rules for determining the ultimate value of a dependency property when something that property depends on is changed.

That precedence system is covered well in the MSDN topic Dependency Property Value Precedence, and Jesse Liberty has a great post as well that explains it in a Silverlight context.

Now, if you're out there developing your own customer controls, you will definitely need to become intimately familiar with dependency properties to enable data binding, styling, animation and other capabilities that WPF developers using your custom control will expect.  If you are declaring your own dependency properties, be sure to take advantage of the nifty propdp snippet in Visual Studio to set up a template for the backing property and the Register method.

If you're looking for more detail on the topic of dependency properties, there's a great series of in-depth articles on MSDN examining the WPF Property System, in addition to the standard .NET Framework documentation on the various classes involved in the dependency property infrastructure.