Compartilhar via


Precedência do valor da propriedade de dependência

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

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, ele ajuda se você estiver familiarizado com XAML (Extensible Application Markup Language) e saber como escrever aplicativos WPF.

O sistema de propriedades do WPF

O sistema de propriedades do WPF usa uma variedade de fatores para determinar o valor das propriedades de dependência, como validação de propriedade em tempo real, associaçã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, aprender isso pode ajudá-lo a evitar configurações de propriedade desnecessárias e também 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. Com exceção da coerção e da animação, um valor de propriedade definido localmente tem a precedência mais alta, portanto, o botão será vermelho, mesmo no mouseover. 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, portanto, o botão será amarelo ao passar o mouse sobre ele e azul, caso contrário. O exemplo substitui o padrão ControlTemplate do botão porque o modelo padrão tem um valor de mouseover Background codificado 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 runtime 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 Coercion e animações.

  2. Animações ativas ou animações com um comportamento hold. Para ter um efeito prático, um valor de animação deve ter precedência sobre o valor base (unanimado), mesmo que o valor base tenha sido definido localmente. Para obter mais informações, consulte Coercion 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 à 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 da propriedade de modelo TemplatedParent. Um elemento tem um TemplatedParent se foi criado por um modelo (ControlTemplate ou DataTemplate). Para obter mais informações, consulte TemplatedParent. Dentro do modelo especificado por TemplatedParent, a ordem de precedência é:

    1. Gatilhos.

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

  5. Estilos implícitos. Aplica-se somente à propriedade Style. O Style valor é qualquer recurso de estilo com um TargetType valor que corresponde ao tipo de 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. Gatilhos em estilos padrão têm uma precedência menor.

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

  8. Valores do setter de estilo. Um valor de estilo setter é um valor que é aplicado por um Setter em um 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). Em 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 todos os elementos em todo o aplicativo. Para obter mais informações, consulte Herança do valor da 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 do sistema de propriedades dessa propriedade. Classes derivadas que herdam uma propriedade de dependência podem substituir metadados da propriedade de dependência (incluindo o valor padrão) com base no 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 herdável não for definida, o valor padrão da raiz ou pai será usado em vez do valor padrão do elemento filho.

TemplatedParent

TemplatedParent precedência não se aplica a propriedades de elementos que são declarados diretamente na marcação padrão do aplicativo. O TemplatedParent conceito existe apenas para itens filho em 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 procura o modelo que criou o elemento. Os valores de propriedade do TemplatedParent modelo geralmente atuam como se fossem valores definidos localmente no elemento, mas com menor precedência do que os valores locais reais, pois 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 propriedade Style é exclusiva por não poder ser estilizada. Não é recomendável coagir ou animar a Style propriedade (e animar a Style 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 atua 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 em linha; no lugar 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 ele 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 que MyButton seja derivado de Button. O Style valor da propriedade tem a mesma precedência que o item 5 na lista de precedência. Chamando o método DependencyPropertyHelper.GetValueSource, passando a propriedade Style e verificando ImplicitStyleReference nos resultados, pode-se detectar um valor de estilo implícito.

  • 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 de tema em tempo de execução pelo mecanismo de apresentação do WPF. Antes do runtime, 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 é conhecido como um estilo de tema.

O ControlTemplate é um item importante dentro do estilo padrão de um controle. ControlTemplate é um valor definidor para a propriedade Template do estilo. Se os estilos padrão não contiverem um modelo, um controle sem um modelo personalizado como parte de um estilo personalizado não teria nenhuma aparência visual. Não só um modelo define a aparência visual de um controle, como 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 Thumb controle, que é um ScrollBar componente.

Um Thumb controle tem determinadas propriedades personalizáveis. O modelo padrão de um controle Thumb cria uma estrutura básica, ou árvore visual, com vários componentes Border aninhados, promovendo uma aparência chanfrada. Dentro do modelo, as propriedades que se destinam a serem personalizáveis pela Thumb classe são expostas por meio de TemplateBinding

Os estilos padrão especificam um TargetType em suas definições. A avaliação de tema de runtime corresponde o TargetType de um estilo padrão à propriedade DefaultStyleKey de um controle. Por outro lado, o comportamento de busca para estilos implícitos utiliza o tipo real do controle. O valor de DefaultStyleKey é herdado por classes derivadas, portanto, elementos derivados que podem não ter nenhum estilo associado obtêm uma aparência visual padrão. Por exemplo, se você derivar MyButton de Button, MyButton herdará o modelo padrão de Button. Classes derivadas podem substituir o valor padrão de DefaultStyleKey em metadados de propriedade de dependência. Portanto, se você quiser uma representação visual diferente para MyButton, poderá substituir os metadados da propriedade de dependência de DefaultStyleKey em MyButton, e então definir o estilo padrão relevante, incluindo um modelo, que você empacotará com seu controle MyButton. Para obter mais informações, consulte a visão geral da criação de controle

Recurso dinâmico

Recurso dinâmico

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 de referências dinâmicas de recursos é: elemento para raiz de página, aplicativo, tema e 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 estão definidas, o valor é adiado. Uma consequência disso é que, se você definir um recurso dinâmico ou associação a um valor local, qualquer alteração no valor local substituirá inteiramente o recurso dinâmico ou a associação. Mesmo que você chame o ClearValue método para limpar o valor definido localmente, o recurso dinâmico ou a associação não serão restaurados. Na verdade, se você chamar a função ClearValue em uma propriedade que tenha um recurso dinâmico ou associação (sem valor local literal), o recurso dinâmico ou associação será removido.

DefinirValorAtual

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 você atribuir outro valor usando SetCurrentValue, a próxima ação de gatilho definirá a propriedade de volta para o valor do gatilho. 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 vinculação.

Coerção e animação

A coerção e a animação agem em 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 por meio da lista de precedência até que o item 2 seja atingido.

Se uma animação não especificar valores de propriedade para From e To em certos comportamentos, ou se a animação reverter deliberadamente para o valor base quando concluída, o valor base pode influenciar o valor da animação. 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 diferem 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 FillBehavior, ao concluir, uma animação restaurará um valor de propriedade para seu valor base. A precedência normal é usada para determinação de 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, consulte a visão geral da animação

A coerção está no cume da lista de precedência. Até 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 embutida. 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 forma que as restrições à coerção sejam 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 ao valor base e, potencialmente, a influência de coerção em uma propriedade cessará assim que todas as restrições forem levantadas. Para obter mais informações sobre o comportamento de coerção, consulte retornos de chamada e validação da propriedade de dependência.

Comportamentos desencadeantes

Os controles geralmente definem comportamentos de gatilho como parte de seu estilo padrão. Definir propriedades locais em controles pode potencialmente entrar em conflito com esses gatilhos, impedindo que os gatilhos respondam (visual ou comportamentalmente) a eventos controlados 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 Button está desabilitado, um gatilho de estilo de tema (IsEnabled está false) define o valor de Foreground para que a Button apareça esmaecida. Se você tiver definido um valor local para Foreground, o valor da propriedade local de maior precedência anulará o valor de estilo de tema Foreground, mesmo quando o Button estiver desabilitado. Ao definir valores de propriedade que substituem comportamentos de gatilho no nível do tema para um controle, tenha cuidado para não interferir indevidamente na experiência de 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. Porém, a chamada ClearValue não garante que o valor padrão estabelecido nos metadados durante o registro de propriedade 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.

Consulte também