Herencia de valores de propiedad (WPF .NET)

La herencia de valores de propiedad es una característica del sistema de propiedades de Windows Presentation Foundation (WPF) y se aplica a las propiedades de dependencia. La herencia de valores de propiedad permite que los elementos secundarios de un árbol de elementos obtengan el valor de una propiedad determinada del elemento principal más cercano. Puesto que un elemento principal también podría haber obtenido su valor de propiedad a través de la herencia de valores de propiedad, el sistema puede volver a la raíz de la página.

El sistema de propiedades de WPF no habilita la herencia de valores de propiedad de manera predeterminada, y la herencia de valores está inactiva a menos que se habilite específicamente en los metadatos de la propiedad de dependencia. Incluso con la herencia de valores de propiedad habilitada, un elemento secundario solo heredará un valor de propiedad en ausencia de un valor de precedencia mayor.

Importante

La documentación de la guía de escritorio para .NET 7 y .NET 6 está en proceso de elaboración.

Requisitos previos

En el artículo se da por supuesto un conocimiento básico de las propiedades de dependencia y que ha leído Información general sobre las propiedades de dependencia. Para seguir los ejemplos de este artículo, resultará útil que esté familiarizado con el lenguaje XAML y que sepa cómo escribir aplicaciones WPF.

Herencia a través de un árbol de elementos

La herencia de valores de propiedad no es el mismo concepto que la herencia de clases en la programación orientada a objetos, en que las clases derivadas heredan los miembros de la clase base. Ese tipo de herencia también está activo en WPF, aunque en XAML las propiedades de la clase base heredadas se exponen como atributos de los elementos XAML que representan las clases derivadas.

La herencia de valores de propiedad es el mecanismo por el que un valor de propiedad de dependencia se propaga de elementos principales a elementos secundarios dentro de un árbol de elementos que contienen la propiedad. En el marcado XAML, un árbol de elementos es visible como elementos anidados.

En el ejemplo siguiente se muestran los elementos anidados en XAML. WPF registra la propiedad de dependencia AllowDrop en la clase UIElement con los metadatos de la propiedad que habilita la herencia de valores de propiedad y establece el valor predeterminado en false. La propiedad de dependencia AllowDrop existe en los elementos Canvas, StackPanel y Label, ya que todos derivan de UIElement. Dado que la propiedad de dependencia AllowDrop de canvas1 se establece en true, los elementos stackPanel1 y label1 descendientes heredan true como su valor 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>

También puede crear un árbol de elementos mediante programación agregando objetos de elemento a la colección de elementos secundarios de otro objeto de elemento. En tiempo de ejecución, la herencia de valores de propiedad funciona en el árbol de objetos resultante. En el ejemplo siguiente, stackPanel2 se agrega a la colección secundaria de canvas2. De forma similar, label2 se agrega a la colección secundaria de stackPanel2. Dado que la propiedad de dependencia AllowDrop de canvas2 se establece en true, los elementos stackPanel2 y label2 descendientes heredan true como su valor 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)

Aplicaciones prácticas de la herencia de valores de propiedad

Las propiedades de dependencia específicas de WPF tienen habilitada la herencia de valores de manera predeterminada, como AllowDrop y FlowDirection. Normalmente, las propiedades con la herencia de valores habilitada de manera predeterminada se implementan en clases de elementos de interfaz de usuario base, por lo que existen en las clases derivadas. Por ejemplo, dado que AllowDrop se implementa en la clase base UIElement, esa propiedad de dependencia también existe en cada control derivado de UIElement. WPF habilita la herencia de valores en propiedades de dependencia para las que es conveniente que un usuario establezca el valor de propiedad una vez en un elemento principal y que ese valor de propiedad se propague a los elementos descendientes en el árbol de elementos.

