Precedência do valor da propriedade de dependência (WPF .NET)

O funcionamento do sistema de propriedades do Windows Presentation Foundation (WPF) afeta o valor de uma propriedade de dependência. Este artigo explica como a precedência de diferentes entradas baseadas em propriedade dentro do sistema de propriedades WPF determina o valor efetivo de uma propriedade de dependência.

Importante

A documentação do Guia da Área de Trabalho para .NET 7 e .NET 6 está em construção.

Pré-requisitos

O artigo pressupõe um conhecimento básico das propriedades de dependência e que você leu Visão geral das propriedades de dependência. Para seguir os exemplos neste artigo, é útil se você estiver familiarizado com XAML (Extensible Application Markup Language) e souber como escrever aplicativos WPF.

O sistema de propriedades WPF

O sistema de propriedades WPF usa uma variedade de fatores para determinar o valor das propriedades de dependência, como validação de propriedade em tempo real, vinculação tardia e notificações de alteração de propriedade para propriedades relacionadas. Embora a ordem e a lógica usadas para determinar valores de propriedade de dependência sejam complexas, aprendê-la pode ajudá-lo a evitar configurações de propriedade desnecessárias e também a descobrir por que uma tentativa de definir uma propriedade de dependência não resultou no valor esperado.

Propriedades de dependência definidas em vários locais

O exemplo XAML a seguir mostra como três operações "set" diferentes na propriedade do Background botão podem influenciar seu 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>

No exemplo, a Background propriedade é definida localmente como Red. No entanto, o estilo implícito declarado no escopo do botão, tenta definir a Background propriedade como Blue. E, quando o mouse está sobre o botão, o gatilho no estilo implícito tenta definir a Background propriedade como Yellow. Exceto para coerção e animação, um valor de propriedade definido localmente tem a precedência mais alta, portanto, o botão ficará vermelho — mesmo ao passar o mouse. Mas, se você remover o valor definido localmente do botão, ele obterá seu Background valor do estilo. Dentro de um estilo, os gatilhos têm precedência, de modo que o botão será amarelo ao passar o mouse e azul caso contrário. O exemplo substitui o padrão do botão porque o modelo padrão ControlTemplate tem um valor de mouseover Background embutido em código.

Lista de precedência de propriedade de dependência

A lista a seguir é a ordem de precedência definitiva que o sistema de propriedades usa ao atribuir valores de tempo de execução a propriedades de dependência. A precedência mais alta é listada primeiro.

  1. Coerção do sistema de propriedades. Para obter mais informações sobre coerção, consulte Coerção e animações.

  2. Animações ativas ou animações com um comportamento Em Espera. Para ter um efeito prático, um valor de animação deve ter precedência sobre o valor base (unanimated), mesmo que o valor base tenha sido definido localmente. Para obter mais informações, consulte Coerção e animações.

  3. Valores locais. Você pode definir um valor local por meio de uma propriedade "wrapper", que equivale a definir 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. Um valor local definido por meio de uma associação ou recurso terá a mesma precedência que um valor definido diretamente.

  4. Valores de propriedade do modelo TemplatedParent. Um elemento tem um se ele foi criado por um TemplatedParent modelo (ControlTemplate ou DataTemplate). Para obter mais informações, consulte TemplatedParent. Dentro do modelo especificado pelo TemplatedParent, a ordem de precedência é:

    1. Gatilhos.

    2. Conjuntos de propriedades, normalmente por meio de atributos XAML.

  5. Estilos implícitos. Aplicável somente à propriedade Style. O Style valor é qualquer recurso de estilo com um valor que corresponda ao tipo de TargetType elemento. O recurso de estilo deve existir dentro da página ou aplicativo. A pesquisa de um recurso de estilo implícito não se estende aos recursos de estilos em Temas.

  6. Gatilhos de estilo. Um gatilho de estilo é um gatilho dentro de um estilo explícito ou implícito. O estilo deve existir dentro da página ou aplicativo. Os gatilhos nos estilos padrão têm uma precedência menor.

  7. Gatilhos de modelo. Um gatilho de modelo é um gatilho de um modelo aplicado diretamente ou de um modelo dentro de um estilo. O estilo deve existir dentro da página ou aplicativo.

  8. Valores do setter de estilo. Um valor de setter de estilo é um valor aplicado por um dentro de um Setter estilo. O estilo deve existir dentro da página ou aplicativo.

  9. Estilos padrão, também conhecidos como estilos de tema. Para obter mais informações, consulte Estilos padrão (tema). Dentro de um estilo padrão, a ordem de precedência é:

    1. Gatilhos ativos.

    2. Setters.

  10. Herança. Algumas propriedades de dependência de um elemento filho herdam seu valor do elemento pai. Portanto, talvez não seja necessário definir valores de propriedade em cada elemento em todo o aplicativo. Para obter mais informações, consulte Herança de valor de propriedade.

  11. Valor padrão dos metadados da propriedade de dependência Uma propriedade de dependência pode ter um valor padrão definido durante o registro dessa propriedade no sistema de propriedades. As classes derivadas que herdam uma propriedade de dependência podem substituir os metadados da propriedade de dependência (incluindo o valor padrão) por tipo. Para obter mais informações, consulte Metadados da propriedade de dependência. Para uma propriedade herdada, o valor padrão de um elemento pai tem precedência sobre o valor padrão de um elemento filho. Portanto, se uma propriedade hereditária não estiver definida, o valor padrão da raiz ou pai será usado em vez do valor padrão do elemento filho.

