I'm a bit mystified as to how Attached Properties actually convey their values to either parent or child elements. TextElement.FontFamily
causes child elements to inherit the value assigned to that property (a seemingly downstream operation, parent to child). Grid.Column
causes a parent item to display that child in a particular position (a seemingly upstream operation, child to parent). How do Attached Property values know to either flow up or down? Is my conception of this incorrect, or is there a piece missing that will put all of this into perspective?
<StackPanel TextElement.FontFamily="Wingdings">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="1" Content="My Button"/>
</Grid>
</StackPanel>
There are two concepts here: dependency properties and attached dependency properties. "Attached Properties" are dependency properties, and as such support dependency property value inheritance.
About basic dependency properties, a very rough statement would be that they basically inherit their values from parent elements in the wpf (logical/visual) tree. A dependency property (attached or not) inherits its value "downwards" if its metadata is set with the FrameworkPropertyMetadataOptions.Inherit flag, and in many cases this is so.
Attached properties are properties which can be set on any wpf object (basically, at least a DependencyObject) via the DependencyObject.SetValue method. The purpose for this mechanism is to "attach" to other objects information needed by parent objects, not the child objects themselves. For example, the Grid.Row is an attached property required by the Grid to place items within its render area.
Dependency properties are inherited "downwards" automatically by the wpf object system.
Attached properties are examined "upwards" explicitly, in the code of specific objects. In the case of Grid, upon determining where to place its items, it checks for the value of Grid.Row and Grid.Column attached properties on each contained item.
It is also often the technique to create custom attached properties which modify in some way the objects they are attached to (for example, the Drag'n'Drop functionality via attached properties).
As an additional note, a good example of an inheriting attached property is TextElement.FontFamily. Grid.Row and Grid.Column properties do not have the Inherits flag set.
TextElement.FontFamily, from Reflector:
FontFamilyProperty = DependencyProperty.RegisterAttached("FontFamily", typeof(FontFamily), typeof(TextElement), new FrameworkPropertyMetadata(SystemFonts.MessageFontFamily, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure), new ValidateValueCallback(TextElement.IsValidFontFamily));
Grid.Row, from Reflector:
RowProperty = DependencyProperty.RegisterAttached("Row", typeof(int), typeof(Grid), new FrameworkPropertyMetadata(0, new PropertyChangedCallback(Grid.OnCellAttachedPropertyChanged)), new ValidateValueCallback(Grid.IsIntValueNotNegative));
From MSDN:
Although attached properties are settable on any object, that does not automatically mean that setting the property will produce a tangible result, or that the value will ever be used by another object. Generally, attached properties are intended so that objects coming from a wide variety of possible class hierarchies or logical relationships can each report common information to the type that defines the attached property. The type that defines the attached property typically follows one of these models:
The type that defines the attached
property is designed so that it can
be the parent element of the elements
that will set values for the attached
property. The type then iterates its
child objects through internal logic
against some object tree structure,
obtains the values, and acts on those
values in some manner.
The type that defines the attached
property will be used as the child
element for a variety of possible
parent elements and content models.
The type that defines the attached
property represents a service. Other
types set values for the attached
property. Then, when the element that
set the property is evaluated in the
context of the service, the attached
property values are obtained through
internal logic of the service class.
An Example of a Parent-Defined Attached Property
The most typical scenario where WPF defines an attached property is when a parent element supports a child element collection, and also implements a behavior where the specifics of the behavior are reported individually for each child element.
DockPanel defines the DockPanel.Dock attached property, and DockPanel has class-level code as part of its rendering logic (specifically, MeasureOverride and ArrangeOverride). A DockPanel instance will always check to see whether any of its immediate child elements have set a value for DockPanel.Dock. If so, those values become input for the rendering logic applied to that particular child element. Nested DockPanel instances each treat their own immediate child element collections, but that behavior is implementation-specific to how DockPanel processes DockPanel.Dock values. It is theoretically possible to have attached properties that influence elements beyond the immediate parent. If the DockPanel.Dock attached property is set on an element that has no DockPanel parent element to act upon it, no error or exception is raised. This simply means that a global property value was set, but it has no current DockPanel parent that could consume the information.
In simple words this is how I understand it (please correct me if I'm wrong).
An object (A) implements a property that will attach to another object (B) (object B doesn't even know about the existence of this "attachable" property). Object B needs to inherit from DependencyObject.
Object A also implements a static method to check for it's "attachable" property in other objects, A.GetAttachedProperty(B).
If B has the attached property from A, A.GetAttachedProperty will read and return it's value. Otherwise A will try to read it, and return null since it's not there.