ENTERPRISE

Windows Presentation Foundation in .NET 4 : Understanding Dependency Properties

5/15/2013 1:38:45 AM

Dependency properties are a completely new implementation of properties—one that has a significant amount of added value. You need dependency properties to plug into core WPF features such as animation, data binding, and styles.

Most of the properties that are exposed by WPF elements are dependency properties. In all the examples you've seen up to this point, you've been using dependency properties without realizing it. That's because dependency properties are designed to be consumed in the same way as normal properties.

However, dependency properties are not normal properties. It's comforting to think of a dependency property as a normal property (defined in the typical .NET fashion) with a set of WPF features added on. Conceptually, dependency features behave this way, but that's not how they're implemented behind the scenes. The simple reason why is performance. If the designers of WPF simply added extra features on top of the .NET property system, they'd need to create a complex, bulky layer for your code to travel through. Ordinary properties could not support all the features of dependency properties without this extra overhead.

Dependency properties are a WPF-specific creation. However, the dependency properties in the WPF libraries are always wrapped by ordinary .NET property procedures. This makes them usable in the normal way, even with code that has no understanding of the WPF dependency property system. It seems odd to think of an older technology wrapping a newer one, but that's how WPF is able to change a fundamental ingredient such as properties without disrupting the rest of the .NET world.

1. Defining a Dependency Property

You'll spend much more time using dependency properties than creating them. However, there are still many reasons that you'll need to create your own dependency properties. Obviously, they're a key ingredient if you're designing a custom WPF element. However, they're also required in some cases if you want to add data binding, animation, or another WPF feature to a portion of code that wouldn't otherwise support it. Creating a dependency property isn't difficult, but the syntax takes a little getting used to. It's thoroughly different from creating an ordinary .NET property.

NOTE

You can add dependency properties only to dependency objects—classes that derive from DependencyObject. Fortunately, most of the key pieces of WPF infrastructure derive indirectly from DependencyObject, with the most obvious example being elements.

The first step is to define an object that represents your property. This is an instance of the DependencyProperty class. The information about your property needs to be available all the time, and possibly even shared among classes (as is common with WPF elements). For that reason, your DependencyProperty object must be defined as a static field in the associated class.

For example, the FrameworkElement class defines a Margin property that all elements share. Unsurprisingly, Margin is a dependency property. That means it's defined in the FrameworkElement class like this:

public class FrameworkElement: UIElement, ...
{
    public static readonly DependencyProperty MarginProperty;

    ...
}

By convention, the field that defines a dependency property has the name of the ordinary property, plus the word Property at the end. That way, you can separate the dependency property definition from the name of the actual property. The field is defined with the readonly keyword, which means it can be set only in the static constructor for the FrameworkElement, which is the task you'll undertake next.

2. Registering a Dependency Property

Defining the DependencyProperty object is just the first step. For it to become usable, you need to register your dependency property with WPF. This step needs to be completed before any code uses the property, so it must be performed in a static constructor for the associated class.

WPF ensures that DependencyProperty objects can't be instantiated directly, because the DependencyProperty class has no public constructor. Instead, a DependencyObject instance can be created only using the static DependencyProperty.Register() method. WPF also ensures that DependencyProperty objects can't be changed after they're created, because all DependencyProperty members are read-only. Instead, their values must be supplied as arguments to the Register() method.

The following code shows an example of how a DependencyProperty must be created. Here, the FrameworkElement class uses a static constructor to initialize the MarginProperty:

static FrameworkElement()
{
    FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(
      new Thickness(), FrameworkPropertyMetadataOptions.AffectsMeasure);

    MarginProperty = DependencyProperty.Register("Margin",
      typeof(Thickness), typeof(FrameworkElement), metadata,
      new ValidateValueCallback(FrameworkElement.IsMarginValid));
    ...
}

There are two steps involved in registering a dependency property. First, you create a FrameworkPropertyMetadata object that indicates what services you want to use with your dependency property (such as support for data binding, animation, and journaling). Next, you register the property by calling the static DependencyProperty.Register() method. At this point, you are responsible for supplying a few key ingredients:

  • The property name (Margin in this example)

  • The data type used by the property (the Thickness structure in this example)

  • The type that owns this property (the FrameworkElement class in this example)

  • Optionally, a FrameworkPropertyMetadata object with additional property settings

  • Optionally, a callback that performs validation for the property