TemplatedParent

TemplatedParent A precedência não se aplica às propriedades dos elementos que são declarados diretamente na marcação de aplicativo padrão. O TemplatedParent conceito existe apenas para itens filho dentro de uma árvore visual que surgem por meio da aplicação de um modelo. Quando o sistema de propriedades pesquisa o modelo especificado pelo TemplatedParent para os valores de propriedade de um elemento, ele está pesquisando o modelo que criou o elemento. Os valores de propriedade do modelo geralmente agem como se fossem valores definidos localmente no elemento, mas com menor precedência do TemplatedParent que os valores locais reais porque os modelos são potencialmente compartilhados. Para obter mais informações, consulte TemplatedParent.

A propriedade Style

A mesma ordem de precedência se aplica a todas as propriedades de dependência, exceto a Style propriedade. A Style propriedade é única, pois não pode ser estilizada. Coagir ou animar a propriedade não é recomendado (e animar a StyleStyle propriedade exigiria uma classe de animação personalizada). Como resultado, nem todos os itens de precedência se aplicam. Há apenas três maneiras de definir a Style propriedade:

  • Estilo explícito. A Style propriedade de um elemento é definida diretamente. O Style valor da propriedade age como se fosse um valor local e tem a mesma precedência que o item 3 na lista de precedência. Na maioria dos cenários, os estilos explícitos não são definidos embutidos e, em vez disso, são explicitamente referenciados como um recurso, por exemplo Style="{StaticResource myResourceKey}".

  • Estilo implícito. A Style propriedade de um elemento não é definida diretamente. Em vez disso, um estilo é aplicado quando existe em algum nível dentro da página ou aplicativo e tem uma chave de recurso que corresponde ao tipo de elemento ao qual o estilo se aplica, por exemplo <Style TargetType="x:Type Button">. O tipo deve corresponder exatamente, por exemplo <Style TargetType="x:Type Button"> , não será aplicado ao MyButton tipo mesmo se MyButton for derivado de Button. O Style valor da propriedade tem a mesma precedência que o item 5 na lista de precedência. Um valor de estilo implícito pode ser detectado chamando o DependencyPropertyHelper.GetValueSource método, passando a Style propriedade e verificando ImplicitStyleReference os resultados.

  • Estilo padrão, também conhecido como estilo de tema. A Style propriedade de um elemento não é definida diretamente. Em vez disso, ele vem da avaliação do tema de tempo de execução pelo mecanismo de apresentação do WPF. Antes do tempo de execução, o valor da Style propriedade é null. O Style valor da propriedade tem a mesma precedência que o item 9 na lista de precedência.

Estilos padrão (Tema)

Cada controle fornecido com o WPF tem um estilo padrão que pode variar de acordo com o tema, e é por isso que o estilo padrão às vezes é chamado de estilo de tema.

