Working with Silverlight Dependency Properties
Use Silverlight dependency properties to extend or write a custom Silverlight control
February 22, 2011
XAML provides a great way to define layout controls, user input controls, shapes, colors, and data-binding expressions in a declarative manner. There's a lot that goes on behind the scenes to make XAML work, and an important part of that magic is the use of dependency properties. If you want to bind data to a property, style it, animate it, or transform it, then the property has to be a dependency property to work properly. If you've ever positioned a control in a Canvas using Canvas.Left or placed a control in a specific Grid row using Grid.Row, you've used an attached property (which is a specialized type of dependency property). Dependency properties play a key role in XAML and the overall Silverlight framework.
In this article I'll provide an introduction to dependency properties and show how you can create and use them. If you're looking to extend an existing Silverlight control, write a custom control from scratch, or even add new functionality to a user control to make it more flexible, you'll need to know about dependency properties. Before we go too far, let's start at the beginning and look at why you need dependency properties and how you can create them.
What Are Dependency Properties?
I have the opportunity to teach a lot of people about Silverlight, and a topic that definitely confuses people initially is the concept of dependency properties. I confess that when I first heard about them, my initial thought was, "Why do we need a specialized type of property?" While you can certainly use standard CLR properties in Silverlight applications, Silverlight relies heavily on dependency properties for just about everything it does behind the scenes. In fact, dependency properties are an essential part of the data-binding, template, style, and animation functionality available in Silverlight. They simply back standard CLR properties.
Any property that you bind, style, template, animate, or transform must be a dependency property in Silverlight applications. You can programmatically bind values to controls and work with standard CLR properties, but if you want to use the built-in binding expressions available in XAML (one of my favorite features) or the Binding class available through code, dependency properties are a necessity. Dependency properties aren't needed in every situation, but if you want to customize your application much, you'll eventually end up needing them. For example, if you create a custom user control and want to expose a property that consumers can use to change the background color, you have to define it as a dependency property if you want bindings, styles, and other features to be available for use.
Now that we've reviewed the overall purpose of dependency properties, let's take a look at how you can create them.
Creating Dependency Properties
When .NET Framework first came out, you had to write backing fields for each property that you defined. Figure 1 shows an example of a standard CLR property with a backing field.
Although .NET 2.0 added auto-implemented properties (for example: public Brush ScheduleBackground \{ get; set; \}) where the compiler would automatically generate the backing field used by get and set blocks, the concept is still the same as shown in Figure 1: A property acts as a wrapper around a field. Silverlight dependency properties replace the _ScheduleBackground field shown in Figure 1 and act as the backing store for a standard CLR property.
The following code shows an example of defining a dependency property named ScheduleBackgroundProperty:
public static readonly DependencyProperty ScheduleBackgroundProperty =
DependencyProperty.Register("ScheduleBackground", typeof(Brush),
typeof(Scheduler), null);
As you look through this code, the first thing that may stand out is that the definition for ScheduleBackgroundProperty is marked as static and readonly and that the property appears to be of type DependencyProperty. This is a standard pattern that you'll use when working with dependency properties. You'll also notice that the property explicitly adds the word "Property" to the name, which is another standard you'll see followed. In addition to defining the property, the code makes a call to the static DependencyProperty.Register method and passes the name of the property to register (ScheduleBackground in this case) as a string. The property's type, the type of the class that owns the property, and a null value (more on the null value later) are also passed. In this example, a class named Scheduler acts as the owner.
The code handles registering the property as a dependency property with the call to Register(), but a little more work has to be done to allow a value to be assigned to and retrieved from the dependency property. Figure 2 shows the complete code that you'll typically use when creating a dependency property. You can find code snippets that greatly simplify the process of creating dependency properties out on the web. The MVVM Light download comes with built-in dependency properties snippets as well.
The standard CLR property code shown in Figure 2 should look familiar, since it simply wraps the dependency property. However, you'll notice that the get and set blocks call GetValue and SetValue methods, respectively, to perform the appropriate operation on the dependency property. GetValue and SetValue are members of the DependencyObject class, which is another key component of the Silverlight framework. Silverlight controls and classes (e.g., TextBox, UserControl, CompositeTransform, DataGrid) ultimately derive from DependencyObject in their inheritance hierarchy, so that they can support dependency properties. Figure 3 shows the inheritance hierarchy for the TextBox control.
Figure 3: TextBox inheritance hierarchy
Dependency properties defined in Silverlight controls and other classes tend to follow the pattern shown in Figure 2. They have a standard property that wraps a registered dependency property and allows a value to be assigned and retrieved. If you need to expose a new property on a custom control that supports data binding expressions in XAML, you'll follow this same pattern. Dependency properties are extremely useful once you understand why they're needed and how they're defined.
Detecting Changes and Setting Defaults
When working with dependency properties, there will be times when you want to assign a default value or detect when a property changes so that you can keep the user interface in sync with the property value. Silverlight's DependencyProperty.Register() method provides a fourth parameter that accepts a PropertyMetadata object instance (it was the null value back in Figure 2). PropertyMetadata can be used to hook a callback method to a dependency property. The callback method is called when the property value changes. PropertyMetadata can also be used to assign a default value to the dependency property. By assigning a value of null for the final parameter passed to Register() in Figure 2, you're telling the property that you don't care about any changes and don't have a default value to apply. Figure 4 shows the different constructor overloads available with the PropertyMetadata class.
Figure 4: PropertyMetadata constructor overloads
There are many situations where you need to know when a dependency property changes or where you want to apply a default. Performing either task is easily accomplished by creating a new instance of the PropertyMetadata class and passing the appropriate values to its constructor. Figure 5 shows an enhanced version of the initial dependency property code shown in Figure 2 that demonstrates these concepts.
The code in Figure 5 wires ScheduleBackgroundProperty to a property change callback method named ScheduleBackgroundChanged. What's interesting is that this callback method is static (as is the dependency property), so it gets passed the instance of the object that owns the property that has changed (otherwise we wouldn't be able to get to the object instance). In this example, the dependency object is cast to a Scheduler object and its Background property is assigned to the new value of the dependency property. The code also handles assigning a default value of LightGray to the dependency property by creating a new instance of a SolidColorBrush.
The Importance of Dependency Properties
In this article you've seen the role of dependency properties and how they can be defined in code. They play a big role in XAML and the overall Silverlight framework. You can think of dependency properties as replacements for fields that you'd normally use with standard CLR properties. In addition to a discussion on how dependency properties are created, you saw how to use the PropertyMetadata class to define default dependency property values and hook a dependency property to a callback method.
The most important thing to understand with dependency properties (especially if you're new to Silverlight) is that they're needed if you want a property to support data binding, animations, transformations, and styles properly. Anytime you create a property on a custom control or user control that has these types of requirements, you'll want to pick a dependency property over a standard CLR property with a backing field.
There's more that can be covered with dependency properties, including a related property called an attached property. I'll provide a look at that topic in a future article.
Dan Wahlin is a Microsoft MVP and founded The Wahlin Group, which specializes in ASP.NET MVC, jQuery, Silverlight, and SharePoint consulting and training solutions. Dan has written several books on .NET and writes for several different technical magazines. He blogs at weblogs.asp.net/dwahlin
.
Read more about:
MicrosoftAbout the Author
You May Also Like