The first three details are all straightforward. The FrameworkPropertyMetadata object and the validation callback are more interesting.

You use the FrameworkPropertyMetadata to configure additional features for your dependency property. Most of the properties of the FrameworkPropertyMetadata class are simple Boolean flags that you set to flip on a feature. (The default value for each Boolean flag is false.) A few are callbacks that point to custom methods that you create to perform a specific task. One—FrameworkPropertyMetadata.DefaultValue—sets the default value that WPF will apply when the property is first initialized. Table 1 lists all the FrameworkPropertyMetadata properties.

Table 1. Properties of the FrameworkPropertyMetadata Class
NameDescription
AffectsArrange, AffectsMeasure, AffectsParentArrange, and AffectsParentMeasureIf true, the dependency property may affect how adjacent elements (or the parent element) are placed during the measure pass and the arrange pass of a layout operation. For example, the Margin dependency property sets AffectsMeasure to true, signaling that if the margin of an element changes, the layout container needs to repeat the measure step to determine the new placement of elements.
AffectsRenderIf true, the dependency property may affect something about the way an element is drawn, requiring that the element be repainted.
BindsTwoWayByDefaultIf true, this dependency property will use two-way data binding instead of one-way data binding by default. However, you can specify the binding behavior you want explicitly when you create the binding.
InheritsIf true, the dependency property value propagates through the element tree and can be inherited by nested elements. For example, Font is an inheritable dependency property—if you set it on a higher-level element, it's inherited by nested elements, unless they explicitly override it with their own font settings.
IsAnimationProhibitedIf true, the dependency property can't be used in an animation.
IsNotDataBindableIf true, the dependency property can't be set with a binding expression.
JournalIf true, this dependency property will be persisted to the journal (the history of visited pages) in a page-based application.
SubPropertiesDoNotAffectRenderIf true, WPF will not rerender an object if one of its subproperties (the property of a property) changes.
DefaultUpdateSourceTriggerThis sets the default value for the Binding.UpdateSourceTrigger property when this property is used in a binding expression. The UpdateSourceTrigger determines when a databound value applies its changes. You can set the UpdateSourceTrigger property manually when you create the binding.
DefaultValueThis sets the default value for the dependency property.
CoerceValueCallbackThis provides a callback that attempts to "correct" a property value before it's validated.
PropertyChangedCallbackThis provides a callback that is called when a property value is changed.

3. Adding a Property Wrapper

The final step to creating a dependency property is to wrap it in a traditional .NET property. However, whereas typical property procedures retrieve or set the value of a private field, the property procedures for a WPF property use the GetValue() and SetValue() methods that are defined in the base DependencyObject class. Here's an example:

public Thickness Margin
{
    set { SetValue(MarginProperty, value); }
    get { return (Thickness)GetValue(MarginProperty); }
}

When you create the property wrapper, you should include nothing more than a call to SetValue() and a call to GetValue(), as in the previous example. You should not add any extra code to validate values, raise events, and so on. That's because other features in WPF may bypass the property wrapper and call SetValue() and GetValue() directly. (One example is when a compiled XAML file is parsed at runtime.) Both SetValue() and GetValue() are public.

NOTE

The property wrapper isn't the right place to validate data or raise an event. However, WPF does provide a place for this code; the trick is to use dependency property callbacks. Validation should be performed through the DependencyProperty.ValidateValueCallback shown previously, while events can be raised from the FrameworkPropertyMetadata.PropertyChangedCallback shown in the next section.

You now have a fully functioning dependency property, which you can set just like any other .NET property using the property wrapper:

myElement.Margin = new Thickness(5);

