Precedência do valor da propriedade da dependência

Este tópico explica como o funcionamento do sistema de propriedades do Windows Presentation Foundation (WPF) pode afetar o valor de uma propriedade de dependência e descreve a precedência pela qual os aspectos do sistema de propriedades se aplicam ao valor efetivo de uma propriedade.

Pré-requisitos

Este tópico pressupõe que você entenda as propriedades de dependência da perspectiva de um consumidor de propriedades de dependência existentes em classes WPF e tenha lido Visão geral das propriedades de dependência. Para seguir os exemplos neste tópico, você também deve entender XAML e saber como escrever aplicativos WPF.

O sistema de propriedades do WPF

O sistema de propriedades do WPF oferece uma maneira poderosa de fazer com que o valor das propriedades de dependência seja determinado por uma variedade de fatores, habilitando recursos como validação de propriedade em tempo real, associação tardia e notificação de propriedades relacionadas de alterações em valores para outras propriedades. A ordem exata e a lógica que é usada para determinar os valores das propriedades de dependência são razoavelmente complexas. Conhecer essa ordem ajudará você a evitar a configuração desnecessária de propriedades e pode também esclarecer dúvidas sobre o motivo exato pelo qual uma tentativa de influenciar ou antecipar um valor da propriedade de dependência não resultou no valor esperado.

Propriedades de dependência podem ser “definidas” em vários locais

Veja a seguir um exemplo de XAML em que a mesma propriedade (Background) tem três operações "set" diferentes que podem influenciar o 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>

Aqui, qual cor você espera que seja aplicada – vermelho, verde ou azul?

Com exceção dos valores animados e da coerção, os conjuntos de propriedades locais são definidos com a precedência mais alta. Se você definir um valor localmente, poderá esperar que ele seja respeitado, até mesmo acima de todos os estilos ou modelos de controle. Aqui no exemplo, Background é definido como Vermelho localmente. Portanto, o estilo definido nesse escopo, mesmo que seja um estilo implícito que, de outra forma, se aplicaria a todos os elementos desse tipo nesse escopo, não é a precedência mais alta para dar à Background propriedade seu valor. Se você removeu o valor Vermelho local da instância Botão, o estilo terá precedência e o botão obterá o valor Tela de Fundo do estilo. No estilo, os gatilhos têm precedência e, portanto, o botão será azul se o mouse estiver sobre ele; caso contrário, será verde.

Lista de precedências de configuração das propriedades de dependência

Veja a seguir a ordem definitiva que o sistema de propriedades usa ao atribuir os valores de tempo de execução das propriedades de dependência. A precedência mais alta é listada primeiro. Essa lista se expande em algumas das generalizações feitas na Visão geral das propriedades de dependência.

  1. Coerção do sistema de propriedades. Para obter detalhes sobre coerção, consulte Coerção, animação e valor base mais adiante neste tópico.

  2. Animações ativas ou animações com um comportamento Em Espera. Para obter um efeito prático, uma animação de uma propriedade deve conseguir ter precedência sobre o valor base (não animado), mesmo que esse valor tenha sido definido localmente. Para obter detalhes, consulte Coerção, animação e valor base mais adiante neste tópico.

  3. Valor local. Um valor local pode ser definido por meio da conveniência da propriedade "wrapper", que também equivale à configuração como um atributo ou elemento de propriedade em XAML, ou por uma chamada para a SetValue API usando uma propriedade de uma instância específica. Se você definir um valor local usando uma associação ou um recurso, cada um deles atuará na precedência como se um valor direto fosse definido.

  4. Propriedades de modelo TemplatedParent. Um elemento tem um TemplatedParent if foi criado como parte de um modelo (a ControlTemplate ou DataTemplate). Para obter detalhes sobre quando isso se aplica, consulte TemplatedParent mais adiante neste tópico. No modelo, a seguinte precedência se aplica:

    1. Gatilhos do TemplatedParent modelo.

    2. Conjuntos de propriedades (normalmente por meio de atributos XAML) no TemplatedParent modelo.

  5. Estilo implícito. Aplicável somente à propriedade Style. A propriedade Style é preenchida por qualquer recurso de estilo com uma chave que corresponde ao tipo desse elemento. Esse recurso de estilo deve existir na página ou no aplicativo; a pesquisa de um recurso de estilo implícito não continua nos temas.

  6. Gatilhos de estilo. Os gatilhos em estilos da página ou do aplicativo (esses estilos podem ser estilos explícitos ou implícitos, mas não baseados em estilos padrão, que têm precedência mais baixa).

  7. Gatilhos de modelo. Qualquer gatilho de um modelo em um estilo ou um modelo aplicado diretamente.

  8. Setters de estilo. Valores de um Setter dentro dos estilos da página ou aplicativo.

  9. Estilo (tema) padrão. Para obter detalhes sobre quando isso se aplica e como os estilos de tema se relacionam com os modelos nos estilos de tema, consulte Estilos (tema) padrão mais adiante neste tópico. Em um estilo padrão, a seguinte ordem de precedência se aplica:

    1. Gatilhos ativos no estilo de tema.

    2. Setters no estilo de tema.

  10. Herança. Algumas propriedades de dependência herdam seus valores do elemento pai para os elementos filho, de modo que elas não precisam ser definidas especificamente em cada elemento em todo o aplicativo. Para obter detalhes, consulte Herança do valor da propriedade.

  11. Valor padrão dos metadados de propriedades de dependência. Uma propriedade de dependência específica pode ter um valor padrão, conforme estabelecido pelo registro do sistema de propriedades dessa propriedade específica. Além disso, as classes derivadas que herdam uma propriedade de dependência têm a opção de substituir esses metadados (incluindo o valor padrão) por tipo. Consulte Metadados de propriedades de dependência para obter mais informações. Como a herança é verificada antes do valor padrão em uma propriedade herdada, o valor padrão de um elemento pai tem precedência sobre um elemento filho. Consequentemente, se uma propriedade herdável não for definida em nenhum lugar, o valor padrão especificado na raiz ou no pai será usado em vez do valor padrão do elemento filho.

