Using an attached property
Attached properties are curious beings. There is no direct analogue to anything else in the .NET framework. The closest may be extension methods, introduced in C# 3.0. Extension methods are a way of extending a type without inheriting from it (even if that type is sealed). Attached properties are dependency properties that are defined by some type, but can be used by (almost) any other typed object. That is, they can extend a type's properties without code derivation. In this recipe, we'll see how to use an existing attached property, and in the next one we'll learn how to create a new attached property.
Getting ready
Make sure Visual Studio is up and running.
How to do it...
We'll create an application that creates a rectangle and places it inside a canvas at exact coordinates using attached properties:
- Create a new WPF Application named
CH01.SimpleAttached
. - Open the
MainWindow.xaml
file and create the following basic layout:<Grid> <Canvas> <RepeatButton Content="Move" /> </Canvas> </Grid>
- This creates a grid which hosts a
Canvas
and that canvas hosts aRepeatButton
. Let's add aRectangle
element to theCanvas
and place it in some position:<Canvas> <RepeatButton Grid.Row="1" Content="Move" /> <Rectangle x:Name="_rect" Width="50" Height="50" Fill="Red" Stroke="Black" StrokeThickness="5" Canvas.Left="30" Canvas.Top="40" /> </Canvas>
- Run the application. You should see something like this:
- The
Canvas.Left
andCanvas.Top
are attached properties. They are defined by theCanvas
type, but they can be applied to any element (technically, anything that derives fromDependencyObject
). In this case, these two properties are applied to theRectangle
, essentially "extending" its property set with two new properties. The syntaxDefiningClassName.PropertyName
is the way to access an attached property in XAML. - Now let's try changing these properties in code. When the repeat button is clicked, let's move the rectangle a little bit to the right. First, let's name the
Rectangle
, so we can easily refer to it in code:<Rectangle x:Name="_rect" Width="50" Height="50" Fill="Red" Stroke="Black" StrokeThickness="5" Canvas.Left="30" Canvas.Top="40" />
- Add a
Click
event handler to theRepeatButton
. In the handler, add the following code:Canvas.SetLeft(_rect, Canvas.GetLeft(_rect) + 5);
- An attached property is accessed in code using a set of static methods on the class declaring the property (in this case, the
Canvas
). The first argument to these methods is the intended target of the property (in this case theRectangle
). Run the application. Click on the button (and hold); you should see the rectangle moving along to the right, 5 units at a time.
How it works...
An attached property is first and foremost a dependency property, meaning it supports all the capabilities of dependency properties. However, as an attached property is "attached" to an object that did not define it, a simple property like syntax is not possible – as C# does not support the concept of attached properties natively. Instead, the declaring class provides two static methods, named DeclaringType.SetPropertyName
and DeclaringType.GetPropertyName
, that provide a way to set or get the property value for some object passed in as the first argument (as demonstrated in the last code snippet).
In XAML, things are simpler, as the XAML parser is aware of attached properties, and converts the simpler DeclaringType.PropertyName
attribute to the aforementioned Set method.
There's more...
The actual implementation of the Set/Get static methods mentioned above is to call the regular DependencyObject.SetValue
/GetValue
as for a regular dependency property. This means that the code to move the rectangle could have been written as follows:
_rect.SetValue(Canvas.LeftProperty, (double)_rect.GetValue(Canvas.LeftProperty) + 5);
One may wonder why to go to all this trouble for the Left
and Top
properties. Would it not be simpler to define the Left
and Top
properties on the (for example) UIElement
class and be done with it? These properties could have been normal dependency properties and enjoy the simpler syntax they carry.
The reason is, that a Left
or Top
property may not always make sense. In fact, it only makes sense when the element is placed within a Canvas
. What if the rectangle is inside a Grid
? Or a StackPanel
? The Left/Top
properties wouldn't make sense. This leads to the conclusion that attached properties are a kind of contextual property – they are relevant under particular circumstances, so they can be "attached" if and when actually needed.
The previous example may lead to a wrong conclusion. It seems Canvas.Left
and the like are only relevant when the element is inside a Canvas
. Similarly, the Grid.Row
and Grid.Column
attached properties only make sense for elements placed inside a Grid
. Is this somehow necessary from an attached property point of view?
Not at all. This is just coincidence. The above properties in fact make sense only for elements placed inside their respective declaring type, but that does not have to be the case. For example, suppose we have a button with a tool tip defined:
<Button Content="Copy" ToolTip="Copy the Selected Items" />
If the button is disabled (IsEnabled
set to true), the tool tip does not appear at runtime. To make it appear even if the control is disabled, we must set the ToolTipService.ShowOnDisabled
attached property to true
:
<Button Content="Copy" ToolTip="Copy the Selected Items"
ToolTipService.ShowOnDisabled="True"/>
We set the property on the button, but it's defined in the ToolTipService
class. This class is not an element (unlike the Canvas
for example). In fact, it's a static class (instances of it cannot be created). So, there is no relationship between the button and the ToolTipService
class, or between the ToolTip
and ToolTipService
classes, for that matter. The way this connection is established (so it can have some effect) will be revealed in the next recipe in this chapter.
See also
To create your own attached properties, refer to the next recipe, Creating an attached property.