Custom Attached Properties
Microsoft Silverlight will reach end of support after October 2021. Learn more.
This topic describes the method accessor pattern that defines an attached property for XAML, as well as the RegisterAttached(String, Type, Type, PropertyMetadata) method that enables a custom attached property to use the Silverlight dependency property store.
When to Create an Attached Property
You might create an attached property when there is a reason to have a property-setting mechanism available for classes other than the defining class. The most common scenario for this is layout. Examples of existing layout properties are Canvas.ZIndex and Canvas.Top. The scenario enabled here is that elements that exist as child elements to layout-controlling elements are able to express layout requirements to their layout parent elements individually, each setting a property value that the parent defines as an attached property.
Another scenario for using an attached property is when your class represents a service, and you want classes to be able to integrate the service more transparently. An example of this scenario in the Silverlight API is the Validation.Errors data validation technique.
How to Create an Attached Property
If your class is defining the attached property strictly for use on other types, then the class does not need to derive from DependencyObject. But you do need to have the target parameter for accessors use DependencyObject if you follow the typical model of having your attached property also be a dependency property, so that you can use the Silverlight property store.
Define your attached property as a dependency property by declaring a public static readonly field of type DependencyProperty. You define this field by using the return value of the RegisterAttached method. The field name must match the attached property name, appended with the string Property, to follow the established convention of naming the identifying fields in relation to the properties that they represent. The attached property provider must also provide static GetPropertyName and SetPropertyName methods as accessors for the attached property. Failing to do this will result in the XAML processor being unable to use your attached property in certain situations, such as setting the property with a style setter. Also, design and coding tools often rely on the *Property naming conventions when they encounter a custom dependency property in a referenced assembly.
Get Accessor
The signature for the GetPropertyName accessor must be the following:
public static valueType GetPropertyName(DependencyObject target)
(equivalent Visual Basic: Public Shared FunctionGetPropertyName(ByVal target As DependencyObject) As valueType) )
The target object can be specified as a more specific type in your implementation, but must derive from DependencyObject.
The valueType return value can be specified as a more specific type in your implementation. The basic Object type is acceptable, but often you will want your attached property to be more type-safe than that, and enforcing the type in the getter and setter signatures is a good technique for enforcing type safety.
Set Accessor
The signature for the SetPropertyName accessor must be the following:
public static void SetPropertyName(DependencyObject target, valueType value)
(equivalent Visual Basic: Public Shared Sub SetPropertyName(ByVal target As DependencyObject, ByVal value As valueType) )
The target object can be specified as a more specific type in your implementation, but must derive from DependencyObject.
The value object and its valueType can be specified as a more specific type in your implementation. Remember that the value for this method is the input coming from the XAML processor when it encounters your attached property in markup. There must be type conversion or existing markup extension support for the type you use, such that the appropriate type can be created from the attribute value (which is ultimately just a string). The basic Object type is acceptable, but often you want further type safety, as discussed in the note under the "Get Accessor" section.
The following example shows the dependency property registration (using the RegisterAttached method), as well as the GetPropertyName and SetPropertyName accessors, for a custom attached property. In the example, the attached property name is IsBubbleSource. Therefore, the accessors must be named GetIsBubbleSource and SetIsBubbleSource.
Public Shared ReadOnly IsBubbleSourceProperty As DependencyProperty = DependencyProperty.RegisterAttached("IsBubbleSource", GetType(Boolean), GetType(AquariumObject), New PropertyMetadata(False))
Public Shared Sub SetIsBubbleSource(ByVal element As UIElement, ByVal value As Boolean)
element.SetValue(IsBubbleSourceProperty, value)
End Sub
Public Shared Function GetIsBubbleSource(ByVal element As UIElement) As Boolean
Return CBool(element.GetValue(IsBubbleSourceProperty))
End Function
public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached(
"IsBubbleSource",
typeof(Boolean),
typeof(AquariumObject),
new PropertyMetadata(false)
);
public static void SetIsBubbleSource(UIElement element, Boolean value)
{
element.SetValue(IsBubbleSourceProperty, value);
}
public static Boolean GetIsBubbleSource(UIElement element)
{
return (Boolean)element.GetValue(IsBubbleSourceProperty);
}
Using the Attached Property in XAML
Once you have defined your attached property and included its support members as part of a custom type, you must then make the assembly available for XAML usage. This means that you must map an XML namespace that will reference the CLR namespace containing the relevant class, as well as the name of the assembly containing that class. An XML namespace mapping for XAML is typically placed in the root element of a XAML page.
For example, if you have a class named Aquarium in the namespace AquariumObjects that contained the IsBubbleSource definitions shown earlier, and you compiled this class into a library named AquariumLibrary, the mapping might look like this:
<UserControl
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:aqua="clr-namespace:AquariumObjects;assembly=AquariumLibrary"
...
>
Then, using the mapping, you can set your IsBubbleSource attached property on any element that matches your target definition, including an existing type that Silverlight defines:
<Image aqua:Aquarium.IsBubbleSource="true" .../>
If you are setting the property on an element that is also within the same mapped XML namespace, you still must include the prefix on the attached property name. This is because the prefix qualifies the owner type, and thus the attached property's attribute cannot be automatically assumed to be within the same XML namespace as the element where the attribute is included per normal rules of XML namespace resolution. For example, if you are setting IsBubbleSource on a custom type of Fish (definition not shown), the XAML would be as follows:
<aqua:Fish aqua:Aquarium.IsBubbleSource="true" .../>
Value Type of a Custom Attached Property
The type that is used as the value type of a custom attached property affects the usage, the definition, or both the usage and definition. The attached property value type is declared in several places: in the signatures of both the Get and Set accessor method, and also as the propertyType parameter of the RegisterAttached call. The most common value type for attached properties (custom or otherwise) is a simple string. This is because attached properties are generally intended for XAML attribute usage, and using a string as the value type keeps the properties lightweight. Other primitives that have native conversion to string, such as Int or Double, or an enumeration value, are also common as attached property value types.
You can use other value types without native string conversion as the attached property value. However, this entails making a choice about either the usage or implementation.
You can leave the attached property as is, but the attached property can only support a usage where the attached property is a property element, and the value is declared as an object element. In this case, the property type does have to support XAML usage as an object element, as documented in the topic XAML and Custom Classes. For existing Silverlight classes, check the XAML syntax in reference to make sure that type supports XAML object element usage.
You can leave the attached property as is, but only use it in an attribute usage through a XAML reference technique such as a Binding or StaticResource that can be expressed as a string.
You can attribute a type-level TypeConverter on the type that serves as the value type. This enables string conversion of all values of the type. For more information, see TypeConverters and XAML.
You can attribute a property-level TypeConverter on the Get accessor method. This enables string conversion of the attached property. Applying TypeConverterAttribute to the Get accessor method rather than the Set accessor method may seem nonintuitive, but that is where XAML processors expect to find the type conversion information (if any) for an attached property. For more information, see TypeConverters and XAML.