TemplatedParent

TemplatedParent como um item de precedência não se aplica a nenhuma propriedade de um elemento declarado diretamente na marcação de aplicativo padrão. O conceito de TemplatedParent existe somente para itens filho em uma árvore visual que passa a existir por meio da aplicação do modelo. Quando o sistema de propriedades pesquisa o TemplatedParent modelo por um valor, ele está pesquisando o modelo que criou esse elemento. Os valores de propriedade do TemplatedParent modelo geralmente agem como se tivessem sido definidos como um valor local no elemento filho, mas essa precedência menor em relação ao valor local existe porque os modelos são potencialmente compartilhados. Para saber detalhes, veja TemplatedParent.

A propriedade de estilo

A ordem de pesquisa descrita anteriormente se aplica a todas as propriedades de dependência possíveis, exceto uma: a Style propriedade. A Style propriedade é exclusiva, pois não pode ser estilizada, portanto, os itens de precedência de 5 a 8 não se aplicam. Além disso, a animação ou a coerção Style não são recomendadas (e a Style animação exigiria uma classe de animação personalizada). Isso deixa três maneiras pelas quais a Style propriedade pode ser definida:

  • Estilo explícito. A Style propriedade é definida diretamente. Na maioria dos cenários, o estilo não é definido embutido, mas referenciado como um recurso, por chave explícita. Nesse caso, a própria propriedade Estilo atua como se fosse um valor local, conforme o item de precedência 3.

  • Estilo implícito. A Style propriedade não é definida diretamente. No entanto, o Style existe em algum nível na sequência de pesquisa de recursos (página, aplicativo) e é chaveado usando uma chave de recurso que corresponde ao tipo ao qual o estilo deve ser aplicado. Nesse caso, a Style própria propriedade atua por uma precedência identificada na sequência como item 5. Essa condição pode ser detectada usando DependencyPropertyHelper contra a Style propriedade e procurando nos ImplicitStyleReference resultados.

  • Estilo padrão, também conhecido como estilo de tema. A Style propriedade não é definida diretamente e, na verdade, será lida como null até o tempo de execução. Nesse caso, o estilo vem da avaliação de tema em tempo de execução que faz parte do mecanismo de apresentação do WPF.

Para estilos implícitos que não estão em temas, o tipo deve corresponder exatamente - uma MyButton Buttonclasse derivada não usará implicitamente um estilo para Button.

Estilos (tema) padrão

Cada controle fornecido com o WPF tem um estilo padrão. Potencialmente, esse estilo padrão varia por tema, razão pela qual ele é, às vezes, conhecido como um estilo de tema.

A informação mais importante encontrada em um estilo padrão para um controle é seu modelo de controle, que existe no estilo de tema como um setter para sua Template propriedade. Se não houvesse nenhum modelo dos estilos padrão, um controle sem um modelo personalizado como parte de um estilo personalizado não teria nenhuma aparência visual. O modelo do estilo padrão fornece à aparência visual de cada controle uma estrutura básica e também define as conexões entre as propriedades definidas na árvore visual do modelo e a classe de controle correspondente. Cada controle expõe um conjunto de propriedades que pode influenciar a aparência visual do controle sem substituir o modelo por completo. Por exemplo, considere a aparência visual padrão de um Thumb controle, que é um componente de um ScrollBar.

