WPF Simplified Part 3: Dependency Properties

If you’ve been coding with C# for some time, you’re no doubt familiar with the CLR’s property construct. A typical CLR property looks like this,

// Private field
private int statusId;

// Public property that wraps the private field
public int StatusId {
    get {
        // Do stuff... 
        return this.statusId;
    }
    set {
        // Do stuff...
        this.statusId = value;
    }
}
 

Note how, for a regular CLR property, the property value is read directly from a private class member. WPF introduces a new construct called a dependency property, which can get it’s value from different sources and the actual value resolution occurs dynamically at runtime based on some precedence rules.

For example, a TextBox‘s Text property, which is a dependency property, can get its value from a style, a trigger, an animation, or locally (among others). Consider the following XAML,

<Grid.Resources>
    <Style x:Key="TextBoxBaseStyle" TargetType="TextBox">
        <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Text" Value="Trigger setter text" />
            </Trigger>
        </Style.Triggers>
        <Setter Property="Text" Value="Property setter text" />
    </Style>
</Grid.Resources>

<!-- The local value of the Text property is set here -->
<TextBox x:Name="MyTextBox" Height="30" Width="100" 
    Text="Local value text" Style="{StaticResource TextBoxBaseStyle}" />

 

If you look closely the TextBox‘s Text (dependency) property is set in three places. So which one applies? If you run this, the TextBox starts out with the text "Local value text", even though we’ve set a Style that says Text should be "Property setter text". Why? This is because when the WPF property system tried to resolve the Text value at runtime the local value has precedence over a Style setter.

The precedence rules are (higher to lower),

    1. Active animations

    2. Local value

    3. Style triggers

    4. Style setters

    5. Theme style

    6. Inheritance

    7. Default value (from dependency property metadata)

As you can see, a local value has higher precedence than a Style trigger or a Style setter. The Text dependency property depends on these different property value providers, each of which is checked in turn at runtime to get the property value. This also explains how triggers are able to revert back the value when they are done (see this post). When the trigger stops changing the value, the WPF property system looks down the list for the next value provider that can supply a value, the trigger changes are thus temporary.

Now say we want to remove the dependency property’s local value so that the Text value can come from elsewhere (like a Style). The local value can be erased by,

    this.MyTextBox.ClearValue(TextBox.TextProperty);
 
    // Note that these, will not remove the local value because
    // we're still setting a local value (to empty or null).
    //this.MyTextBox.Text = string.Empty;
    //this.MyTextBox.Text = null;

 

Once the local value is erased, you’ll see the Text changed to "Property setter text", and mouse over text to "Trigger setter text". Thus, the Text (dependency) property can get it’s value from different sources (local, style, trigger), and the runtime value depends a set of precedence rules.

// A dependency property is usually wrapped in a regular CLR property. This wrapper is not necessary,
// we can directly call GetValue/SetValue, but its useful if the dependency property needs to be set via XAML.
// Note that at runtime WPF calls the underlying GetValue/SetValue methods directly, bypassing the wrappers.
public int MyValue
{
    get { return (int)GetValue(MyValueProperty); }
    set { SetValue(MyValueProperty, value); }
}

// A dependency property definition
public static readonly DependencyProperty MyValueProperty =
            DependencyProperty.Register(
                "MyValue",              // Name of the dependency property
                typeof(int),            // Type of the dependency property
                typeof(MyClass),        // Type of the owner
                new PropertyMetadata(
                    0,                                            // The default value of the dependency property
                    new PropertyChangedCallback(OnValueChanged),  // Callback when the property changes
                    new CoerceValueCallback(CoerceValue)),        // Callback when value coercion is required
                new ValidateValueCallback(IsValidValue));     // Callback for custom validation

// The validation callback
private static bool IsValidValue(object value) { /* Validate the set value */ }
 
// The coercion callback
private static object CoerceValue(DependencyObject d, object value) { /* Adjust the value without throwing an exception */ }
 
// The value changed callback 
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ /* Called every time the dependency property value changes */ }

 

Dependency properties can also be read-only. A dependency property can only be created on a class which derives from the DependencyObject class, but since this class is very high up the WPF class heirarchy, this is not much of a concern. Some of the advantages of dependency properties are,

    1. XAML friendly: Dependency properties can be set in XAML (via a CLR property wrapper)

    2. Change notifications and validation: Get change notifications and validate property values by registering callbacks.

    3. Value inheritance: Dependency property values can be inherited from up the logical tree.

    4. Reduced memory: Since dependency properties are static, they save per-instance memory compared to regular CLR properties (which are based on per instance fields).

You can also listen in to dependency property changes,

// We want to hook into TextBox’s Text property... 
DependencyPropertyDescriptor textDescr = 
                  DependencyPropertyDescriptor.FromProperty(TextBox.TextProperty, typeof(TextBox));
 
if (textDescr != null)
{
    textDescr.AddValueChanged(myTextBox, delegate
        {
            // Add your property changed logic here...
        });
} 


Digg This
Advertisements

About soumya chattopadhyay
I live and work in Seattle, WA. I work with Microsoft technologies, and I'm especially interested in C#.

4 Responses to WPF Simplified Part 3: Dependency Properties

  1. Pingback: WPF Simplified Series « I.Net

  2. Pingback: WPF Simplified Part 14: INotifyPropertyChanged and ObservableCollection « I.Net

  3. Narendra says:

    whenever i want to refresh my wpf concept i read this blog..love it

  4. Mani says:

    Great blog Soumya. Thanks a lot. It is crisp, clean but covers all that’s required. Well done.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: