Introducción a las propiedades de dependencia

Windows Presentation Foundation (WPF) proporciona un conjunto de servicios que se pueden usar para ampliar la funcionalidad de la propiedad de un tipo. Colectivamente, se suele hacer referencia a estos servicios como el sistema de propiedades de WPF. Una propiedad respaldada por el sistema de propiedades de WPF se conoce como una propiedad de dependencia. En esta información general se describe el sistema de propiedades de WPF y las funcionalidades de una propiedad de dependencia. Esto incluye cómo usar las propiedades de dependencia existentes en XAML y en el código. Esta información general también presenta aspectos especializados de las propiedades de dependencia, como los metadatos de las propiedades de dependencia y el proceso de creación de una propiedad de dependencia propia en una clase personalizada.

Requisitos previos

En este tema se supone que tiene conocimientos básicos sobre el sistema de tipos de .NET y la programación orientada a objetos. Para seguir los ejemplos de este tema, también debe comprender el lenguaje XAML y saber cómo escribir aplicaciones de WPF. Para obtener más información, vea Tutorial: Mi primera aplicación de escritorio WPF.

Propiedades de dependencia y propiedades CLR

En WPF, las propiedades se suelen exponer como propiedades estándar de .NET. En un nivel básico, podría interactuar directamente con estas propiedades y no llegar a saber que están implementadas como una propiedad de dependencia. Pero debería familiarizarse con todas o algunas de las características del sistema de propiedades de WPF, a fin de poder aprovechar estas características.

El propósito de las propiedades de dependencia es proporcionar una manera de calcular el valor de una propiedad en función del valor de otras entradas. Estas otras entradas pueden incluir propiedades del sistema, tales como temas y preferencias del usuario, mecanismos de determinación de propiedades Just-In-Time como el enlace de datos y las animaciones o los guiones gráficos, plantillas de usos múltiples como recursos y estilos, o valores conocidos a través de relaciones primario-secundario con otros elementos del árbol de elementos. Además, una propiedad de dependencia se puede implementar para proporcionar una validación autocontenida, valores predeterminados, devoluciones de llamada que controlen los cambios en otras propiedades y un sistema que pueda forzar los valores de propiedad de acuerdo con la información de tiempo de ejecución en potencia. Las clases derivadas también pueden cambiar algunas características concretas de una propiedad existente mediante la invalidación de los metadatos de la propiedad de dependencia, en lugar de invalidar la implementación real de las propiedades existentes o de crear nuevas propiedades.

En la referencia del SDK, puede identificar qué propiedad es una propiedad de dependencia por la presencia de la sección de información de la propiedad de dependencia en la página de referencia administrada de esa propiedad. En la sección de información de las propiedades de dependencia se incluye un vínculo al campo de identificador DependencyProperty de esa propiedad de dependencia, así como una lista de las opciones de metadatos establecidas para esa propiedad, información de invalidación por clases y otros detalles.

Las propiedades de dependencia respaldan las propiedades CLR

Las propiedades de dependencia y el sistema de propiedades de WPF extienden la funcionalidad de las propiedades al proporcionar un tipo que respalda una propiedad, como una implementación alternativa al patrón estándar de respaldar la propiedad con un campo privado. El nombre de este tipo es DependencyProperty. El otro tipo importante que define el sistema de propiedades de WPF es DependencyObject. DependencyObject define la clase base que puede registrar y poseer una propiedad de dependencia.

A continuación se enumera la terminología que se usa con las propiedades de dependencia:

  • Propiedad de dependencia: una propiedad que está respaldada por una DependencyProperty.

  • Identificador de la propiedad de dependencia: una instancia de DependencyProperty, que se obtiene como un valor devuelto al registrar una propiedad de dependencia y, después, se almacena como un miembro estático de una clase. Este identificador se usa como un parámetro para muchas de las API que interactúan con el sistema de propiedades de WPF.

  • "Contenedor" de CLR: implementaciones de get y set reales de la propiedad. Estas implementaciones incorporan el identificador de la propiedad de dependencia usándolo en las llamadas a GetValue y SetValue, proporcionando así el respaldo para la propiedad que usa el sistema de propiedades WPF.