El modelo de herencia de valores de propiedad asigna valores de propiedad, tanto heredados como no heredados, según la precedencia de valores de propiedad de dependencia. Así pues, un valor de propiedad de un elemento principal solo se aplicará a un elemento secundario si la propiedad del elemento secundario no tiene un valor de precedencia mayor, como un valor establecido localmente, o un valor obtenido a través de estilos, plantillas o enlace de datos.

La propiedad de dependencia FlowDirection establece la dirección de diseño de los elementos de la interfaz de usuario secundaria y de texto dentro de un elemento principal. Normalmente, esperaría que la dirección del flujo de texto y de los elementos de la interfaz de usuario dentro de una página sea coherente. Puesto que la herencia de valores está habilitada en los metadatos de la propiedad de FlowDirection, un valor solo se debe establecer una vez en la parte superior del árbol de elementos de una página. En el caso poco frecuente en el que se prevea una combinación de direcciones de flujo para una página, se puede establecer una dirección de flujo diferente en un elemento del árbol mediante la asignación de un valor establecido localmente. A continuación, la nueva dirección del flujo se propagará a los elementos descendientes por debajo de ese nivel.

Hacer heredable una propiedad personalizada

Puede hacer que una propiedad de dependencia personalizada sea heredable habilitando la propiedad Inherits en una instancia de FrameworkPropertyMetadata y, a continuación, registrando la propiedad de dependencia personalizada con esa instancia de metadatos. De manera predeterminada, Inherits se establece en false en FrameworkPropertyMetadata. Hacer que un valor de propiedad sea heredable afecta al rendimiento, por lo que solo debe establecer Inherits en true si esa característica es necesaria.

Al registrar una propiedad de dependencia con Inherits habilitado en los metadatos, use el método RegisterAttached tal y como se describe en Registro de una propiedad adjunta. Asimismo, asigne un valor predeterminado a la propiedad para que exista un valor heredable. También puede crear un contenedor de propiedades con los descriptores de acceso get y set en el tipo de propietario, igual que lo haría para una propiedad de dependencia no adjunta. De este modo, puede establecer el valor de propiedad mediante el contenedor de propiedades en un propietario o un tipo derivado. En el ejemplo siguiente se crea una propiedad de dependencia denominada IsTransparent, con Inherits habilitado y un valor predeterminado de false. En el ejemplo también se incluye un contenedor de propiedades con los descriptores de acceso get y 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

Las propiedades adjuntas son conceptualmente parecidas a las propiedades globales. Puede comprobar su valor en cualquier DependencyObject y obtener un resultado válido. El escenario típico para las propiedades adjuntas consiste en establecer valores de propiedad en los elementos secundarios, y ese escenario es más eficaz si la propiedad en cuestión está presente implícitamente como una propiedad adjunta en cada elemento DependencyObject del árbol.

Herencia de valores de propiedad entre límites de árbol

La herencia de propiedades funciona al recorrer un árbol de elementos. Este árbol suele ser paralelo al árbol lógico. Sin embargo, siempre que incluya un objeto de nivel básico WPF, como Brush, en el marcado que define un árbol de elementos, habrá creado un árbol lógico discontinuo. Un verdadero árbol lógico no se extiende conceptualmente a través de Brush, ya que el árbol lógico es un concepto de nivel de marco de WPF. Puede usar los métodos auxiliares de LogicalTreeHelper para analizar y ver la extensión de un árbol lógico. La herencia de valores de propiedad puede pasar valores heredados a través de un árbol lógico discontinuo, pero solo si la propiedad heredable se registró como una propiedad adjunta y no hay un límite deliberado de bloqueo de herencia, como Frame.

Nota:

Aunque puede parecer que la herencia de valores de propiedad funciona para las propiedades de dependencia no adjuntas, el comportamiento de herencia de una propiedad no adjunta a través de algunos límites de elementos en el árbol en tiempo de ejecución no está definido. Siempre que especifique Inherits en los metadatos de la propiedad, registre las propiedades mediante RegisterAttached.

Vea también