Información general sobre las propiedades adjuntas (WPF .NET)

Una propiedad adjunta es un concepto del Lenguaje de marcado de aplicación extensible (XAML). Las propiedades adjuntas permiten establecer pares de propiedades y valores adicionales en cualquier elemento XAML que derive de DependencyObject, aunque el elemento no defina esas propiedades adicionales en su modelo de objetos. Las propiedades adicionales son accesibles globalmente. Las propiedades adjuntas se definen normalmente como una forma especializada de propiedad de dependencia que no tiene un contenedor de propiedades convencional.

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 que tiene 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, le resultará útil estar familiarizado con XAML y saber cómo escribir aplicaciones de Windows Presentation Foundation (WPF).

Motivos para usar las propiedades adjuntas

Una propiedad adjunta permite que un elemento secundario especifique un valor único para una propiedad definida en un elemento primario. Un escenario común es un elemento secundario que especifica cómo debe representarse su elemento primario en la interfaz de usuario. Por ejemplo, DockPanel.Dock es una propiedad adjunta porque se establece en elementos secundarios de un DockPanel, no en el propio DockPanel. La clase DockPanel define un campo estático DependencyProperty, denominado DockProperty y, a continuación, proporciona los métodos GetDock y SetDock como descriptores de acceso públicos para la propiedad adjunta.

Propiedades adjuntas en XAML

En XAML se establecen las propiedades adjuntas mediante la sintaxis <attached property provider type>.<property name>, donde el proveedor de propiedades adjuntas es la clase que define la propiedad adjunta. En el ejemplo siguiente se muestra cómo un elemento secundario de DockPanel puede establecer el valor de la propiedad DockPanel.Dock.

<DockPanel>
    <TextBox DockPanel.Dock="Top">Enter text</TextBox>
</DockPanel>

El uso es similar al de una propiedad estática, porque se hace referencia al tipo que posee y registra la propiedad adjunta (por ejemplo, DockPanel), no el nombre de instancia.

Cuando se especifica una propiedad adjunta mediante un atributo XAML, solo se aplica la acción «establecer». No se puede obtener directamente un valor de propiedad a través de XAML, aunque hay algunos mecanismos indirectos para comparar valores, como desencadenadores en estilos.

Propiedades adjuntas en WPF

Las propiedades adjuntas son un concepto XAML, las propiedades de dependencia son un concepto de WPF. En WPF la mayoría de las propiedades adjuntas relacionadas con la interfaz de usuario en los tipos de WPF se implementan como propiedades de dependencia. Las propiedades adjuntas de WPF que se implementan como propiedades de dependencia admiten conceptos de propiedad de dependencia como los metadatos de propiedad, incluidos los valores predeterminados de los metadatos.

Modelos de uso de propiedades adjuntas

Aunque cualquier objeto puede establecer un valor de propiedad adjunta, eso no significa que establecer un valor producirá un resultado tangible o que otro objeto usará el valor. El propósito principal de las propiedades adjuntas es proporcionar una manera de que los objetos de una amplia variedad de jerarquías de clase y relaciones lógicas notifiquen información común al tipo que define la propiedad adjunta. El uso de propiedades adjuntas suele seguir uno de estos modelos:

  • El tipo que define la propiedad adjunta es el elemento primario de los elementos que establecen valores para la propiedad adjunta. El tipo primario recorre en iteración sus objetos secundarios a través de la lógica interna que actúa sobre la estructura del árbol de objetos, obtiene los valores y actúa sobre esos valores de alguna manera.
  • El tipo que define la propiedad adjunta se usa como elemento secundario para varios elementos primarios y modelos de contenido posibles.
  • El tipo que define la propiedad adjunta representa un servicio. Otros tipos establecen los valores de la propiedad adjunta. A continuación, cuando el elemento que establece la propiedad se evalúa en el contexto del servicio, los valores de la propiedad adjunta se obtienen a través de la lógica interna de la clase de servicio.

Ejemplo de una propiedad adjunta definida por el elemento primario

El escenario típico en el que WPF define una propiedad adjunta es cuando un elemento primario admite una colección de elementos secundarios y el elemento primario implementa un comportamiento basado en los datos notificados por cada uno de sus elementos secundarios.

DockPanel define la propiedad adjunta DockPanel.Dock. DockPanel tiene código de nivel de clase, específicamente MeasureOverride y ArrangeOverride, que forma parte de su lógica de representación. Una instancia de DockPanel comprueba si alguno de sus elementos secundarios inmediatos ha establecido un valor para DockPanel.Dock. Si es así, esos valores se convierten en entradas para la lógica de representación aplicada a cada elemento secundario. Aunque teóricamente es posible que las propiedades adjuntas influyan en elementos más allá del elemento primario inmediato, el comportamiento definido para una instancia anidada de DockPanel es interactuar solo con su colección de elementos secundarios inmediatos. Por lo tanto, si establece DockPanel.Dock en un elemento que no tiene ningún elemento primario DockPanel, no se producirá ningún error o excepción y habría creado un valor de propiedad global que no consumirá ningún DockPanel.