En el ejemplo siguiente se define la propiedad de dependencia IsSpinning y se muestra la relación del identificador DependencyProperty con la propiedad que respalda.

public static readonly DependencyProperty IsSpinningProperty =
    DependencyProperty.Register(
    "IsSpinning", typeof(Boolean),
    typeof(MyCode)
    );
public bool IsSpinning
{
    get { return (bool)GetValue(IsSpinningProperty); }
    set { SetValue(IsSpinningProperty, value); }
}
Public Shared ReadOnly IsSpinningProperty As DependencyProperty =
    DependencyProperty.Register("IsSpinning",
                                GetType(Boolean),
                                GetType(MyCode))

Public Property IsSpinning() As Boolean
    Get
        Return CBool(GetValue(IsSpinningProperty))
    End Get
    Set(ByVal value As Boolean)
        SetValue(IsSpinningProperty, value)
    End Set
End Property

La convención de nomenclatura de la propiedad y su campo de respaldo DependencyProperty es importante. El nombre del campo siempre es el nombre de la propiedad, con el sufijo Property anexado. Para obtener más información acerca de esta convención y las razones para usarla, consulte Propiedades de dependencia personalizadas.

Establecimiento de valores de propiedad

Puede establecer propiedades en el código o en XAML.

Establecimiento de valores de propiedad en XAML

El siguiente ejemplo de XAML especifica el color de fondo de un botón como rojo. En este ejemplo se muestra un caso donde el analizador de XAML de WPF convirtió el tipo del valor de cadena simple de un atributo XAML en un tipo WPF (Color, por medio de SolidColorBrush) en el código generado.

<Button Background="Red" Content="Button!"/>

XAML admite una variedad de formatos de sintaxis para establecer propiedades. La sintaxis que se debe usar para una propiedad determinada dependerá del tipo de valor que use la propiedad, así como de otros factores, tal como la presencia de un convertidor de tipos. Para obtener más información sobre la sintaxis XAML para establecer propiedades, vea XAML en WPF y Sintaxis de XAML detallada.

Como ejemplo de sintaxis sin atributos, en el ejemplo de XAML siguiente se muestra otro fondo de botón. Esta vez, en lugar de establecer un color sólido simple, el fondo se establece en una imagen, con un elemento que representa esa imagen y el origen de la imagen especificado como un atributo del elemento anidado. Este es un ejemplo de sintaxis de elemento de propiedad.

<Button Content="Button!">
  <Button.Background>
    <ImageBrush ImageSource="wavy.jpg"/>
  </Button.Background>
</Button>

Establecimiento de propiedades en el código

Establecer valores de propiedad de dependencia en el código solo suele requerir una llamada a la implementación establecida que expone el "contenedor" de CLR.

Button myButton = new Button();
myButton.Width = 200.0;
Dim myButton As New Button()
myButton.Width = 200.0

Para obtener un valor de propiedad también se requiere esencialmente una llamada a la implementación de get del "contenedor":

double whatWidth;
whatWidth = myButton.Width;
Dim whatWidth As Double
whatWidth = myButton.Width

También puede llamar a las API del sistema de propiedades GetValue y SetValue directamente. Generalmente, no es necesario si usa las propiedades existentes (los contenedores son más prácticos y ofrecen una mejor exposición de la propiedad para las herramientas de desarrollo), pero llamar a API directamente es adecuado en determinados escenarios.

Las propiedades también se pueden establecer en XAML y se puede acceder a ellas más adelante en el código a través del código subyacente. Para obtener información detallada, vea Código subyacente y XAML en WPF.

Funcionalidad de propiedad proporcionada por una propiedad de dependencia

Una propiedad de dependencia proporciona una funcionalidad que amplía la funcionalidad de una propiedad, al contrario que una propiedad respaldada por un campo. A menudo, esta funcionalidad representa o admite una de las características específicas siguientes:

