Dependency 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
- 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.
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.
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
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/SetValuecalls 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
Register method can have as many as five arguments:
- Property name (line 9) – the CLR property corresponding to the
DependencyPropertyfield, 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
DependencyPropertywould 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.
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
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.