Prioridad de los valores de propiedades de dependencia

En este tema se explica cómo puede afectar el trabajo del sistema de propiedades de Windows Presentation Foundation (WPF) al valor de una propiedad de dependencia y se describe la precedencia de aplicación de los aspectos del sistema de propiedades al valor efectivo de una propiedad.

Requisitos previos

En este tema, se supone que entiende las propiedades de dependencia desde la perspectiva de un consumidor de propiedades de dependencia existentes en las clases de WPF y que ha leído Información general sobre las propiedades de dependencia. Para seguir los ejemplos de este tema, también debe comprender el lenguaje XAML y saber cómo escribir aplicaciones de WPF.

Sistema de propiedades de WPF

El sistema de propiedades de WPF permite que el valor de las propiedades de dependencia se determine de forma eficaz mediante una serie de factores, que habilita características como la validación de propiedades en tiempo real, el enlace en tiempo de ejecución y la notificación de propiedades relacionadas de cambios a valores de otras propiedades. El orden y la lógica exactos que se usan para determinar los valores de las propiedades de dependencia son bastante complejos. Conocer este orden le evitará establecer propiedades innecesarias y puede eliminar la confusión sobre el motivo por el que un intento de influir en el valor de una propiedad de dependencia o de anticiparlo no dio como resultado el valor esperado.

Las propiedades de dependencia podrían ser "set" en varios lugares

El siguiente es un ejemplo de XAML donde la misma propiedad (Background) tiene tres operaciones "set" diferentes que podrían influir en el 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>

¿Qué color espera que se aplique aquí: rojo, verde o azul?

A excepción de los valores animados y la coerción, los conjuntos de propiedades locales se establecen con la precedencia más alta. Si establece un valor localmente, puede esperar que este se respete, incluso por encima de cualquier estilo o plantilla de control. En el ejemplo, Background se establece en Red localmente. Por lo tanto, el estilo definido en este ámbito, aunque sea un estilo implícito que, de otro modo, se aplicaría a todos los elementos de ese tipo en ese ámbito, no es la máxima precedencia para dar a la propiedad Background su valor. En el caso de quitar el valor local de Red desde esa instancia de Button, el estilo tendría precedencia y el botón obtendría el valor de Background del estilo. Dentro del estilo, los desencadenadores tienen precedencia, por lo que el botón será azul si el mouse está encima de él y verde en caso contrario.

Lista de precedencia de configuración de propiedades de dependencia

