wpf Attached dependency properties


Example

When to use

An attached property is a dependency property that can be applied to any DependencyObject to enhance the behavior of various controls or services that are aware of the property's existence.

Some use cases for attached properties include:

  1. Having a parent element iterate through its children and act upon the children in a certain way. For example, the Grid control uses the Grid.Row, Grid.Column, Grid.RowSpan, and Grid.ColumnSpan attached properties to arrange elements into rows and columns.
  2. Adding visuals to existing controls with custom templates, e.g adding watermarks to empty text boxes app-wide without having to subclass TextBox.
  3. Providing a generic service or feature to some or all existing controls, e.g. ToolTipService or FocusManager. These are commonly referred to as attached behaviors.
  4. When inheritance down the visual tree is required, e.g. similar to the behavior of DataContext.

This further demonstrates what is happening in the Grid use case:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <Label Grid.Column="0" Content="Your Name:" />
    <TextBox Grid.Column="1" Text="{Binding FirstName}" />
</Grid>

Grid.Column is not a property that exists on either Label or TextBox. Rather, the Grid control looks through its child elements and arranges them according to the values of the attached properties.

How to define

We'll continue to use Grid for this example. The definition of Grid.Column is shown below, but the DependencyPropertyChangedEventHandler is excluded for brevity.

public static readonly DependencyProperty RowProperty =
    DependencyProperty.RegisterAttached("Row", typeof(int), typeof(Grid),
        new FrameworkPropertyMetadata(0, ...));

public static void SetRow(UIElement element, int value)
{
    if (element == null)
        throw new ArgumentNullException("element");

    element.SetValue(RowProperty, value);
}

public static int GetRow(UIElement element)
{
    if (element == null)
        throw new ArgumentNullException("element");

    return ((int)element.GetValue(RowProperty));
}

Because the attached properties can be attached to a wide variety of items, they cannot be implemented as CLR properties. A pair of static methods is introduced instead.

Hence, in contrast to standard dependency properties, attached properties can also be defined in classes that are not derived from DependencyObject.

The same naming conventions that apply to regular dependency properties also apply here: the dependency property RowProperty has the corresponding methods GetRow and SetRow.

Caveats

As documented on MSDN:

Although property value inheritance might appear to work for nonattached dependency properties, the inheritance behavior for a nonattached property through certain element boundaries in the run-time tree is undefined. Always use RegisterAttached to register properties where you specify Inherits in the metadata.