Dependency property metadata (WPF .NET)
The Windows Presentation Foundation (WPF) property system includes a dependency property metadata reporting system. The information available through the metadata reporting system exceeds what is available through reflection or general common language runtime (CLR) characteristics. When you register a dependency property, you have the option to create and assign metadata to it. If you derive from a class that defines a dependency property, you can override the metadata for the inherited dependency property. And, if you add your class as an owner of a dependency property, you can override the metadata of the inherited dependency property.
The Desktop Guide documentation for .NET 6 and .NET 5 (including .NET Core 3.1) is under construction.
The article assumes a basic knowledge of dependency properties, and that you've read Dependency properties overview. To follow the examples in this article, it helps if you're familiar with Extensible Application Markup Language (XAML) and know how to write WPF applications.
How metadata is used
You can query dependency property metadata to examine the characteristics of a dependency property. When the property system processes a dependency property, it accesses its metadata. The metadata object for a dependency property contains the following types of information:
The default value of the dependency property, which is set by the property system when no other value applies, such as a local, style, or inheritance value. For more information about value precedence during run-time assignment of dependency property values, see Dependency property value precedence.
References to coercion value callbacks and property change callbacks on the owner type. You can only obtain references to callbacks that have a
publicaccess modifier or are within your permitted access scope. For more information about dependency property callbacks, see Dependency property callbacks and validation.
WPF framework-level dependency property characteristics (if the dependency property is a WPF framework property). WPF processes, such as the framework layout engine and the property inheritance logic, query WPF framework-level metadata. For more information, see Framework property metadata.
The PropertyMetadata class stores most of the metadata used by the property system. Metadata instances can be created and assigned by:
Types that register dependency properties with the property system.
Types that inherit from a class that defines a dependency property.
Types that add themselves as an owner of a dependency property.
If a type registers a dependency property without specifying metadata, the property system assigns a
PropertyMetadata object with default values for that type to the dependency property.
More specific metadata classes, derived from
PropertyMetadata, exist for different architectural areas. For example, UIPropertyMetadata supports animation reporting, and FrameworkPropertyMetadata supports WPF framework properties. Dependency properties can also be registered with the
PropertyMetadata derived classes. Although
GetMetadata returns a
PropertyMetadata object, when applicable you can cast to a derived type to examine type-specific properties.
The property characteristics that are exposed by
FrameworkPropertyMetadata are sometimes referred to as flags. When you create a
FrameworkPropertyMetadata instance, you have the option to pass an instance of the enumeration type FrameworkPropertyMetadataOptions into the
FrameworkPropertyMetadataOptions lets you specify metadata flags in bitwise combination. The
FrameworkPropertyMetadataOptions to keep the length of its constructor signature reasonable. On dependency property registration, the metadata flags that you set on
FrameworkPropertyMetadataOptions are exposed within
Boolean properties rather than a bitwise combination of flags, to make querying metadata characteristics more intuitive.
Override or create new metadata?
When you inherit a dependency property, you have the option to change characteristics of the dependency property by overriding its metadata. However, you might not always be able to accomplish your dependency property scenario by overriding metadata, and sometimes it's necessary to define a custom dependency property in your class with new metadata. Custom dependency properties have the same capabilities as dependency properties defined by WPF types. For more information, see Custom dependency properties.
One characteristic of a dependency property that you can't override is its value type. If an inherited dependency property has the approximate behavior you need, but your scenario requires a different value type, consider implementing a custom dependency property. You might be able to link the property values through type conversion or other implementation in your derived class.
Scenarios for overriding metadata
Example scenarios for overriding existing dependency property metadata are:
Changing the default value, which is a common scenario.
Changing or adding property-change callbacks, which might be necessary if an inherited dependency property interacts with other dependency properties differently than its base implementation does. One of the characteristics of a programming model that supports both code and markup, is that property values might be set in any order. This factor can affect how you implement property-change callbacks. For more information, see Dependency property callbacks and validation.
Changing WPF framework property metadata options. Typically, metadata options are set during registration of a new dependency property, but you can respecify them in OverrideMetadata or AddOwner calls. For more information about overriding framework property metadata, see Specifying metadata. For how to set framework property metadata options when registering a dependency property, see Custom dependency properties.
Since validation callbacks aren't part of metadata, they can't be changed by overriding metadata. For more information, see Validation callbacks.
When implementing a new dependency property, you can set its metadata by using overloads of the Register method. If your class inherits a dependency property, you can override inherited metadata values using the OverrideMetadata method. For example, you might use
OverrideMetadata to set type-specific values. For more information and code samples, see Override metadata for a dependency property.
An example of a WPF dependency property, is Focusable. The FrameworkElement class registers
Focusable. The Control class derives from
FrameworkElement, inherits the
Focusable dependency property, and overrides the inherited property metadata. The override changes the default property value from
true, but preserves other inherited metadata values.
Since most existing dependency properties aren't virtual properties, their inherited implementation shadows the existing member. When you override a metadata characteristic, the new metadata value either replaces the original value or they're merged:
For a DefaultValue, the new value will replace the existing default value. If you don't specify a
DefaultValuein the override metadata, the value comes from the nearest ancestor that specified
For a PropertyChangedCallback, the default merge logic stores all
PropertyChangedCallbackvalues in a table, and all are invoked on a property change. The callback order is determined by class depth, where a callback registered by the base class in the hierarchy would run first.
For a CoerceValueCallback, the new value will replace the existing
CoerceValueCallbackvalue. If you don't specify a
CoerceValueCallbackin the override metadata, the value comes from the nearest ancestor that specified
The default merge logic is implemented by the Merge method. You can specify custom merge logic in a derived class that inherits a dependency property, by overriding
Merge in that class.
Add a class as an owner
To "inherit" a dependency property that's registered in a different class hierarchy, use the AddOwner method. This method is typically used when the adding class isn't derived from the type that registered the dependency property. In the
AddOwner call, the adding class can create and assign type-specific metadata for the inherited dependency property. To be a full participant in the property system, through code and markup, the adding class should implement these public members:
A dependency property identifier field. The value of the dependency property identifier is the return value of the
AddOwnercall. This field should be a
public static readonlyfield of type DependencyProperty.
A CLR wrapper that implements
setaccessors. By using a property wrapper, consumers of dependency properties can get or set dependency property values, just as they would any other CLR property. The
setaccessors interact with the underlying property system through DependencyObject.GetValue and DependencyObject.SetValue calls, passing in the dependency property identifier as a parameter. Implement the wrapper the same way you would when registering a custom dependency property. For more information, see Custom dependency properties
A class that calls
AddOwner has the same requirements for exposing the object model of the inherited dependency property as a class that defines a new custom dependency property. For more information, see Add an owner type for a dependency property.
Attached property metadata
In WPF, most UI-related attached properties on WPF types are implemented as dependency properties. Attached properties implemented as dependency properties support dependency property concepts, such as metadata that derived classes can override. Metadata for an attached property is generally no different than for a dependency property. You can override the default value, property change callbacks, and WPF framework properties for the inherited attached property, on instances of the overriding class. For more information, see Attached property metadata
Always use RegisterAttached to register properties where you specify Inherits in the metadata. Although property value inheritance might appear to work for nonattached dependency properties, the value inheritance behavior for a nonattached property through certain object-object divisions in the runtime tree is undefined. The
Inherits property isn't relevant for nonattached properties. For more information, see RegisterAttached(String, Type, Type, PropertyMetadata), and the remarks section of Inherits.
Add a class as owner of an attached property
To inherit an attached property from another class, but expose it as a nonattached dependency property on your class:
Call AddOwner to add your class as an owner of the attached dependency property.
Assign the return value of the
AddOwnercall to a
public static readonlyfield, for use as the dependency property identifier.
Define a CLR wrapper, which adds the property as a class member and supports nonattached property usage.