El siguiente es el orden definitivo que usa el sistema de propiedades al asignar los valores de tiempo de ejecución de las propiedades de dependencia. La precedencia más alta aparece primero. Esta lista desarrolla algunas de las generalizaciones realizadas en la Información general sobre las propiedades de dependencia.

  1. Coerción del sistema de propiedades. Para obtener más información sobre la coerción, consulte Coerción, animación y valor base más adelante en este tema.

  2. Animaciones activas o animaciones con un comportamiento Hold. Para tener cualquier efecto práctico, una animación de una propiedad debe poder tener precedencia sobre el valor base (inanimado), aunque dicho valor se estableciera localmente. Para obtener más información, consulte Coerción, animación y valor base más adelante en este tema.

  3. Valor local. Un valor local se puede establecer mediante la práctica propiedad "wrapper", que también equivale a establecerlo como un atributo o un elemento de propiedad en XAML, o mediante una llamada a la API de SetValue mediante una propiedad de una instancia específica. Si establece un valor local mediante un enlace o un recurso, cada uno de estos actuará con la precedencia como si se hubiese establecido un valor directo.

  4. Propiedades de la plantilla TemplatedParent. Un elemento tiene un TemplatedParent si ha sido creado como parte de una plantilla (ControlTemplate o DataTemplate). Para obtener más información sobre cuándo se aplica el caso anterior, consulte TemplatedParent más adelante en este tema. En la plantilla, se aplica la siguiente precedencia:

    1. Desencadenadores de la plantilla TemplatedParent.

    2. Conjuntos de propiedades (normalmente mediante atributos XAML) en la plantilla TemplatedParent.

  5. Estilo implícito. Solo se aplica a la propiedad Style. La propiedad Style se rellena con cualquier recurso de estilo que tenga una clave que coincida con el tipo de ese elemento. Ese recurso de estilo debe existir en la página o la aplicación; la búsqueda de un recurso de estilo implícito no continúa en los temas.

  6. Desencadenadores de estilo. Desencadenadores dentro de los estilos de una página o aplicación (estos estilos pueden ser explícitos o implícitos, pero no pueden ser predeterminados, con una precedencia más baja).

  7. Desencadenadores de plantilla. Cualquier desencadenador de una plantilla dentro de un estilo o una plantilla aplicada directamente.

  8. Establecedores de estilo. Valores de un objeto Setter dentro de estilos de una página o aplicación.

  9. Estilo (tema) predeterminado. Para obtener más información sobre cuándo se aplica y cómo se relacionan los estilos de tema con las plantillas dentro de los estilos de tema, consulte Estilos (temas) predeterminados más adelante en este tema. Dentro de un estilo predeterminado, se aplica el orden de precedencia siguiente:

    1. Desencadenadores activos del estilo de tema.

    2. Establecedores del estilo de tema.

  10. Herencia. Algunas propiedades de dependencia heredan sus valores del elemento primario a los elementos secundarios, de manera que no se tienen que establecer específicamente en cada elemento de una aplicación. Para obtener información detallada, consulte Herencia de valores de propiedad.

  11. Valor predeterminado de los metadatos de las propiedades de dependencia. Cualquier propiedad de dependencia puede tener un valor predeterminado establecido en el registro del sistema de propiedades de esa propiedad concreta. Además, las clases derivadas que heredan una propiedad de dependencia tienen la opción de invalidar esos metadatos (incluido el valor predeterminado) por tipo. Para obtener más información, consulte Metadados de las propiedades de dependencia. Puesto que la herencia se comprueba antes que el valor predeterminado, para una propiedad heredada, un valor predeterminado de un elemento primario tiene precedencia sobre un elemento secundario. Por consiguiente, si una propiedad heredable no está establecida en ningún lugar, se usa el valor predeterminado tal como se especifica en la raíz o el elemento primario, en lugar del valor predeterminado del elemento secundario.

TemplatedParent

TemplatedParent, como un elemento de precedencia, no se aplica a ninguna propiedad de un elemento que se declara directamente en la marcación de aplicación estándar. El concepto de TemplatedParent existe solo para los elementos secundarios dentro de un árbol visual que se genera a través de la aplicación de la plantilla. Cuando el sistema de propiedades busca un valor en la plantilla TemplatedParent, realiza la búsqueda en la plantilla que creó ese elemento. Por lo general, los valores de propiedad de la plantilla TemplatedParent actúan como si se hubieran establecido como un valor local en el elemento secundario, pero esta precedencia menor en comparación con el valor local existe porque las plantillas se comparten potencialmente. Para obtener información detallada, vea TemplatedParent.

Propiedad Style

El orden de búsqueda descrito anteriormente se aplica a todas las propiedades de dependencia posibles excepto una: la propiedad Style. La propiedad Style es única en que no se puede aplicar estilos a sí misma, por lo que no se aplican los elementos de precedencia 5 a 8. Además, no se recomienda la animación o coerción de Style (y animar Style requeriría una clase de animación personalizada). Esto deja tres formas de establecer la propiedad Style:

  • Estilo explícito. La propiedad Style está establecida directamente. En la mayoría de los escenarios, el estilo no se define en línea, sino que se hace referencia a este como un recurso, por la clave explícita. En este caso, la propia propiedad Style actúa como si fuera un valor local, el elemento de precedencia 3.

  • Estilo implícito. La propiedad Style no está establecida directamente. No obstante, la propiedad Style existe en algún nivel de la secuencia de búsqueda de recursos (página o aplicación) y se codifica mediante una clave de recurso que coincide con el tipo al que se va a aplicar el estilo. En este caso, la propiedad Style actúa por sí sola con una precedencia identificada en la secuencia como el elemento 5. Esta condición se puede detectar mediante el uso de DependencyPropertyHelper en la propiedad Style y la búsqueda de ImplicitStyleReference en los resultados.

  • Estilo predeterminado, también conocido como estilo de tema. La propiedad Style no se establece directamente y, de hecho, se leerá como null hasta el tiempo de ejecución. En este caso, el estilo procede de la evaluación del tema en tiempo de ejecución que forma parte del motor de presentación de WPF.