Recursos

Un valor de propiedad de dependencia se puede establecer mediante una referencia a un recurso. Los recursos se especifican normalmente como el valor de propiedad Resources de un elemento de raíz de la página o de la aplicación (estas ubicaciones permiten un acceso más cómodo al recurso). En el ejemplo siguiente se muestra cómo definir un recurso SolidColorBrush.

<DockPanel.Resources>
  <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
</DockPanel.Resources>

Una vez que el recurso está definido, puede hacer referencia al recurso y usarlo para proporcionar un valor de propiedad:

<Button Background="{DynamicResource MyBrush}" Content="I am gold" />

Se hace referencia a este recurso concreto como una Extensión de marcado DynamicResource (en XAML de WPF, puede usar una referencia a recursos estáticos o dinámicos). Para usar una referencia a recursos dinámicos, se debe establecer una propiedad de dependencia, que es específicamente el uso de referencias a recursos dinámicos que permite el sistema de propiedades de WPF. Para obtener más información, consulte Recursos XAML.

Nota:

Los recursos se tratan como un valor local, lo que significa que si establece otro valor local, eliminará la referencia a los recursos. Para obtener más información, consulte Prioridad de los valores de propiedades de dependencia.

Enlace de datos

Una propiedad de dependencia puede hacer referencia a un valor mediante el enlace de datos. El enlace de datos funciona mediante una sintaxis de extensión de marcado específica en XAML, o bien mediante el objeto Binding en el código. Con el enlace de datos, la determinación del valor de propiedad final se aplaza hasta el tiempo de ejecución, momento en el que se obtiene el valor de un origen de datos.

En el ejemplo siguiente se establece la propiedad Content para un Button, mediante un enlace declarado en XAML. El enlace usa un contexto de datos heredado y un origen de datos XmlDataProvider (no se muestra). El propio enlace especifica la propiedad de origen deseada mediante XPath en el origen de datos.

<Button Content="{Binding XPath=Team/@TeamName}"/>

Nota:

Los enlaces se tratan como un valor local, lo que significa que si establece otro valor local, eliminará el enlace. Para obtener más información, consulte Prioridad de los valores de propiedades de dependencia.

Las propiedades de dependencia, o bien la clase DependencyObject, no admiten de forma nativa INotifyPropertyChanged para fines de generación de notificaciones de cambios en el valor de propiedad de origen DependencyObject para las operaciones de enlace de datos. Para obtener más información acerca de cómo crear propiedades para su uso en un enlace de datos capaz de notificar los cambios en un destino de enlace de datos, consulte Información general sobre el enlace de datos.

Estilos

Los estilos y las plantillas son dos de los principales escenarios que invitan a usar las propiedades de dependencia. Los estilos son particularmente útiles para establecer las propiedades que definen la interfaz de usuario (UI) de la aplicación. Los estilos se definen normalmente como recursos en XAML. Los estilos interactúan con el sistema de propiedades porque suelen contener "establecedores" para determinadas propiedades, así como "desencadenadores" que cambian un valor de propiedad según el valor en tiempo real de otra propiedad.

En el ejemplo siguiente se crea un estilo sencillo (que se definiría dentro de un diccionario Resources, que no se muestra), y después se aplica ese estilo directamente a la propiedad Style de un Button. El establecedor dentro del estilo establece la propiedad Background de un Button con estilo en el color verde.

<Style x:Key="GreenButtonStyle">
  <Setter Property="Control.Background" Value="Green"/>
</Style>
<Button Style="{StaticResource GreenButtonStyle}">I am green!</Button>

Para obtener más información, consulte Aplicar estilos y plantillas.

Animaciones

Las propiedades de dependencia se pueden animar. Cuando una animación está aplicada y en ejecución, el valor animado funciona con una precedencia más alta que cualquier otro valor (por ejemplo, un valor local) que tenga la propiedad.

En el ejemplo siguiente se anima el atributo Background de una propiedad Button (técnicamente, se anima Background mediante la sintaxis de elemento de propiedad para especificar un SolidColorBrush en blanco para Background, y después la propiedad Color de ese SolidColorBrush es la que se anima directamente).

