In .Net 1.1/2.0, a typical CLR property looks like this,
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 its value from different sources and the actual value resolution occurs dynamically at run-time 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,
If we observe closely in above sample, the TextBox‘s Text (dependency) property is set in three places. So which one applies? If we 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 run-time, the local value has precedence over a Style setter.
The precedence rules are (higher to lower),
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,
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 its value from different sources (local, style, trigger), and the run-time value depends a set of precedence rules. See the example, how to create dependency property?
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 hierarchy, this is not much of a concern.
Some of the advantages of dependency properties are,
// Private field
private int _postId;
// Public property that wraps the private field
public int PostId{
get { return _postId; }
set { _postId= value; }
}
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 its value from different sources and the actual value resolution occurs dynamically at run-time 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 we observe closely in above sample, the TextBox‘s Text (dependency) property is set in three places. So which one applies? If we 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 run-time, the local value has precedence over a Style setter.
The precedence rules are (higher to lower),
- Active animations
- Local value
- Style triggers
- Style setters
- Theme style
- Inheritance
- Default value (from dependency property metadata)
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 its value from different sources (local, style, trigger), and the run-time value depends a set of precedence rules. See the example, how to create dependency property?
// A dependency property is usually wrapped in a regular CLR property. This wrapper is not necessary,
// we can directly call GetValue/SetValue, but it’s 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 hierarchy, this is not much of a concern.
Some of the advantages of dependency properties are,
- XAML friendly: Dependency properties can be set in XAML (via a CLR property wrapper)
- Change notifications and validation: Get change notifications and validate property values by registering callbacks.
- Value inheritance: Dependency property values can be inherited from up the logical tree.
- Reduced memory: Since dependency properties are static, they save per-instance memory compared to regular CLR properties (which are based on per instance fields).
// We have 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...
});
}