Para los estilos implícitos que no procedan de temas, el tipo debe coincidir exactamente: una clase derivada de MyButtonButton no usará de manera implícita un estilo para Button.

Estilos (temas) predeterminados

Todos los controles que se suministran con WPF tienen un estilo predeterminado. Ese estilo predeterminado puede variar según el tema, motivo por el cual se denomina a veces "estilo de tema".

La información más importante que se encuentra dentro de un estilo predeterminado de un control es su plantilla de control, que existe en el estilo de tema como un establecedor de su propiedad 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. La plantilla del estilo predeterminado proporciona a la apariencia visual de cada control una estructura básica, además de definir las conexiones entre las propiedades definidas en el á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 completamente la plantilla. Por ejemplo, considere la apariencia visual predeterminada de un control Thumb, que es un componente de ScrollBar.

Un 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. Si una propiedad que forma parte de la plantilla se va a exponer para que la personalice la clase Thumb, debe exponerla un objeto TemplateBinding dentro de la plantilla. En el caso de Thumb, diversas propiedades de estos bordes comparten un enlace a plantilla de propiedades como Background o BorderThickness. No obstante, otras propiedades u organizaciones visuales están codificadas de forma rígida en la plantilla de control o enlazadas a valores que proceden directamente del tema, y no se pueden cambiar a poco de reemplazar la plantilla completa. Por lo general, si una propiedad procede de un elemento primario con plantilla y no se expone mediante un enlace a plantilla, no puede ajustarse mediante estilos porque no hay ninguna forma sencilla 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 el valor predeterminado.

Los estilos de tema usan un tipo como clave en sus definiciones. Sin embargo, cuando se aplican temas a una instancia de un elemento determinado, la búsqueda de temas de este tipo se realiza mediante la comprobación de la propiedad DefaultStyleKey en un control. Esto es lo contrario a usar el literal Type, como hacen los estilos implícitos. El valor de DefaultStyleKey se heredaría en las clases derivadas, aunque el implementador no lo cambiara (la forma prevista de cambiar la propiedad es no invalidarla en el nivel de propiedad, sino cambiar su valor predeterminado en los metadatos de propiedad). Este direccionamiento indirecto permite que las clases base definan los estilos de tema de elementos derivados que, de lo contrario, no tendrían ningún estilo (o más importante aún, no tendrían ninguna plantilla dentro de ese estilo y no tendrían, por tanto, ninguna apariencia visual predeterminada en absoluto). De este modo, puede derivar MyButton de Button y aún así obtendrá la plantilla predeterminada Button. Si fuera el autor del control de MyButton y quisiera un comportamiento diferente, podría invalidar los metadatos de la propiedad de dependencia de DefaultStyleKey en MyButton para devolver una clave distinta y, a continuación, definir los estilos de tema pertinentes, incluida la plantilla de MyButton que se debe empaquetar con el control MyButton. Para obtener más detalles sobre los temas, los estilos y la creación de controles, consulte Información general sobre la creación de controles.

Enlace y referencias de recursos dinámicos

Las operaciones de enlace y referencias de recursos dinámicos respetan la precedencia de la ubicación en la que están establecidas. Por ejemplo, un recurso dinámico aplicado a un valor local actúa por elemento de precedencia 3, un enlace para un establecedor de propiedad dentro de un estilo de tema se aplica al elemento de precedencia 9 y así sucesivamente. Dado que el enlace y las referencias de recursos dinámicos deben ser capaces de obtener los valores del estado de tiempo de ejecución de la aplicación, esto implica que el proceso real de determinar la precedencia de los valores de propiedad para cualquier propiedad determinada se extiende también al tiempo de ejecución.

Las referencias de recursos dinámicos no forman parte del sistema de propiedades en sentido estricto, pero tienen un orden de búsqueda propio que interactúa con la secuencia citada anteriormente. Esa precedencia se documenta con más detalle en Recursos XAML. El resumen básico de esa precedencia es: elemento a raíz de la página, aplicación, tema y sistema.

Los enlaces y los recursos dinámicos tienen la precedencia de donde se establecieron, pero 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á el recurso dinámico o el enlace completamente. 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 establecidos (sin ningún valor local literal), la llamada también borra ClearValue.