Propiedades adjuntas en el código

Las propiedades adjuntas de WPF no tienen los típicos métodos de contenedor CLR get y set porque las propiedades se pueden establecer desde fuera del espacio de nombres CLR. Para permitir que un procesador XAML establezca esos valores al analizar XAML, la clase que define la propiedad adjunta debe implementar métodos de descriptores de acceso dedicados en forma de Get<property name> y Set<property name>.

También puede usar los métodos de descriptores de acceso dedicados para obtener y establecer una propiedad adjunta en el código, como se muestra en el ejemplo siguiente. En el ejemplo, myTextBox es una instancia de la clase TextBox.

DockPanel myDockPanel = new();
TextBox myTextBox = new();
myTextBox.Text = "Enter text";

// Add child element to the DockPanel.
myDockPanel.Children.Add(myTextBox);

// Set the attached property value.
DockPanel.SetDock(myTextBox, Dock.Top);
Dim myDockPanel As DockPanel = New DockPanel()
Dim myTextBox As TextBox = New TextBox()
myTextBox.Text = "Enter text"

' Add child element to the DockPanel.
myDockPanel.Children.Add(myTextBox)

' Set the attached property value.
DockPanel.SetDock(myTextBox, Dock.Top)

Si no agrega myTextBox como elemento secundario de myDockPanel, llamar a SetDock no iniciará una excepción ni tendrá ningún efecto. Solo un valor DockPanel.Dock establecido en un elemento secundario de un DockPanel puede afectar a la representación, y la representación será la misma si establece el valor antes o después de agregar el elemento secundario a DockPanel.

Desde la perspectiva del código, una propiedad adjunta es como un campo de respaldo que tiene los descriptores de acceso de método en lugar de los descriptores de acceso de propiedad, y se puede establecer en cualquier objeto sin necesidad de definir primero en esos objetos.

Metadatos de las propiedades adjuntas

Los metadatos de una propiedad adjunta no suelen ser distintos de los de una propiedad de dependencia. Al registrar una propiedad adjunta, use FrameworkPropertyMetadata para especificar características de la propiedad, como si la propiedad afecta o no a la representación o la medida. Cuando se especifica un valor predeterminado mediante la invalidación de los metadatos de propiedad adjunta, ese valor se convierte en el valor predeterminado de la propiedad adjunta implícita en las instancias de la clase de invalidación. Si no se establece un valor de propiedad adjunto, el valor predeterminado se notifica cuando se consulta la propiedad mediante el descriptor de acceso Get<property name> con una instancia de la clase donde ha especificado los metadatos.

Para habilitar la herencia de valores de propiedad en una propiedad, use propiedades adjuntas en lugar de propiedades de dependencia no adjuntas. Para obtener más información, consulte Herencia de valores de propiedad.

Propiedades adjuntas personalizadas

Cuándo crear una propiedad adjunta

La creación de una propiedad adjunta es útil cuando:

  • Necesita un mecanismo de configuración de propiedades disponible para las clases que no son la clase de definición. Un escenario común es para el diseño de la interfaz de usuario, por ejemplo DockPanel.Dock, Panel.ZIndex y Canvas.Top son todos ejemplos de propiedades de diseño existentes. En el escenario de diseño, los elementos secundarios de un elemento de control de diseño pueden expresar los requisitos de diseño a su elemento primario de diseño y establecer un valor para una propiedad adjunta definida por el elemento primario.

  • Una de las clases representa un servicio y desea que otras clases integren el servicio de forma más transparente.

  • Quiere compatibilidad para Visual Studio WPF Designer, como la capacidad de editar una propiedad a través de la ventana Propiedades. Para obtener más información, consulte Información general sobre la creación de controles.

  • Quiere usar la herencia de valores de propiedad.

Cómo crear una propiedad adjunta

Si la clase define una propiedad adjunta únicamente para su uso por otros tipos, no es necesario que la clase derive de DependencyObject. De lo contrario, siga el modelo de WPF para que una propiedad adjunta también sea una propiedad de dependencia, derivando la clase de DependencyObject.

Defina la propiedad adjunta como una dependencia en la clase de definición declarando un campo public static readonly de tipo DependencyProperty. A continuación, asigne el valor devuelto del método RegisterAttached al camp, que también se conoce como identificador de propiedad. Siga la convención de nomenclatura de propiedades de WPF que distingue los campos de las propiedades que representan, asignando el nombre al campo de identificador <property name>Property. Además, proporcione métodos estáticos y de descriptores de acceso Get<property name> y Set<property name>, lo que permite que el sistema de propiedades acceda a la propiedad adjunta.

