Bewerken

Delen via


Property value inheritance (WPF .NET)

Property value inheritance is a feature of the Windows Presentation Foundation (WPF) property system and applies to dependency properties. Property value inheritance lets child elements in a tree of elements obtain the value of a particular property from the nearest parent element. Since a parent element might also have obtained its property value through property value inheritance, the system potentially recurses back to the page root.

The WPF property system doesn't enable property value inheritance by default, and value inheritance is inactive unless specifically enabled in dependency property metadata. Even with property value inheritance enabled, a child element will only inherit a property value in the absence of a higher precedence value.

Prerequisites

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.

Inheritance through an element tree

Property value inheritance isn't the same concept as class inheritance in object-oriented programming, where derived classes inherit base class members. That kind of inheritance is also active in WPF, although in XAML the inherited base class properties are exposed as attributes of XAML elements that represent derived classes.

Property value inheritance is the mechanism by which a dependency property value propagates from parent to child elements within a tree of elements that contain the property. In XAML markup, a tree of elements is visible as nested elements.

The following example shows nested elements in XAML. WPF registers the AllowDrop dependency property on the UIElement class with property metadata that enables property value inheritance and sets the default value to false. The AllowDrop dependency property exists on Canvas, StackPanel, and Label elements since they all derive from UIElement. Since the AllowDrop dependency property on canvas1 is set to true, the descendant stackPanel1 and label1 elements inherit true as their AllowDrop value.

<Canvas x:Name="canvas1" Grid.Column="0" Margin="20" Background="Orange" AllowDrop="True">
    <StackPanel Name="stackPanel1" Margin="20" Background="Green">
        <Label Name="label1" Margin="20" Height="40" Width="40" Background="Blue"/>
    </StackPanel>
</Canvas>

You can also create a tree of elements programmatically by adding element objects to the child element collection of another element object. At run time, property value inheritance operates on the resultant object tree. In the following example, stackPanel2 is added to the child collection of canvas2. Similarly, label2 is added to the child collection of stackPanel2. Since the AllowDrop dependency property on canvas2 is set to true, the descendant stackPanel2 and label2 elements inherit true as their AllowDrop value.

Canvas canvas2 = new()
{
    AllowDrop = true
};
StackPanel stackPanel2 = new();
Label label2 = new();
canvas2.Children.Add(stackPanel2);
stackPanel2.Children.Add(label2);
Dim canvas2 As New Canvas With {
    .AllowDrop = True
}
Dim stackPanel2 As New StackPanel()
Dim label2 As New Label()
canvas2.Children.Add(stackPanel2)
stackPanel2.Children.Add(label2)

Practical applications of property value inheritance

Specific WPF dependency properties have value inheritance enabled by default, such as AllowDrop and FlowDirection. Typically, properties with value inheritance enabled by default are implemented on base UI element classes, so they exist on derived classes. For example, since AllowDrop is implemented on the UIElement base class, that dependency property also exists on every control derived from UIElement. WPF enables value inheritance on dependency properties for which it's convenient for a user to set the property value once on a parent element and have that property value propagate to descendant elements in the element tree.

The property value inheritance model assigns property values, both inherited and uninherited, according to dependency property value precedence. So, a parent element property value will only get applied to a child element, if the child element property doesn't have a higher precedence value, such as a locally set value, or a value obtained through styles, templates, or data binding.

The FlowDirection dependency property sets the layout direction of text and child UI elements within a parent element. Typically, you would expect the flow direction of text and UI elements within a page to be consistent. Since value inheritance is enabled in the property metadata of FlowDirection, a value need only be set once at the top of the element tree for a page. In the rare case where a mix of flow directions is intended for a page, a different flow direction can be set on an element in the tree by assigning a locally set value. The new flow direction will then propagate to descendant elements below that level.

Making a custom property inheritable

You can make a custom dependency property inheritable by enabling the Inherits property in an instance of FrameworkPropertyMetadata, and then registering your custom dependency property with that metadata instance. By default, Inherits is set to false in FrameworkPropertyMetadata. Making a property value inheritable affects performance, so only set Inherits to true if that feature is needed.

