Dependency Properties in WPF
Dependency Property :
Dependency properties are just normal .NET properties hooked into some extra WPF infrastructure.
A Dependency Property in WPF enable styling, automatic data binding, animation.
The value of a dependency property might come from data binding, animation, template resources specified in the XAML, styles, or local values.
A dependency property depends on multiple providers for determining its value at any point in time.
It enable rich functionality directly from declarative markup.
First define an object that represents your property and an instance of the DependencyProperty class.
The information about property needs to be available all the time,
and possibly even shared among classes, So DependencyProperty object must be static field in the associated class.
For example, the FrameworkElement class defines a Margin Dependency property that all elements share.
That means it’s defined in the FrameworkElement class like this:
public class FrameworkElement: UIElement, ...
{
public static readonly DependencyProperty MarginProperty;
...
}
Registering a Dependency Property
WPF ensures that DependencyProperty objects can’t be instantiated directly, because the DependencyProperty class has no public constructor.
DependencyObject instance can be created only using the static DependencyProperty.Register() method.
WPF 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.
Parameters of DependencyProperty.Register
The property name (Margin in this example)
The data type used by the property (the Thickness structure )
The type that owns this property (the FrameworkElement class)
Optionally, a FrameworkPropertyMetadata object with additional property settings
Optionally, a callback that performs validation for the property.
public static readonly DependencyProperty WidthProperty;
public double Width
{get { return (double) this.GetValue(WidthProperty); }
set {base.SetValue(WidthProperty, value);} }
Syntax to Register DependencyProperty:
public static DependencyProperty Register(string name,
Type propertyType, Type ownerType,
PropertyMetadata typeMetadata)
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));
...
}
Adding a Property Wrapper
The final step to creating a dependency property is to wrap it in a traditional .NET property.
public Thickness Margin
{
set { SetValue(MarginProperty, value); }
get { return (Thickness)GetValue(MarginProperty); }
}
Now you can set just like any other .NET property using the property wrapper:
myElement.Margin = new Thickness(5);
myElement.ClearValue(FrameworkElement.MarginProperty);
The Validation Callback
MarginProperty = DependencyProperty.Register("Margin",
typeof(Thickness), typeof(FrameworkElement), metadata,
new ValidateValueCallback(FrameworkElement.IsMarginValid));
Use this callback to enforce the validation that you’d normally add in the set portion of a property procedure.
The callback you supply must point to a method that accepts an object parameter and returns a Boolean value.
Return true to accept the object as valid and false to reject it.
private static bool IsMarginValid(object value)
{
Thickness thickness1 = (Thickness) value;
return thickness1.IsValid(true, false, true, false);
}
Dependency properties do not automatically fire events 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.
Dependency property functionality supports :
. Change notification
. Property value inheritance
. Support for multiple providers
Change notification
Whenever the value of a dependency property changes, WPF can automatically trigger a number of actions depending on the property’s metadata.
These actions can be re-rendering the appropriate elements, updating the current layout, refreshing data bindings, and much more.
one interesting features enabled by this built-in change notification is property triggers, which enable you to perform your own custom actions when a property value changes without writing any procedural code.
<Button MouseEnter=”Button_MouseEnter” MouseLeave=”Button_MouseLeave”
MinWidth=”75” Margin=”10”>Help</Button>
This handlers could be implemented in a C# code-behind file as follows:
Change the foreground to blue when the mouse enters the button
void Button_MouseEnter(object sender, MouseEventArgs e)
{
Button b = sender as Button;
if (b != null) b.Foreground = Brushes.Blue;
}
With a property trigger,you can accomplish this same behavior purely in XAML.
The following concise Trigger object is all you need:
<Trigger Property="IsMouseOver" Value=”True”>
<Setter Property=”Foreground” Value=”Blue”/>
</Trigger>
Attaching style to all buttons with Triggers:
<Button MinWidth=”75” Margin=”10”>
<Button.Style>
<Style TargetType=”{x:Type Button}”>
<Style.Triggers>
<Trigger Property=”IsMouseOver” Value=”True”>
<Setter Property=”Foreground” Value=”Blue”/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
OK
</Button>
Property value Inheritance
Flowing of property values down the element tree.
Change the Property at window level like FontSize will flow to children in tree for all the controls which support Dependency property called FontSize.