O ControlTemplate é um item importante dentro do estilo padrão para um controle. ControlTemplate é um valor setter para a propriedade do Template estilo. Se os estilos padrão não contivessem um modelo, um controle sem um modelo personalizado como parte de um estilo personalizado não teria aparência visual. Um modelo não apenas define a aparência visual de um controle, ele também define as conexões entre as propriedades na árvore visual do modelo e a classe de controle correspondente. Cada controle expõe um conjunto de propriedades que podem influenciar a aparência visual do controle sem substituir o modelo. Por exemplo, considere a aparência visual padrão de um controle, que é um ThumbScrollBar componente.

Um Thumb controle tem certas propriedades personalizáveis. O modelo padrão de um Thumb controle cria uma estrutura básica, ou árvore visual, com vários componentes aninhados Border para criar uma aparência chanfrada. Dentro do modelo, as propriedades que se destinam a ser personalizáveis pela classe são expostas por meio de ThumbTemplateBinding. O modelo padrão para o Thumb controle tem várias propriedades de borda que compartilham uma associação de modelo com propriedades como Background ou BorderThickness. Mas, quando os valores de propriedades ou arranjos visuais são codificados no modelo ou estão vinculados a valores que vêm diretamente do tema, você só pode alterar esses valores substituindo o modelo inteiro. Geralmente, se uma propriedade vem de um pai modelo e não é exposta por um TemplateBinding, o valor da propriedade não pode ser alterado por estilos porque não há uma maneira conveniente de direcioná-lo. Mas, essa propriedade ainda pode ser influenciada pela herança do valor da propriedade no modelo aplicado ou por um valor padrão.

Os estilos padrão especificam a TargetType em suas definições. A avaliação do tema de tempo de execução corresponde o TargetType de um estilo padrão à DefaultStyleKey propriedade de um controle. Por outro lado, o comportamento de pesquisa para estilos implícitos usa o tipo real do controle. O valor de DefaultStyleKey é herdado por classes derivadas, portanto, elementos derivados que, de outra forma, poderiam não ter nenhum estilo associado obtêm uma aparência visual padrão. Por exemplo, se você derivar MyButton de , MyButton herdará o modelo padrão de ButtonButton. As classes derivadas podem substituir o valor padrão dos metadados da DefaultStyleKey propriedade in dependency. Portanto, se você quiser uma representação visual diferente para o , poderá substituir os metadados da propriedade de dependência para MyButtonDefaultStyleKey on MyButtone, em seguida, definir o estilo padrão relevante, incluindo um modelo, que você empacotará com seu MyButton controle. Para obter mais informações, consulte Visão geral sobre a criação de controles.

Recurso dinâmico

As referências dinâmicas de recursos e as operações de vinculação têm a precedência do local em que estão definidas. Por exemplo, um recurso dinâmico aplicado a um valor local tem a mesma precedência que o item 3 na lista de precedência. Como outro exemplo, uma associação dinâmica de recursos aplicada a um setter de propriedades dentro de um estilo padrão tem a mesma precedência que o item 9 na lista de precedência. Como as referências de recursos dinâmicos e a vinculação devem obter valores do estado de tempo de execução do aplicativo, o processo para determinar a precedência do valor da propriedade para qualquer propriedade específica se estende até o tempo de execução.

As referências de recursos dinâmicos não fazem parte tecnicamente do sistema de propriedades e têm sua própria ordem de pesquisa que interage com a lista de precedência. Essencialmente, a precedência das referências de recursos dinâmicos é: elemento para raiz de página, aplicativo, tema e, em seguida, sistema. Para obter mais informações, consulte Recursos XAML.

Embora as referências e associações de recursos dinâmicos tenham a precedência do local em que são definidas, o valor é adiado. Uma consequência disso é que, se você definir um recurso dinâmico ou uma associação a um valor local, qualquer alteração no valor local substituirá totalmente o recurso dinâmico ou a associação. Mesmo se você chamar o método para limpar o valor definido localmente, o ClearValue recurso dinâmico ou a associação não serão restaurados. Na verdade, se você chamar ClearValue uma propriedade que tenha um recurso dinâmico ou uma vinculação (sem valor local literal), o recurso dinâmico ou a vinculação serão limpos.

SetCurrentValue