En el ejemplo siguiente se muestra cómo registrar una propiedad de dependencia mediante el método RegisterAttached y cómo definir los métodos de descriptores de acceso. En el ejemplo, el nombre de la propiedad adjunta es HasFish, por lo que el campo de identificador se denomina HasFishProperty y los métodos de descriptores de acceso se denominan GetHasFish y SetHasFish.

public class Aquarium : UIElement
{
    // Register an attached dependency property with the specified
    // property name, property type, owner type, and property metadata.
    public static readonly DependencyProperty HasFishProperty = 
        DependencyProperty.RegisterAttached(
      "HasFish",
      typeof(bool),
      typeof(Aquarium),
      new FrameworkPropertyMetadata(defaultValue: false,
          flags: FrameworkPropertyMetadataOptions.AffectsRender)
    );

    // Declare a get accessor method.
    public static bool GetHasFish(UIElement target) =>
        (bool)target.GetValue(HasFishProperty);

    // Declare a set accessor method.
    public static void SetHasFish(UIElement target, bool value) =>
        target.SetValue(HasFishProperty, value);
}
Public Class Aquarium
    Inherits UIElement

    ' Register an attached dependency property with the specified
    ' property name, property type, owner type, and property metadata.
    Public Shared ReadOnly HasFishProperty As DependencyProperty =
        DependencyProperty.RegisterAttached("HasFish", GetType(Boolean), GetType(Aquarium),
            New FrameworkPropertyMetadata(defaultValue:=False,
                flags:=FrameworkPropertyMetadataOptions.AffectsRender))

    ' Declare a get accessor method.
    Public Shared Function GetHasFish(target As UIElement) As Boolean
        Return target.GetValue(HasFishProperty)
    End Function

    ' Declare a set accessor method.
    Public Shared Sub SetHasFish(target As UIElement, value As Boolean)
        target.SetValue(HasFishProperty, value)
    End Sub

End Class

El descriptor de acceso Get

La firma del método de descriptor de acceso get es public static object Get<property name>(DependencyObject target) donde:

  • target es el DependencyObject desde el que se lee la propiedad adjunta. El tipo target puede ser más específico que DependencyObject. Por ejemplo, el método de descriptores de acceso DockPanel.GetDock escribe target como UIElement porque la propiedad adjunta está diseñada para establecerse en instancias de UIElement. UiElement deriva indirectamente de DependencyObject.
  • El tipo de valor devuelto puede ser más específico que object. Por ejemplo, el método GetDock escribe el valor devuelto como Dock porque el valor devuelto debe ser una enumeración Dock.

Nota:

El descriptor de acceso get de una propiedad adjunta es necesario para la compatibilidad con el enlace de datos en herramientas de diseño como Visual Studio o Blend para Visual Studio.

El descriptor de acceso Set

La firma del método de descriptor de acceso set es public static void Set<property name>(DependencyObject target, object value), donde:

  • target es el DependencyObject en el que se escribe la propiedad adjunta. El tipo target puede ser más específico que DependencyObject. Por ejemplo, el método SetDock escribe target como UIElement porque la propiedad adjunta está diseñada para establecerse en instancias de UIElement. UiElement deriva indirectamente de DependencyObject.
  • El tipo value puede ser más específico que object. Por ejemplo, el método SetDock requiere un valor Dock. El cargador XAML debe ser capaz de generar el tipo value a partir de la cadena de marcado que representa el valor de propiedad adjunta. Por lo tanto, debe haber compatibilidad con la conversión de tipos, el serializador de valores o la extensión de marcado para el tipo que use.

Atributos de propiedades adjuntas

WPF define varios atributos de .NET que proporcionan información sobre las propiedades adjuntas a los procesos de reflexión y también a los consumidores de información de reflexión y propiedades, como los diseñadores. Los diseñadores usan atributos de .NET definidos por WPF para limitar las propiedades que se muestran en la ventana de propiedades, con el fin de evitar que los usuarios abrumen con una lista global de todas las propiedades adjuntas. Considere la posibilidad de aplicar estos atributos a sus propias propiedades adjuntas personalizadas. El propósito y la sintaxis de los atributos de .NET se describen en estas páginas de referencia:

Saber más

  • Para obtener más información sobre cómo crear una propiedad adjunta, consulte Registrar una propiedad adjunta.
  • Para obtener escenarios de uso más avanzados sobre las propiedades de dependencia y las propiedades adjuntas, consulte Propiedades de dependencia personalizadas.
  • Puede registrar una propiedad como una propiedad adjunta y como una propiedad de dependencia e incluir contenedores de propiedades convencionales. De esta manera, una propiedad se puede establecer en un elemento mediante contenedores de propiedades y también en cualquier otro elemento mediante la sintaxis de propiedades adjuntas XAML. Para obtener un ejemplo, consulte FrameworkElement.FlowDirection.

Vea también