Наследование значений свойств (WPF .NET)

Наследование значений свойств представляет собой возможность системы свойств WPF. Наследование значений свойств позволяет дочерним элементам в дереве элементов получать значение определенного свойства от ближайшего родительского элемента. Поскольку родительский элемент также мог получить свое значение свойства через наследование значения свойства, система потенциально рекурсивно может возвращаться к корню страницы.

Система свойств WPF по умолчанию не допускает наследование значений свойств и наследование значений неактивно. Его требуется специально включить в метаданных свойства зависимостей. Даже с включенным наследованием значений свойств дочерний элемент будет наследовать значение свойства только в отсутствие значения с более высоким приоритетом.

Необходимые компоненты

Для понимания статьи нужно иметь базовые знания о свойствах зависимостей и прочитать Общие сведения о свойствах зависимостей. Чтобы понимать примеры в этой статье полезно познакомиться с языком XAML и узнать, как создавать приложения WPF.

Наследование по дереву элементов

Наследование значений свойств не совпадает с понятием наследования классов в объектно-ориентированном программировании, когда производные классы наследуют члены базового класса. Этот тип наследования также активен в WPF, хотя в XAML унаследованные свойства базового класса предстают в виде атрибутов элементов XAML, представляющих производные классы.

Наследование значений свойств — это механизм, с помощью которого значение свойства зависимостей распространяется от родительского элемента к дочерним по дереву элементов, содержащих данное свойство. В разметке XAML дерево элементов отображается как вложенные элементы.

В следующем примере показаны вложенные элементы в XAML. WPF регистрирует свойство зависимостей AllowDrop в класса UIElement с метаданными свойства, которые обеспечивают наследование значений свойства и задают значение по умолчанию false. Свойство зависимостей AllowDrop существует в элементах Canvas, StackPanel и Label, поскольку все они производные от UIElement. Поскольку свойству зависимостей AllowDrop в canvas1 задано значениеtrue, потомок stackPanel1 и элементы label1 наследуют true в качестве своего значения AllowDrop.

<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>

Дерево элементов также можно создать программным способом, добавив объекты элементов в коллекцию дочерних элементов другого объекта элемента. Во время выполнения наследование значений свойств работает в результирующем деревом объектов. В следующем примере stackPanel2 добавляется в дочернюю коллекциюобъекта canvas2. Аналогичным образом label2 добавляется в дочернюю коллекцию объекта stackPanel2. Поскольку свойству зависимостей AllowDrop в canvas2 задано значениеtrue, потомок stackPanel2 и элементы label2 наследуют true в качестве своего значения AllowDrop.

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)

Практическое применение наследования значения свойства

Для определенных свойств зависимостей WPF наследование значений включено по умолчанию, например AllowDrop и FlowDirection. Как правило, свойства с наследованием значений, включенные по умолчанию, реализуются в классах базовых элементов пользовательского интерфейса, поэтому они существуют в производных классах. Например, так как свойство AllowDrop реализовано в базовом классе UIElement. Это свойство зависимостей также существует для каждого элемента управления, производного от UIElement. WPF позволяет наследовать значения свойств зависимостей, для которых удобно задать значение свойства один раз в родительском элементе и распространить это значение свойства на элементы-потомки в дереве элементов.

В модели наследования значений свойств значения свойств, унаследованные и неунаследованные, назначаются в соответствии с приоритетом значений свойств зависимостей. Таким образом, значение свойства родительского элемента применяется к дочернему элементу, только если у свойства дочернего элемента нет значения с более высоким приоритетом, например локально заданного значения или значения, полученное посредством стилей, шаблонов или привязки данных.

Свойство зависимостей FlowDirection задает направление макета текстовых и дочерних элементов пользовательского интерфейса в родительском элементе. Как правило, ожидается, что направление потока текста и элементов пользовательского интерфейса на странице будет согласовано. Так как наследование значений разрешено в метаданных свойства FlowDirection, значение необходимо задать только один раз в верхней части дерева элементов страницы. В редких случаях, когда для страницы предполагается сочетание направлений потока, элементу в дереве можно задать другое направление потока, назначив локально заданное значение. Новое направление потока распространяется на элементы-потомки, расположенные ниже этого уровня.

Создание настраиваемого наследуемого свойства

Настраиваемое свойство зависимости можно сделать наследуемым, включив свойство Inherits в экземпляре FrameworkPropertyMetadata, а затем зарегистрировав пользовательское свойство зависимостей в этом экземпляре метаданных. По умолчанию свойству Inherits задано значение false в FrameworkPropertyMetadata. Создание наследуемого значения свойства влияет на производительность, поэтому свойству Inherits задается значение true, только если эта возможность необходима.

При регистрации свойства зависимостей со свойством Inherits, включенным в метаданных, используйте метод RegisterAttached, как описано в статье Регистрация присоединенного свойства. Также назначьте свойству значение по умолчанию, чтобы существовало наследуемое значение. Кроме того, может потребоваться создать оболочку свойств с методами доступа get и set для заданного типа владельца, так же как и для присоединенного свойства зависимостей. Таким образом с помощью оболочки свойства значение свойства можно задать для владельца или производного типа. В следующем примере создается свойство зависимостей IsTransparent с включенным свойством Inherits и значением по умолчанию false. В этом примере также содержится оболочка свойств с методами доступа get и set.

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

Присоединенные свойства концептуально похожи на глобальные свойства. Можно проверить их значение в любом объекте DependencyObject и получить допустимый результат. Типичный сценарий для присоединенных свойств — установка значений свойств для дочерних элементов. Этот сценарий более эффективен, если рассматриваемое свойство неявно присутствует в качестве присоединенного свойства в каждом элементе (DependencyObject) в дереве.

Наследование значений свойств за границами дерева

Наследование свойств работает путем обхода дерева элементов. Это дерево часто параллельно логическому дереву. Однако при включении объекта уровня ядра WPF, такого как Brush, в разметку, определяющую дерево элементов, создается несплошное логическое дерево. Истинное логическое дерево концептуально не расширяется посредством Brush, потому что логическое дерево — это концепция уровня платформы WPF. Для анализа и просмотра экстентов логического дерева можно использовать вспомогательные методы LogicalTreeHelper. При наследовании значений свойств унаследованные значения могут передаваться через несплошное логическое дерево, но только если наследуемое свойство было зарегистрировано как присоединенное свойство и отсутствуют преднамеренные границы блокировки наследования, такие как Frame.

Примечание.

Несмотря на то что наследование значения свойства может выполняться для неприсоединенных свойств зависимостей, поведение наследования для таких свойств через определенные границы элементов в дереве среды выполнения не определено. Каждый раз, указывая Inherits в метаданных свойств, зарегистрируйте свойства с помощью RegisterAttached.

См. также