Prioridad de los valores de propiedades de dependencia (WPF .NET)
El funcionamiento del sistema de Windows Presentation Foundation (WPF) afecta al valor de una propiedad de dependencia. En este artículo se explica cómo la precedencia de las distintas entradas basadas en propiedades dentro del sistema de propiedades de WPF determina el valor efectivo de una propiedad de dependencia.
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.
Sistema de propiedades de WPF
El sistema de propiedades de WPF usa una variedad de factores para determinar el valor de las propiedades de dependencia, como la validación de propiedades en tiempo real, el enlace en tiempo de ejecución y las notificaciones de cambio de propiedad para las propiedades relacionadas. Aunque el orden y la lógica usados para determinar los valores de propiedad de dependencia son complejos, aprenderlo puede ayudarle a evitar configuraciones de propiedad innecesarias y también a averiguar por qué un intento de establecer una propiedad de dependencia no da como resultado el valor esperado.
Propiedades de dependencia establecidas en varios lugares
En el ejemplo XAML siguiente se muestra cómo tres operaciones «set» diferentes en la propiedad del botón Background pueden influir en su valor.
<StackPanel>
<StackPanel.Resources>
<ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</StackPanel.Resources>
<Button Template="{StaticResource ButtonTemplate}" Background="Red">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Blue"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Yellow" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
Which color do you expect?
</Button>
</StackPanel>
En el ejemplo, la propiedad Background
se establece localmente en Red
. Sin embargo, el estilo implícito declarado en el ámbito del botón intenta establecer la propiedad Background
en Blue
. Y, cuando el mouse está sobre el botón, el desencadenador en el estilo implícito intenta establecer la propiedad Background
en Yellow
. Excepto por la coerción y la animación, un valor de propiedad establecido localmente tiene la prioridad más alta, por lo que el botón será rojo, incluso con el evento «mouseover». Sin embargo, si quita el valor establecido localmente del botón, recibirá su valor del estilo, Background
. Dentro de un estilo los desencadenadores tienen prioridad, por lo que el botón será amarillo con el evento «mouseover» y azul en caso contrario. En el ejemplo se reemplaza el valor predeterminado ControlTemplate del botón porque la plantilla predeterminada tiene un valor Background
para «mouseover» incluido en el código.
Lista de prioridades de las propiedades de dependencia
La lista siguiente es el orden de prioridad definitivo que usa el sistema de propiedades al asignar valores en tiempo de ejecución a las propiedades de dependencia. La precedencia más alta aparece primero.
Coerción del sistema de propiedades. Para obtener más información sobre la coerción, consulte Coerción y animaciones.
Animaciones activas o animaciones con un comportamiento en espera. Para tener un efecto práctico, un valor de animación debe tener prioridad sobre el valor base (no animado), incluso si el valor base se ha establecido localmente. Para obtener más información, consulte Coerción y animaciones.
Valores locales. Puede establecer un valor local a través de una propiedad de «contenedor», lo que equivale a establecer un atributo o elemento de propiedad en XAML o mediante una llamada a la API SetValue utilizando una propiedad de una instancia específica. Un valor local establecido a través de un enlace o recurso tendrá la misma prioridad que un valor que se establece directamente.
Valores de propiedad de plantilla TemplatedParent. Un elemento tiene un TemplatedParent si ha sido creado por una plantilla (ControlTemplate o DataTemplate). Para obtener más información, consulte TemplatedParent. Dentro de la plantilla especificada por el
TemplatedParent
el orden de prioridad es:Desencadenadores.
Conjuntos de propiedades, normalmente a través de atributos XAML.
Estilos implícitos. Solo se aplica a la propiedad Style. El valor
Style
es cualquier recurso de estilo con un valor TargetType que coincide con el tipo de elemento. El recurso de estilo debe existir dentro de la página o aplicación. La búsqueda de un recurso de estilo implícito no se extiende a los recursos de estilos de Temas.Desencadenadores de estilo. Un desencadenador de estilo es un desencadenador dentro de un estilo explícito o implícito. El estilo debe existir dentro de la página o aplicación. Los desencadenadores de los estilos predeterminados tienen una prioridad menor.
Desencadenadores de plantilla. Un desencadenador de plantilla es un desencadenador de una plantilla aplicada directamente o de una plantilla dentro de un estilo. El estilo debe existir dentro de la página o aplicación.
Valores de establecedores de estilo. Un valor de establecedor de estilo es un valor aplicado por un Setter dentro de un estilo. El estilo debe existir dentro de la página o aplicación.
Estilos predeterminados, también conocidos como estilos de tema. Para obtener más información, consulte Estilos (de tema) predeterminados. Dentro de un estilo predeterminado el orden de prioridad es:
Desencadenadores activos.
Establecedores.
Herencia. Algunas propiedades de dependencia de un elemento secundario heredan su valor del elemento primario. Por lo tanto, puede que no sea necesario establecer valores de propiedad en todos los elementos de la aplicación. Para obtener más información, consulte Herencia de valores de propiedad.
Valor predeterminado de los metadatos de la propiedad de dependencia: una propiedad de dependencia puede tener un valor predeterminado establecido durante el registro del sistema de propiedades de esa propiedad. Las clases derivadas que heredan una propiedad de dependencia pueden invalidar los metadatos (incluido el valor predeterminado) de propiedad de dependencia por tipo. Para obtener más información, consulte Metadatos de propiedad de dependencia. Para una propiedad heredada, el valor predeterminado de un elemento primario tiene prioridad sobre el valor predeterminado de un elemento secundario. Por lo tanto, si no se establece una propiedad heredable, se usa el valor predeterminado de la raíz o del elemento primario en lugar del valor predeterminado del elemento secundario.
TemplatedParent
TemplatedParent la precedencia no se aplica a las propiedades de los elementos que se declaran directamente en el marcado de aplicación estándar. El concepto TemplatedParent
solo existe para los elementos secundarios dentro de un árbol visual que existen a través de la aplicación de una plantilla. Cuando el sistema de propiedades busca en la plantilla especificada por el TemplatedParent
para los valores de propiedad de un elemento, busca en la plantilla que ha creado al elemento. Los valores de propiedad de la plantilla TemplatedParent
suelen actuar como si fueran valores establecidos localmente en el elemento, pero con menos prioridad que los valores locales reales porque las plantillas, potencialmente, se comparten. Para obtener más información, vea TemplatedParent.
La propiedad de estilo
El mismo orden de prioridad se aplica a todas las propiedades de dependencia, excepto a la propiedad Style. La propiedad Style
es única en cuanto que no puede darse estilo a sí misma. No se recomienda la coerción o animación de la propiedad Style
(y animar la propiedad Style
requeriría una clase de animación personalizada). Como resultado, no se aplican todos los elementos de precedencia. Solo hay tres maneras de establecer la propiedad Style
:
Estilo explícito. La propiedad
Style
de un elemento se establece directamente. El valor de la propiedadStyle
actúa como si fuera un valor local y tiene la misma prioridad que el elemento 3 de la lista de prioridad. En la mayoría de los escenarios los estilos explícitos no se definen en línea y, en su lugar, se hace referencia a ellos explícitamente como un recurso, por ejemplo,Style="{StaticResource myResourceKey}"
.Estilo implícito. La propiedad
Style
de un elemento no se establece directamente. En su lugar, se aplica un estilo cuando existe en algún nivel dentro de la página o aplicación y tiene una clave de recurso que coincide con el tipo de elemento al que se aplica el estilo, por ejemplo,<Style TargetType="x:Type Button">
. El tipo debe coincidir exactamente, por ejemplo,<Style TargetType="x:Type Button">
no se aplicará al tipoMyButton
aunqueMyButton
se derive deButton
. El valor de propiedadStyle
tiene la misma prioridad que el elemento 5 de la lista de prioridad. Se puede detectar un valor de estilo implícito llamando al método DependencyPropertyHelper.GetValueSource, pasando la propiedadStyle
y comprobandoImplicitStyleReference
en los resultados.Estilo predeterminado, también conocido como estilo del tema. La propiedad
Style
de un elemento no se establece directamente. En su lugar, procede de la evaluación de temas en tiempo de ejecución por parte del motor de presentación de WPF. Antes del tiempo de ejecución el valor de la propiedadStyle
esnull
. El valor de propiedadStyle
tiene la misma prioridad que el elemento 9 de la lista de prioridad.
Estilos (de tema) predeterminados
Todos los controles que se suministran con WPF tienen un estilo predeterminado que puede variar según el tema, que es la razón por la que el estilo predeterminado a veces se conoce como estilo de tema.
El ControlTemplate es un elemento importante dentro del estilo predeterminado para un control. ControlTemplate
es un valor de establecedor para la propiedad del estilo Template. Si no hubiera ninguna plantilla en los estilos predeterminados, un control sin una plantilla personalizada como parte de un estilo personalizado no tendría ninguna apariencia visual. Una plantilla no solo define la apariencia visual de un control, sino que también define las conexiones entre las propiedades del árbol visual de la plantilla y la clase de control correspondiente. Cada control expone un conjunto de propiedades que puede influir en la apariencia visual del control sin reemplazar la plantilla. Por ejemplo, considere la apariencia visual predeterminada de un control Thumb, que es un componente ScrollBar.
Un control Thumb tiene ciertas propiedades personalizables. La plantilla predeterminada de un control Thumb
crea una estructura básica, o árbol visual, con varios componentes Border anidados para crear un aspecto biselado. Dentro de la plantilla las propiedades diseñadas para ser personalizables por la clase Thumb
se exponen a través de TemplateBinding. La plantilla predeterminada para el control Thumb
tiene varias propiedades de borde que comparten un enlace de plantilla con propiedades como Background o BorderThickness. Pero donde los valores de propiedades o arreglos visuales están codificados de forma rígida en la plantilla, o están ligados a valores que proceden directamente del tema, solo se pueden cambiar esos valores reemplazando toda la plantilla. Por lo general, si una propiedad procede de un elemento primario con plantilla y no lo expone un elemento TemplateBinding
, el valor de la propiedad no se puede cambiar mediante estilos porque no hay ninguna manera conveniente de establecerla como destino. No obstante, aún se podría influir en esa propiedad mediante la herencia de valores de propiedad de la plantilla aplicada o por un valor predeterminado.
Los estilos predeterminados especifican un TargetType en sus definiciones. La evaluación del tema en tiempo de ejecución asocia el TargetType
de un estilo predeterminado con la propiedad DefaultStyleKey de un control. En cambio, el comportamiento de búsqueda de estilos implícitos usa el tipo real del control. El valor de DefaultStyleKey
lo heredan las clases derivadas, por lo que los elementos derivados que de otro modo podrían no tener ningún estilo asociado obtengan una apariencia visual predeterminada. Por ejemplo, si deriva MyButton
de Button, MyButton
heredará la plantilla predeterminada de Button
. Las clases derivadas pueden invalidar el valor predeterminado de DefaultStyleKey
en los metadatos de la propiedad de dependencia. Por lo tanto, si desea una representación visual diferente para MyButton
, puede invalidar los metadatos de la propiedad de dependencia para DefaultStyleKey
en MyButton
y, a continuación, definir el estilo predeterminado pertinente, incluida una plantilla, que empaquetará con el control MyButton
. Para obtener más información, consulte Información general sobre la creación de controles.
Recurso dinámico
Recurso dinámico: las operaciones de enlace y referencias de recursos dinámicos tiene la precedencia de la ubicación en la que están establecidas. Por ejemplo, un recurso dinámico aplicado a un valor local tiene la misma prioridad que el elemento 3 de la lista de precedencia. Otro ejemplo es que un enlace de recursos dinámicos aplicado a un establecedor de propiedades dentro de un estilo predeterminado tiene la misma prioridad que el elemento 9 de la lista de precedencia. Dado que las referencias y el enlace de recursos dinámicos deben obtener valores del estado de tiempo de ejecución de la aplicación, el proceso para determinar la prioridad del valor de propiedad para cualquier propiedad determinada se extiende al tiempo de ejecución.
Las referencias de recursos dinámicos técnicamente no forman parte del sistema de propiedades y tienen su propio orden de búsqueda que interactúa con la lista de precedencia. Básicamente, la prioridad de las referencias de recursos dinámicos es: elemento a raíz de página, aplicación, tema y, a continuación, sistema. Para obtener más información, consulte Recursos XAML.
Aunque las referencias y enlaces de recursos dinámicos tienen la prioridad de la ubicación en la que se establecen, el valor se aplaza. Una consecuencia de esto es que, si establece un recurso dinámico o un enlace en un valor local, cualquier cambio en el valor local reemplazará completamente el recurso dinámico o el enlace. Aunque llame al método ClearValue para borrar el valor establecido localmente, el recurso dinámico o el enlace no se restaurarán. De hecho, si llama a ClearValue
en una propiedad que tiene un recurso dinámico o enlace (sin ningún valor local literal), se borrará el recurso dinámico o el enlace.
SetCurrentValue
El método SetCurrentValue es otra manera de establecer una propiedad, pero no está en la lista de prioridad. SetCurrentValue
permite cambiar el valor de una propiedad sin sobrescribir el origen de un valor anterior. Por ejemplo, si un desencadenador establece una propiedad y, a continuación, asigna otro valor mediante SetCurrentValue
, la acción del siguiente desencadenador volverá a establecer la propiedad en el valor del desencadenador. Puede usar SetCurrentValue
siempre que desee establecer un valor de propiedad sin dar a ese valor el nivel de precedencia de un valor local. De forma similar, puede usar SetCurrentValue
para cambiar el valor de una propiedad sin sobrescribir un enlace.
Coerción y animación
Tanto la coerción como la animación actúan en un valor base. El valor base es el valor de la propiedad de dependencia con la prioridad más alta, determinado evaluando hacia arriba a través de la lista de precedencia hasta que se alcanza el elemento 2.
Si una animación no especifica los valores de propiedad From y To para determinados comportamientos, o si la animación revierte deliberadamente al valor base cuando se completa, el valor base puede afectar al valor animado. Para verlo en la práctica, ejecute la aplicación de ejemplo valores de destino. En el ejemplo, para la altura del rectángulo, pruebe a establecer valores locales iniciales que difieran de cualquier valor de From
. Las animaciones de ejemplo comienzan a usar el valor From
inmediatamente, en lugar del valor base. Al especificar Stop como el FillBehavior, al finalizar una animación se restablecerá un valor de propiedad a su valor base. La prioridad normal se usa para la determinación del valor base después de que finalice una animación.
Se pueden aplicar varias animaciones a una sola propiedad y que cada animación tenga una prioridad diferente. En lugar de aplicar la animación con la prioridad más alta, el motor de presentación de WPF podría componer los valores de animación en función de cómo se han definido las animaciones y el tipo de valores animados. Para obtener más información, vea Información general sobre animaciones.
La coerción está en la parte superior de la lista de precedencia. Incluso una animación que ya se está ejecutando está sujeta a la coerción de valores. Ciertas propiedades de dependencia existentes en WPF tienen coerción integrada. Para las propiedades de dependencia personalizadas, puede definir el comportamiento de coerción escribiendo un objeto CoerceValueCallback que se pasa como parte de los metadatos al crear una propiedad. También puede invalidar el comportamiento de la coerción de las propiedades existentes mediante la invalidación de los metadatos de esa propiedad en una clase derivada. La coerción interactúa con el valor base de forma que se aplican las restricciones en la coerción, ya que esas restricciones existen en el momento, pero el valor base se conserva. Por lo tanto, si las restricciones en la coerción se levantan posteriormente, la coerción devolverá el valor más próximo posible a ese valor base y la influencia de la coerción sobre una propiedad cesará potencialmente en cuanto se levanten todas las restricciones. Para obtener más información sobre el comportamiento de la coerción, consulte Devoluciones de llamada y validación de las propiedades de dependencia.
Comportamientos de los desencadenadores
Los controles suelen definir los comportamientos de los desencadenadores como parte de su estilo predeterminado. Establecer propiedades locales en controles podría entrar en conflicto con esos desencadenadores, lo que impediría que los desencadenadores respondan (ya sea visualmente o con su comportamiento) a eventos controlados por el usuario. Un uso común de un desencadenador de propiedad es controlar las propiedades de estado, como IsSelected o IsEnabled. Por ejemplo, de forma predeterminada, cuando se deshabilita un Button, un desencadenador de estilo de tema (IsEnabled
es false
) establece el valor Foreground para que el Button
aparezca atenuado. Si ha establecido un valor local Foreground
, el valor de propiedad local de precedencia superior anulará el valor Foreground
del estilo de tema, incluso cuando Button
esté deshabilitado. Al establecer valores de propiedad que invalidan los comportamientos de desencadenador de nivel de tema para un control, tenga cuidado de no interferir indebidamente con la experiencia de usuario prevista para ese control.
ClearValue
El método ClearValue borra cualquier valor aplicado localmente de una propiedad de dependencia para un elemento. Sin embargo, llamar a ClearValue
no garantiza que el valor predeterminado establecido en los metadatos durante el registro de propiedades sea el nuevo valor efectivo. Todos los demás participantes de la lista de precedencia siguen activos y solo se quita el valor establecido localmente. Por ejemplo, si llama a ClearValue
en una propiedad que tiene un estilo de tema, el valor de estilo de tema se aplicará como el nuevo valor, en lugar del valor predeterminado basado en metadatos. Si desea establecer un valor de propiedad en el valor predeterminado de metadatos registrados, obtenga el valor de metadatos predeterminado consultando los metadatos de la propiedad de dependencia y establezca localmente el valor de propiedad con una llamada a SetValue.
Vea también
.NET Desktop feedback