<Button>I am animated
  <Button.Background>
    <SolidColorBrush x:Name="AnimBrush"/>
  </Button.Background>
  <Button.Triggers>
    <EventTrigger RoutedEvent="Button.Loaded">
      <BeginStoryboard>
        <Storyboard>
          <ColorAnimation
            Storyboard.TargetName="AnimBrush" 
            Storyboard.TargetProperty="(SolidColorBrush.Color)"
            From="Red" To="Green" Duration="0:0:5" 
            AutoReverse="True" RepeatBehavior="Forever" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Button.Triggers>
</Button>

Para obtener más información sobre la animación de propiedades, consulte Información general sobre animaciones e Información general sobre objetos Storyboard.

Invalidaciones de metadatos

Puede cambiar ciertos comportamientos de una propiedad de dependencia mediante la invalidación de los metadatos de la propiedad cuando se deriva de la clase que originalmente registra la propiedad de dependencia. La invalidación de metadatos se basa en el identificador DependencyProperty. Para invalidar los metadatos no es necesario volver a implementar la propiedad. El sistema de propiedades controla de forma nativa el cambio en los metadatos; cada clase puede contener metadatos individuales de todas las propiedades que se heredan de las clases base, por tipo.

En el ejemplo siguiente se invalidan los metadatos para una propiedad de dependencia DefaultStyleKey. La invalidación de los metadatos de esta propiedad de dependencia concreta forma parte de un patrón de implementación que crea controles que pueden usar estilos predeterminados de temas.

public class SpinnerControl : ItemsControl
{
    static SpinnerControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(SpinnerControl),
            new FrameworkPropertyMetadata(typeof(SpinnerControl))
        );
    }
}
Public Class SpinnerControl
    Inherits ItemsControl
    Shared Sub New()
        DefaultStyleKeyProperty.OverrideMetadata(GetType(SpinnerControl), New FrameworkPropertyMetadata(GetType(SpinnerControl)))
    End Sub
End Class

Para obtener más información sobre cómo invalidar u obtener los metadatos de las propiedades, consulte Metadatos de las propiedades de dependencia.

Herencia de valores de propiedad

Un elemento puede heredar el valor de una propiedad de dependencia de su elemento primario en el árbol de objetos.

Nota:

El comportamiento de la herencia de valores de propiedad no está habilitado globalmente para todas las propiedades de dependencia, porque el tiempo de cálculo de la herencia afecta de algún modo al rendimiento. La herencia de valores de propiedad suele habilitarse normalmente solo para las propiedades donde un escenario determinado sugiere que dicha herencia es adecuada. Para determinar si una propiedad de dependencia se hereda, puede consultar la sección de información sobre las propiedades de dependencia correspondiente a esa propiedad de dependencia en la referencia del SDK.

En el ejemplo siguiente se muestra un enlace y se establece la propiedad DataContext que especifica el origen del enlace, que no se mostró en el ejemplo del enlace anterior. No es necesario que los enlaces siguientes en los objetos secundarios especifiquen el origen; pueden usar el valor heredado de DataContext en el objeto StackPanel primario. (Como alternativa, un objeto secundario podría especificar directamente su propio DataContext o un Source en Binding, y no usar deliberadamente el valor heredado para el contexto de datos de sus enlaces).

<StackPanel Canvas.Top="50" DataContext="{Binding Source={StaticResource XmlTeamsSource}}">
  <Button Content="{Binding XPath=Team/@TeamName}"/>
</StackPanel>

Para más información, vea Herencia de valores de propiedad.

Integración de WPF Designer

Un control personalizado con propiedades que se implementan como propiedades de dependencia recibirá soporte técnico adecuado de WPF Designer for Visual Studio. Un ejemplo es la capacidad de editar las propiedades de dependencia directas y adjuntas con la ventana Propiedades. Para obtener más información, consulte Información general sobre la creación de controles.