A Thumb tem certas propriedades personalizáveis. O modelo padrão de um Thumb cria uma estrutura básica / árvore visual com vários componentes aninhados Border para criar uma aparência de chanfro. Se uma propriedade que faz parte do modelo se destina a ser exposta para personalização pela Thumb classe, essa propriedade deve ser exposta por um TemplateBinding, dentro do modelo. No caso de Thumb, várias propriedades dessas bordas compartilham uma associação de modelo a propriedades como Background ou BorderThickness. Porém, algumas outras propriedades ou disposições visuais são embutidas em código no modelo de controle ou são associadas a valores obtidos diretamente do tema e não podem ser alteradas sem que seja necessário substituir o modelo inteiro. Em geral, se uma propriedade é obtida de um pai modelo e não é exposta por uma associação de modelo, ela não pode ser ajustada por estilos, pois não há nenhuma maneira fácil de direcioná-la. Porém, essa propriedade ainda poderá ser influenciada pela herança do valor da propriedade no modelo aplicado ou pelo valor padrão.

Os estilos de tema usam um tipo como a chave em suas definições. No entanto, quando os temas são aplicados a uma determinada instância de elemento, a pesquisa de temas para esse tipo é executada verificando a DefaultStyleKey propriedade em um controle. Isso é diferente de usar o Tipo literal, como os estilos implícitos usam. O valor de DefaultStyleKey herdaria para classes derivadas mesmo que o implementador não o alterasse (a maneira pretendida de alterar a propriedade não é substituí-la no nível da propriedade, mas alterar seu valor padrão nos metadados da propriedade). Essa indireção permite que as classes base definam os estilos de tema para elementos derivados que, de outro modo, não têm um estilo (ou mais importante, não tem um modelo dentro desse estilo e, portanto, não têm nenhuma aparência visual padrão). Assim, você pode derivar MyButton e Button ainda obterá o Button modelo padrão. Se você fosse o autor do controle e quisesse um comportamento diferente, poderia substituir os metadados da propriedade de dependência para DefaultStyleKey on para retornar uma chave diferente e, em seguida, definir os estilos de MyButton tema relevantes, incluindo o modelo para MyButton o qual você deve empacotar com seu MyButton MyButton controle. Para obter mais detalhes sobre temas, estilos e criação de controle, consulte Visão geral da criação de controle.

Referências a recursos dinâmicos e associação

Referências a recursos dinâmicos e operações de associação respeitam a precedência da localização na qual estão definidas. Por exemplo, um recurso dinâmico aplicado a um valor local atua de acordo com o item de precedência 3, uma associação de um setter de propriedade em um estilo de tema é aplicada no item de precedência 9 e assim por diante. Como as referências a recursos dinâmicos e a associação devem poder obter valores do estado de tempo de execução do aplicativo, isso implica que o processo real de determinação da precedência do valor da propriedade específica se estende para o tempo de execução também.

Estritamente falando, as referências a recursos dinâmicos não fazem parte do sistema de propriedades, mas têm uma própria ordem de pesquisa que interage com a sequência listada acima. Essa precedência é documentada mais detalhadamente em Recursos XAML. O resumo básico dessa precedência é: elemento na raiz da página, aplicativo, tema e sistema.

Os recursos dinâmicos e as associações têm a precedência da localização na qual foram definidas, mas o valor é adiado. Uma consequência disso é que, se você definir um recurso dinâmico ou uma associação com um valor local, uma alteração no valor local substituirá o recurso dinâmico ou a associação por completo. Mesmo se você chamar o ClearValue método para limpar o valor definido localmente, o recurso dinâmico ou a associação não será restaurado. Na verdade, se você chamar ClearValue uma propriedade que tenha um recurso dinâmico ou uma associação em vigor (sem valor local literal), eles também serão limpos pela ClearValue chamada.

SetCurrentValue

O SetCurrentValue método é outra maneira de definir uma propriedade, mas não está na ordem de precedência. Em vez disso, SetCurrentValue permite que você altere o valor de uma propriedade sem substituir a origem de um valor anterior. Você pode usar SetCurrentValue qualquer momento que desejar definir um valor sem dar a esse valor a precedência de um valor local. Por exemplo, se uma propriedade for definida por um gatilho e, em seguida, atribuída a outro valor por meio de SetCurrentValue, o sistema de propriedades ainda respeitará o gatilho e a propriedade será alterada se a ação do gatilho ocorrer. SetCurrentValue permite que você altere o valor da propriedade sem fornecer a ela uma fonte com uma precedência mais alta. Da mesma forma, você pode usar SetCurrentValue para alterar o valor de uma propriedade sem substituir uma associação.