When you register a dependency property with Inherits enabled in metadata, use the RegisterAttached method as described in Register an attached property. Also, assign a default value to the property so that an inheritable value exists. You might also want to create a property wrapper with get and set accessors on the owner type, just as you would for a nonattached dependency property. That way you can set the property value using the property wrapper on an owner or derived type. The following example creates a dependency property named IsTransparent, with Inherits enabled and a default value of false. The example also includes a property wrapper with get and set accessors.

public class Canvas_IsTransparentInheritEnabled : Canvas
{
    // Register an attached dependency property with the specified
    // property name, property type, owner type, and property metadata
    // (default value is 'false' and property value inheritance is enabled).
    public static readonly DependencyProperty IsTransparentProperty =
        DependencyProperty.RegisterAttached(
            name: "IsTransparent",
            propertyType: typeof(bool),
            ownerType: typeof(Canvas_IsTransparentInheritEnabled),
            defaultMetadata: new FrameworkPropertyMetadata(
                defaultValue: false,
                flags: FrameworkPropertyMetadataOptions.Inherits));

    // Declare a get accessor method.
    public static bool GetIsTransparent(Canvas element)
    {
        return (bool)element.GetValue(IsTransparentProperty);
    }

    // Declare a set accessor method.
    public static void SetIsTransparent(Canvas element, bool value)
    {
        element.SetValue(IsTransparentProperty, value);
    }

    // For convenience, declare a property wrapper with get/set accessors.
    public bool IsTransparent
    {
        get => (bool)GetValue(IsTransparentProperty);
        set => SetValue(IsTransparentProperty, value);
    }
}
Public Class Canvas_IsTransparentInheritEnabled
    Inherits Canvas

    ' Register an attached dependency property with the specified
    ' property name, property type, owner type, and property metadata
    ' (default value is 'false' and property value inheritance is enabled).
    Public Shared ReadOnly IsTransparentProperty As DependencyProperty =
        DependencyProperty.RegisterAttached(
            name:="IsTransparent",
            propertyType:=GetType(Boolean),
            ownerType:=GetType(Canvas_IsTransparentInheritEnabled),
            defaultMetadata:=New FrameworkPropertyMetadata(
                defaultValue:=False,
                flags:=FrameworkPropertyMetadataOptions.[Inherits]))

    ' Declare a get accessor method.
    Public Shared Function GetIsTransparent(element As Canvas) As Boolean
        Return element.GetValue(IsTransparentProperty)
    End Function

    ' Declare a set accessor method.
    Public Shared Sub SetIsTransparent(element As Canvas, value As Boolean)
        element.SetValue(IsTransparentProperty, value)
    End Sub

    ' For convenience, declare a property wrapper with get/set accessors.
    Public Property IsTransparent As Boolean
        Get
            Return GetValue(IsTransparentProperty)
        End Get
        Set(value As Boolean)
            SetValue(IsTransparentProperty, value)
        End Set
    End Property
End Class

Attached properties are conceptually similar to global properties. You can check their value on any DependencyObject and get a valid result. The typical scenario for attached properties is to set property values on child elements, and that scenario is more effective if the property in question is implicitly present as an attached property on each DependencyObject element in the tree.

Inheriting property values across tree boundaries

Property inheritance works by traversing a tree of elements. This tree is often parallel to the logical tree. However, whenever you include a WPF core-level object, such as a Brush, in the markup that defines an element tree, you've created a discontinuous logical tree. A true logical tree doesn't conceptually extend through the Brush, because the logical tree is a WPF framework-level concept. You can use the helper methods of LogicalTreeHelper to analyze and view the extent of a logical tree. Property value inheritance is able to pass inherited values through a discontinuous logical tree, but only if the inheritable property was registered as an attached property and there isn't a deliberate inheritance-blocking boundary, such as a Frame.

Note

Although property value inheritance might appear to work for nonattached dependency properties, the inheritance behavior for a nonattached property through some element boundaries in the runtime tree is undefined. Whenever you specify Inherits in property metadata, register your properties using RegisterAttached.

See also