There's one extra detail. Dependency properties follow strict rules of precedence to determine their current value. Even if you don't set a dependency property directly, it may already have a value—perhaps one that's applied by a binding, style, or animation, or one that's inherited through the element tree. (You'll learn more about these rules of precedence in the next section, "How WPF Uses Dependency Properties.") However, as soon as you set the value directly, it overrides all these other influences.

At some point later, you may want to remove your local value setting and let the property value be determined as though you never set it. Obviously, you can't accomplish this by setting a new value. Instead, you need to use another method that's inherited from DependencyObject: the ClearValue() method. Here's how it works:

myElement.ClearValue(FrameworkElement.MarginProperty);

4. How WPF Uses Dependency Properties

Dependency properties are required for a range of WPF features. However, all of these features work through two key behaviors that every dependency property supports—change notification and dynamic value resolution.

Contrary to what you might expect, dependency properties do not automatically fireevents to let you know when a property value changes. Instead, they trigger a protected method named OnPropertyChangedCallback(). This method passes the information along to two WPF services (data binding and triggers). It also calls the PropertyChangedCallback, if one is defined.

NOTE

If you're dealing with a control that you've created, you can use the property callback mechanism to react to property changes and even raise an event. Many common controls use this technique for properties that correspond to user-supplied information. For example, the TextBox provides a TextChanged event, and the ScrollBar provides a ValueChanged event. A control can implement functionality like this using the PropertyChangedCallback, but this functionality isn't exposed from dependency properties in a general way for performance reasons.

The second feature that's key to the way dependency properties work is dynamic value resolution. This means when you retrieve the value from a dependency property, WPF takes several factors into consideration.

This behavior gives dependency properties their name—in essence, a dependency property depends on multiple property providers, each with its own level of precedence. When you retrieve a value from a property value, the WPF property system goes through a series of steps to arrive at the final value. First, it determines the base value for the property by considering the following factors, arranged from lowest to highest precedence:

  1. The default value (as set by the FrameworkPropertyMetadata object)

  2. The inherited value (if the FrameworkPropertyMetadata.Inherits flag is set and a value has been applied to an element somewhere up the containment hierarchy)

  3. The value from a theme style

  4. The value from a project style

  5. The local value (in other words, a value you've set directly on this object using code or XAML)

As this list shows, you override the entire hierarchy by applying a value directly. If you don't, the value is determined by the next applicable item up on the list.

NOTE

One of the advantages of this system is that it's very economical. If the value of a property has not been set locally, WPF will retrieve its value from a style, another element, or the default. In this case, no memory is required to store the value. You can quickly see the savings if you add a few buttons to a form. Each button has dozens of properties, which, if they are set through one of these mechanisms, use no memory at all.

WPF follows the previous list to determine the base value of a dependency property. However, the base value is not necessarily the final value that you'll retrieve from a property. That's because WPF considers several other providers that can change a property's value.

Here's the four-step process WPF follows to determine a property value:

  1. Determine the base value (as described previously).

  2. If the property is set using an expression, evaluate that expression. Currently, WPF supports two types of expression: data binding and resources .

  3. If this property is the target of animation, apply that animation.

  4. Run the CoerceValueCallback to "correct" the value.

Essentially, dependency properties are hardwired into a small set of WPF services. If it weren't for this infrastructure, these features would add unnecessary complexity and significant overhead.

In future versions of WPF, the dependency property pipeline could be extended to include additional services. When you design custom elements , you'll probably use dependency properties for most (if not all) of their public properties.


5. Shared Dependency Properties

Some classes share the same dependency property, even though they have separate class hierarchies. For example, both TextBlock.FontFamily and Control.FontFamily point to the same static dependency property, which is actually defined in the TextElement class and TextElement.FontFamilyProperty. The static constructor of TextElement registers the property, but the static constructors of TextBlock and Control simply reuse it by calling the DependencyProperty.AddOwner() method:

TextBlock.FontFamilyProperty =
  TextElement.FontFamilyProperty.AddOwner(typeof(TextBlock));

You can use the same technique when you create your own custom classes (assuming the property is not already provided in the class you're inheriting from, in which case you get it for free). You can also use an overload of the AddOwner() method that allows you to supply a validation callback and a new FrameworkPropertyMetadata that will apply only to this new use of the dependency property.

Reusing dependency properties can lead to some strange side effects in WPF, most notably with styles. For example, if you use a style to set the TextBlock.FontFamily property automatically, your style will also affect the Control.FontFamily property because behind the scenes both classes use the same dependency property. 

6. Attached Dependency Properties

 An attached property is a dependency property, and it's managed by the WPF property system. The difference is that an attached property applies to a class other than the one where it's defined.

The Grid class defines the attached properties Row and Column, which you set on the contained elements to indicate where they should be positioned. Similarly, the DockPanel defines the attached property Dock, and the Canvas defines the attached properties Left, Right, Top, and Bottom.

To define an attached property, you use the RegisterAttached() method instead of Register(). Here's an example that registers the Grid.Row property:

FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(
  0, new PropertyChangedCallback(Grid.OnCellAttachedPropertyChanged));

Grid.RowProperty = DependencyProperty.RegisterAttached("Row", typeof(int),
  typeof(Grid), metadata, new ValidateValueCallback(Grid.IsIntValueNotNegative));

					  

As with an ordinary dependency property, you can supply a FrameworkPropertyMetadata object and a ValidateValueCallback.

When creating an attached property, you don't define the .NET property wrapper. That's because attached properties can be set on any dependency object. For example, the Grid.Row property may be set on a Grid object (if you have one Grid nested inside another) or on some other element. In fact, the Grid.Row property can be set on an element even if that element isn't in a Grid—and even if there isn't a single Grid object in your element tree.

Instead of using a .NET property wrapper, attached properties require a pair of static methods that can be called to set and get the property value. These methods use the familiar SetValue() and GetValue() methods (inherited from the DependencyObject class). The static methods should be named SetPropertyName() and GetPropertyName().

Here are the static methods that implement the Grid.Row attached property:

public static int GetRow(UIElement element)
{
    if (element == null)
    {
        throw new ArgumentNullException(...);
    }
    return (int)element.GetValue(Grid.RowProperty);
}

public static void SetRow(UIElement element, int value)
{
    if (element == null)
    {
        throw new ArgumentNullException(...);
    }
    element.SetValue(Grid.RowProperty, value);
}

Here's an example that positions an element in the first row of a Grid using code:

Grid.SetRow(txtElement, 0);

Alternatively, you can call the SetValue() or GetValue() method directly and bypass the static methods:

txtElement.SetValue(Grid.RowProperty, 0);

The SetValue() method also provides one brain-twisting oddity. Although XAML doesn't allow it, you can use an overloaded version of the SetValue() method in code to attach a value for any dependency property, even if that property isn't defined as an attached property. For example, the following code is perfectly legitimate:

ComboBox comboBox = new ComboBox();
...
comboBox.SetValue(PasswordBox.PasswordCharProperty, "*");

Here, a value for the PasswordBox.PasswordChar property is set for a ComboBox object, even though PasswordBox.PasswordCharProperty is registered as an ordinary dependency property, not an attached property. This action won't change the way the ComboBox works—after all, the code inside the ComboBox won't look for the value of a property that it doesn't know exists—but you could act upon the PasswordChar value in your own code.

Although rarely used, this quirk provides some more insight into the way the WPF property systemworks, and it demonstrates its remarkable extensibility. It also shows that even though attached properties are registered with a different method than normal dependency properties, in the eyes of WPF there's no real distinction. The only difference is what the XAML parser allows. Unless you register your property as an attached property, you won't be able to set it in on other elements in your markup.

Other  
 
Video tutorials
- How To Install Windows 8 On VMware Workstation 9

- How To Install Windows 8

- How To Install Windows Server 2012

- How To Disable Windows 8 Metro UI

- How To Change Account Picture In Windows 8

- How To Unlock Administrator Account in Windows 8

- How To Restart, Log Off And Shutdown Windows 8

- How To Login To Skype Using A Microsoft Account

- How To Enable Aero Glass Effect In Windows 8

- How To Disable Windows Update in Windows 8

- How To Disable Windows 8 Metro UI

- How To Add Widgets To Windows 8 Lock Screen
programming4us programming4us
Top 10
Free Mobile And Desktop Apps For Accessing Restricted Websites
MASERATI QUATTROPORTE; DIESEL : Lure of Italian limos
TOYOTA CAMRY 2; 2.5 : Camry now more comely
KIA SORENTO 2.2CRDi : Fuel-sipping slugger
How To Setup, Password Protect & Encrypt Wireless Internet Connection
Emulate And Run iPad Apps On Windows, Mac OS X & Linux With iPadian
Backup & Restore Game Progress From Any Game With SaveGameProgress
Generate A Facebook Timeline Cover Using A Free App
New App for Women ‘Remix’ Offers Fashion Advice & Style Tips
SG50 Ferrari F12berlinetta : Prancing Horse for Lion City's 50th
Popular Tags
Video Tutorail Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Exchange Server Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe Photoshop CorelDRAW X5 CorelDraw 10 windows Phone 7 windows Phone 8 Iphone