Coerção, animações e valor base

A coerção e a animação atuam em um valor que é denominado como o "valor base" em todo este SDK. O valor base é, portanto, qualquer valor determinado por meio da avaliação ascendente de itens até chegar ao item 2.

Para uma animação, o valor base pode ter um efeito sobre o valor animado, caso essa animação não especifique “De” e “Para” para alguns comportamentos ou caso a animação seja revertida deliberadamente para o valor base quando concluída. Para ver isso na prática, execute a Amostra de valores de destino De, Para e Por da animação. Tente definir os valores locais da altura do retângulo no exemplo, para que o valor local inicial seja diferente de qualquer “De” na animação. Você observará que as animações são iniciadas imediatamente usando os valores de “De” e substituem o valor base depois de iniciadas. A animação pode especificar para retornar ao valor encontrado antes da animação depois que ela for concluída, especificando o Stop FillBehavior. Posteriormente, a precedência normal é usada para a determinação do valor base.

Várias animações podem ser aplicadas a uma única propriedade, em que cada uma dessas animações possivelmente foi definida em diferentes pontos na precedência de valor. No entanto, potencialmente, essas animações compõem seus valores, em vez de apenas aplicar a animação da precedência mais alta. Isso depende de como exatamente as animações são definidas e do tipo do valor que está sendo animado. Para obter mais informações sobre propriedades de animação, consulte Visão geral da animação.

A coerção é aplicada no nível mais alto de todos. Mesmo uma animação já em execução está sujeita à coerção de valor. Determinadas propriedades de dependência existentes no WPF têm coerção interna. Para uma propriedade de dependência personalizada, você define o comportamento de coerção para uma propriedade de dependência personalizada escrevendo um CoerceValueCallback e passando o retorno de chamada como parte dos metadados ao criar a propriedade. Também é possível substituir o comportamento de coerção das propriedades existentes substituindo os metadados dessa propriedade em uma classe derivada. A coerção interage com o valor base de tal forma que as restrições na coerção são aplicadas com as restrições existentes no momento, mas o valor base ainda é retido. Portanto, se as restrições na coerção forem retiradas posteriormente, a coerção retornará o valor mais próximo possível do valor base e, potencialmente, a influência de coerção em uma propriedade deixará de existir assim que todas as restrições forem retiradas. Para obter mais informações sobre o comportamento de coerção, consulte Retornos de chamada da propriedade de dependência e validação.

Comportamentos de gatilho

Em geral, os controles definem os comportamentos de gatilho como parte de seu estilo padrão em temas. A configuração de propriedades locais nos controles pode impedir que os gatilhos possam responder a eventos controlados pelo usuário de maneira visual ou comportamental. O uso mais comum de um gatilho de propriedade é para propriedades de controle ou estado, como IsSelected. Por exemplo, por padrão, quando a Button está desabilitado (gatilho para IsEnabled é false), o Foreground valor no estilo do tema é o que faz com que o controle apareça "esmaecido". Mas se você tiver definido um valor local Foreground , essa cor cinza normal será anulada em precedência pelo conjunto de propriedades locais, mesmo nesse cenário disparado por propriedade. Seja cuidadoso ao definir valores para propriedades que têm comportamentos de gatilho no nível de tema e garanta que você não está interferindo impropriamente na experiência do usuário pretendida para o controle.

ClearValue e precedência de valor

O ClearValue método fornece um meio conveniente para limpar qualquer valor aplicado localmente de uma propriedade de dependência definida em um elemento. No entanto, a chamada ClearValue não é uma garantia de que o padrão, conforme estabelecido nos metadados durante o registro da propriedade, seja o novo valor efetivo. Todos os outros participantes na precedência de valor ainda estão ativos. Somente o valor definido localmente foi removido da sequência de precedência. Por exemplo, se você chamar ClearValue uma propriedade em que essa propriedade também é definida por um estilo de tema, o valor do tema será aplicado como o novo valor em vez do padrão baseado em metadados. Se você quiser tirar todos os participantes do valor da propriedade do processo e definir o valor como o padrão de metadados registrados, poderá obter esse valor padrão definitivamente consultando os metadados da propriedade de dependência e, em seguida, poderá usar o valor padrão para definir localmente a propriedade com uma chamada para SetValue.

Confira também