SetCurrentValue

El método SetCurrentValue es otra manera de establecer una propiedad, pero no está en el orden de prioridad. En su lugar, SetCurrentValue le permite cambiar el valor de una propiedad sin sobrescribir el origen de un valor anterior. Puede usar SetCurrentValue cuando quiera establecer un valor de propiedad sin dar a ese valor el nivel de prioridad de un valor local. Por ejemplo, si una propiedad se establece mediante un desencadenador y, a continuación, se le asigna otro valor mediante SetCurrentValue, el sistema de propiedades sigue respetando el desencadenador y la propiedad cambia si se produce la acción del desencadenador. SetCurrentValue permite cambiar el valor de la propiedad sin proporcionarle un origen con una precedencia más alta. De forma similar, puede usar SetCurrentValue para cambiar el valor de una propiedad sin sobrescribir un enlace.

Coerción, animaciones y valor base

Tanto la coerción como la animación actúan sobre un valor al que nos referimos como "valor base" en este SDK. El valor base es, por tanto, cualquier valor que se determine mediante la evaluación ascendente de los elementos hasta alcanzar el elemento 2.

Para una animación, el valor base puede tener un efecto en el valor animado si la animación no especifica los valores "From" y "To" para ciertos comportamientos o si se revierte deliberadamente al valor base al completarse. Para verlo en la práctica, ejecute el Ejemplo de valores de destino de animación From, To y By. Intente establecer los valores locales del alto del rectángulo del ejemplo, de modo que el valor local inicial difiera de cualquier valor "From" de la animación. Observará que las animaciones se inician inmediatamente mediante los valores "From" y reemplazan el valor base una vez iniciadas. La animación puede especificar que se vuelva al valor encontrado antes de la animación una vez completada mediante la especificación de Stop para la propiedad FillBehavior. Después, se usa la precedencia normal para la determinación del valor base.

Varias animaciones podrían aplicarse a una propiedad única y cada una de estas animaciones se podría haber definido desde diferentes puntos de la precedencia de valores. Sin embargo, estas animaciones compondrán potencialmente sus valores, en lugar de aplicar la animación de mayor precedencia simplemente. Esto depende de la exactitud con la que estén definidas las animaciones y del tipo de valor que se esté animando. Para más información sobre la animación de propiedades, vea Información general sobre animaciones.

La coerción se aplica en el nivel más alto de todos. 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 la coerción integrada. Para definir el comportamiento de la coerción de una propiedad de dependencia personalizada, se escribe un elemento CoerceValueCallback y se pasa la devolución de llamada como parte de los metadatos cuando se crea la 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 sigue conservando. 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 en los temas. Establecer las propiedades locales en los controles puede impedir que los desencadenadores sean capaces de responder a eventos controlados por el usuario, ya sea de manera visual o conductual. El uso más común de un desencadenador de propiedad es para propiedades de control o estado como IsSelected. Por ejemplo, de forma predeterminada, cuando se deshabilita un Button (el desencadenador para IsEnabled es false) el valor Foreground en el estilo de tema es lo que causa que el control aparezca atenuado. No obstante, si estableció un valor local de Foreground, el conjunto de propiedades local anulará la precedencia del color de atenuación normal, incluso en este escenario desencadenado por propiedades. Tenga precaución al establecer valores de propiedades con comportamientos de desencadenador de nivel de tema y asegúrese de no interferir excesivamente en la experiencia de usuario prevista para este control.

Precedencia de los valores y ClearValue

El método ClearValue proporciona medios adecuados para borrar cualquier valor aplicado localmente de una propiedad de dependencia establecida en 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 en la precedencia de valores siguen estando activos. Solo el valor establecido localmente se quitó de la secuencia de precedencia. Por ejemplo, si llama a ClearValue en una propiedad donde esa propiedad también está establecida mediante un estilo de tema, el valor de tema se aplica como el nuevo valor en lugar de como el valor predeterminado basado en metadatos. Si quiere quitar del proceso todos los participantes del valor de propiedad y establecer el valor predeterminado de los metadatos registrados, puede obtener ese valor predeterminado definitivamente mediante la consulta de los metadatos de la propiedad de dependencia. Posteriormente, puede usar el valor predeterminado para establecer localmente la propiedad con una llamada a SetValue.

Vea también