O SetCurrentValue método é outra maneira de definir uma propriedade, mas não está na lista de precedência. SetCurrentValue Permite alterar o valor de uma propriedade sem substituir a origem de um valor anterior. Por exemplo, se uma propriedade for definida por um gatilho e, em seguida, você atribuir outro valor usando SetCurrentValue, a próxima ação de gatilho definirá a propriedade de volta para o valor de disparador. Você pode usar SetCurrentValue sempre que quiser definir um valor de propriedade sem dar a esse valor o nível de precedência de um valor local. Da mesma forma, você pode usar SetCurrentValue para alterar o valor de uma propriedade sem substituir uma associação.

Coerção e animação

A coerção e a animação atuam sobre um valor base. O valor base é o valor da propriedade de dependência com a precedência mais alta, determinado pela avaliação para cima através da lista de precedências até que o item 2 seja alcançado.

Se uma animação não especificar os valores de propriedade From e To ambos para determinados comportamentos, ou se a animação reverter deliberadamente para o valor base quando concluída, o valor base poderá afetar o valor animado. Para ver isso na prática, execute o aplicativo de exemplo Valores de Destino . No exemplo, para a altura do retângulo, tente definir valores locais iniciais que sejam diferentes de qualquer From valor. As animações de exemplo começam imediatamente usando o From valor em vez do valor base. Ao especificar Stop como o , após a FillBehaviorconclusão, uma animação redefinirá um valor de propriedade para seu valor base. A precedência normal é usada para determinação do valor base após o término de uma animação.

Várias animações podem ser aplicadas a uma única propriedade, com cada animação tendo uma precedência diferente. Em vez de aplicar a animação com a precedência mais alta, o mecanismo de apresentação do WPF pode compor os valores de animação, dependendo de como as animações foram definidas e do tipo de valores animados. Para obter mais informações, confira Visão geral de animação.

A coerção está no topo da lista de precedência. Mesmo uma animação em execução está sujeita à coerção de valor. Algumas propriedades de dependência existentes no WPF têm coerção interna. Para propriedades de dependência personalizadas, você pode definir o comportamento de coerção escrevendo um CoerceValueCallback que você passa como parte dos metadados ao criar uma propriedade. Você também pode substituir o comportamento de coerção de 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 à coerção são aplicadas como existem no momento, mas o valor base ainda é mantido. Como resultado, se as restrições na coerção forem posteriormente levantadas, a coerção retornará o valor mais próximo possível do valor base e, potencialmente, a influência da coerção sobre uma propriedade cessará assim que todas as restrições forem suspensas. Para obter mais informações sobre o comportamento de coerção, consulte Retornos de chamada e validação da propriedade de dependência.

Comportamentos de gatilho

Os controles geralmente definem comportamentos de gatilho como parte de seu estilo padrão. A definição de propriedades locais em controles pode entrar em conflito com esses disparadores, impedindo que os gatilhos respondam (visual ou comportamentalmente) a eventos orientados pelo usuário. Um uso comum de um gatilho de propriedade é controlar propriedades de estado, como IsSelected ou IsEnabled. Por exemplo, por padrão, quando um é desabilitado, um gatilho de estilo de Button tema (IsEnabled is false) define o valor para fazer com que o ForegroundButton look apareça acinzentado. Se você tiver definido um valor local, o valor de propriedade local Foreground de precedência mais alto anulará o valor de estilo Foreground de tema, mesmo quando o Button estiver desabilitado. Ao definir valores de propriedade que substituem comportamentos de gatilho em nível de tema para um controle, tenha cuidado para não interferir indevidamente na experiência do usuário pretendida para esse controle.

ClearValue

O ClearValue método limpa qualquer valor aplicado localmente de uma propriedade de dependência para um elemento. Mas, a chamada ClearValue não garante que o valor padrão estabelecido nos metadados durante o registro do imóvel seja o novo valor efetivo. Todos os outros participantes na lista de precedência ainda estão ativos e somente o valor definido localmente é removido. Por exemplo, se você chamar ClearValue uma propriedade que tenha um estilo de tema, o valor de estilo de tema será aplicado como o novo valor, em vez do padrão baseado em metadados. Se você quiser definir um valor de propriedade para o valor padrão de metadados registrados, obtenha o valor de metadados padrão consultando os metadados da propriedade de dependência e defina localmente o valor da propriedade com uma chamada para SetValue.

Confira também