Prioridad de los valores de propiedades de dependencia

Cuando se obtiene el valor de una propiedad de dependencia, potencialmente se está obteniendo un valor establecido en esa propiedad mediante cualquiera de las otras entradas basadas en propiedades que se incluyen en el sistema de propiedades de WPF. La precedencia de los valores de propiedad de dependencia existe para que distintos escenarios sobre cómo las propiedades obtienen sus valores puedan interactuar de forma predecible.

Considere el ejemplo siguiente. El ejemplo incluye un estilo que se aplica a todos los botones y sus propiedades Background, pero después también especifica un botón con un valor Background establecido localmente.

Nota:

La documentación del SDK usa los términos "valor local" o "valor establecido localmente" ocasionalmente al hablar de las propiedades de dependencia. Un valor establecido localmente es un valor de propiedad que se establece directamente en una instancia de objeto en el código, o bien como un atributo en un elemento en XAML.

En principio, para el primer botón, la propiedad se establece dos veces, pero se aplica solo un valor: el valor con la precedencia más alta. Un valor establecido localmente tiene la precedencia más alta (excepto para una animación en ejecución, pero ninguna animación es aplicable en este ejemplo) y, por tanto, el valor establecido localmente se usa en lugar del valor del establecedor de estilo para el fondo del primer botón. El segundo botón no tiene ningún valor local (ni ningún otro valor con una precedencia más alta que un establecedor de estilo) y, por tanto, el fondo de ese botón procede del establecedor de estilo.

<StackPanel>
  <StackPanel.Resources>
    <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
     <Setter Property="Background" Value="Red"/>
    </Style>
  </StackPanel.Resources>
  <Button Background="Green">I am NOT red!</Button>
  <Button>I am styled red</Button>
</StackPanel>

¿Por qué existe la precedencia de las propiedades de dependencia?

Por lo general, no le interesa que los estilos siempre apliquen y oculten siquiera un valor establecido localmente de un elemento individual (de lo contrario, sería difícil usar estilos o elementos en general). Por lo tanto, los valores que proceden de estilos funcionan con una precedencia más baja que un valor establecido localmente. Para obtener una lista más completa de las propiedades de dependencia y conocer la procedencia del valor efectivo de una propiedad de dependencia, consulte Prioridad de los valores de propiedades de dependencia.

Nota:

Existen varias propiedades definidas en elementos de WPF que no son propiedades de dependencia. En general, las propiedades se implementaban como propiedades de dependencia solo cuando era necesario admitir al menos uno de los escenarios que habilitaba el sistema de propiedades: enlace de datos, estilos, animación, compatibilidad con los valores predeterminados, herencia, propiedades adjuntas o invalidación.

Más información sobre las propiedades de dependencia

  • Una propiedad adjunta es un tipo de propiedad que admite una sintaxis especializada en XAML. A menudo, una propiedad adjunta no tiene una correspondencia 1:1 con una propiedad de Common Language Runtime (CLR) ni es necesariamente una propiedad de dependencia. El propósito típico de una propiedad adjunta es permitir que los elementos secundarios comuniquen valores de propiedad a un elemento primario, aunque el elemento primario y el elemento secundario no posean a la vez esa propiedad como parte de las listas de miembros de clase. Un escenario principal es habilitar elementos secundarios para que informen al elemento primario de cómo deben aparecer en la UI. Para obtener un ejemplo, vea Dock o Left. Para obtener información detallada, vea Información general sobre propiedades asociadas.

  • Los desarrolladores de componentes o de aplicaciones pueden querer crear su propia propiedad de dependencia para habilitar funcionalidades, tales como el enlace de datos o la compatibilidad con estilos, o para admitir la invalidación y la coerción de valores. Para obtener más información, consulte Propiedades de dependencia personalizadas.

  • Considere las propiedades de dependencia como propiedades públicas, accesibles o al menos reconocibles para cualquier llamador que tenga acceso a una instancia. Para obtener más información, consulte Seguridad de las propiedades de dependencia.

Vea también