WPF Simplified Part 6: Attached Properties

Attached properties is a new construct in WPF that allows you to attach a property defined for an object to another  object when the second object has no definition or declaration of the property.

For example, consider the simple XAML,

    <Grid>
        <!-- A simple button -->
        <Button Height="100" Width="100" Content="Some Text" />
    </Grid>

 

Now say, we’d like to set the FontSize on the Button, this is simple,

    <Grid>
        <!-- Setting FontSize on the button -->
        <Button Height="100" Width="100" FontSize="20" Content="Some Text" />
    </Grid>
 

This was easy because Button has a dependency property called FontSize. Now say we have many buttons inside the Grid and we’d like to set all their FontSizes. This could be done with Styles, but for the purpose of this explanation we’ll do it via attached properties. The following gives a compile time error, because Grid does not have any Font related properties.

    <!-- ERROR: The property 'FontSize' was not found in type 'Grid' -->
    <Grid FontSize="20">
        <Button Height="100" Width="100" Content="Some Text" />
    </Grid>

 

But this works,

    <Grid TextBlock.FontSize="20">    
        <Button Height="100" Width="100" Content="Some Text" />
    </Grid>

 

How? FontSize is defined in WPF as an attached (read attachable) property of the TextBlock class. This attachable property defined in TextBlock (attached property provider) is attached to a completely different class (Grid) here. The end result is that Grid now effectively has a new property (FontSize) that the Button inherits. The code behind is a little bit more explanatory,

Grid grid = new Grid();
TextBlock.SetFontSize(grid, 20);
Button button = new Button() { Height = 100, Width = 100, Content = "Some Text" };
grid.Children.Add(button);

 

As you can see, the attached property is just a method call that associates an object with an otherwise-unrelated property. But why get the attached property FontSize from TextBlock? Why not from Control or Button or TextElement? We could have used TextElement in place of TextBlock in the examples above, the reason is that TextBlock and TextElement register FontSize as an attached property, while Control and Button do not.

Attached properties can be used as an extensibility mechanism,

// Define a class that derives from DependencyObject
public class MyClass : DependencyObject { }
 
// The attach the 'Tag' property to it
MyClass myClass = new MyClass();
myClass.SetValue(FrameworkElement.TagProperty, "My Data");

// Retrieve the 'Tag' property from it
string tagValue = (string)myClass.GetValue(FrameworkElement.TagProperty);
 

Declaring a custom attached property is similar to declaring a dependency property,

public static void SetMyAttachedProperty(DependencyObject target, int value)
{
    // Calls DependencyObject.SetValue on the passed in DependencyObject
    target.SetValue(MyAttachedProperty, value);
}

public static int GetMyAttachedProperty(DependencyObject target)
{
    // Calls the DependencyObject.GetValue
    return (int)target.GetValue(MyAttachedProperty);
}

public static readonly DependencyProperty MyAttachedProperty = DependencyProperty.RegisterAttached(
        "MyAttached",           // Name of the attached property
        typeof(int),            // Type of the attached property
        typeof(MyControl),      // Type of the owner
        new PropertyMetadata(0,
            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 */ }

 

Unlike dependency properties, attached properties are not wrapped in CLR properties. And in the case of attached properties the property values are stored on the element the consumes the property instead of the element that declares the property (which is where the value is stored for dependency properties).

Although setting attached properties via XAML require the presence of the static Set[AttachedProperty] method, in code we can call DependencyObject.SetValue directly. This means that we can use any dependency property as an attached property in procedural code, as we did above for the Tag dependency property.

Advertisements

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

3 Responses to WPF Simplified Part 6: Attached Properties

  1. Pingback: WPF Simplified Series « I.Net

  2. Srripriya says:

    Awesome.. explanation.. in simple words.. with simple examples

  3. Stefan Lober says:

    Interesting example, although – to me – attached/attachable properties look a bit like a work-around mechanism. Why not use a Style as a resource to set the FontSize of the TextBox? I think that’s the preferred way to determine the appearance of controls in WPF.
    Also, the syntax of the static Get[AttachedProperty] and Set[AttachedProperty] methods deviates unnecessarily(?) from the standard get/set of C# properties.

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: