Compartilhar via


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 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, é útil se você estiver familiarizado com XAML (Extensible Application Markup Language) e souber 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 os valores da propriedade de dependência sejam complexas, aprendê-las 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, portanto, o botão ficará amarelo ao passar o mouse e azul caso contrário. O exemplo substitui o padrão ControlTemplate do botão porque o modelo padrão tem um valor mouseover Background embutido em código.

Lista de precedência de propriedade de dependência

A lista a seguir é a ordem definitiva de precedência 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 (não animado), 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 TemplatedParent if foi criado por um 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 TargetType valor que corresponda ao tipo de elemento. O recurso de estilo deve existir na página ou no 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 na página ou no aplicativo. Os gatilhos em 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 na página ou no aplicativo.

  8. Valores do setter de estilo. Um valor de setter de estilo é um valor aplicado por um Setter dentro de um estilo. O estilo deve existir na página ou no 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 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 do sistema de propriedades dessa propriedade. 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 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 A precedência não se aplica às propriedades de elementos declarados diretamente na marcação de aplicativo padrão. O TemplatedParent conceito existe apenas para itens filho dentro de uma árvore visual que passam a existir 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 TemplatedParent modelo geralmente agem como se fossem valores definidos localmente no elemento, mas com menor precedência do 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. Não é recomendável forçar 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. Existem 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 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 na 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. 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 de 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, mas 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 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 devem ser personalizáveis pela classe são expostas por meio de Thumb TemplateBinding. 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 associados a valores que vêm diretamente do tema, você só pode alterar esses valores substituindo todo o modelo. Geralmente, se uma propriedade vier de um pai com modelo e não for exposta por um TemplateBinding, o valor da propriedade não poderá ser alterado por estilos porque não há uma maneira conveniente de direcioná-la. No entanto, 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 a TargetType DefaultStyleKey um estilo padrão à 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, os elementos derivados que, de outra forma, não teriam 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. As classes derivadas podem substituir o valor padrão dos metadados da propriedade de DefaultStyleKey dependência. Portanto, se você quiser uma representação visual diferente para MyButton, poderá substituir os metadados da propriedade de dependência para DefaultStyleKey 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 da criação de controle.

Recurso dinâmico

As referências de recursos dinâmicos e as operações de associação têm a precedência do local em que sã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 de recursos dinâmicos aplicada a um setter de propriedades em um estilo padrão tem a mesma precedência que o item 9 na lista de precedência. Como as referências e a associação de recursos dinâmicos 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 se estende ao 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 para um valor local, qualquer alteração no valor local substituirá totalmente 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á restaurado. Na verdade, se você chamar ClearValue uma propriedade que tenha um recurso dinâmico ou uma associação (sem valor local literal), o recurso dinâmico ou a associação será limpo.

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 do gatilho definirá a propriedade de volta para o valor do acionador. 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 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 alcançado.

Se uma animação não especificar os From valores de propriedade e To para determinados comportamentos, ou se a animação for revertida 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 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 , 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 forma que as restrições à coerção sejam aplicadas conforme existem no momento, mas o valor base ainda é retido. Como resultado, se as restrições na coerção forem posteriormente suspensas, a coerção retornará o valor mais próximo possível do valor base e, potencialmente, a influência da coerção em 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 de propriedade de dependência.

Comportamentos de gatilho

Os controles geralmente definem comportamentos de gatilho como parte de seu estilo padrão. A configuração de propriedades locais em controles pode entrar em conflito com esses gatilhos, impedindo que os gatilhos respondam (visualmente 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 a Button está desabilitado, um gatilho de estilo de tema (IsEnabled is false) define o Foreground valor para fazer com que a Button aparência fique esmaecida. Se você tiver definido um valor local Foreground , o valor da propriedade local de precedência mais alta anulará o valor do estilo Foreground do tema, 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 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. No entanto, a chamada ClearValue não garante que o valor padrão estabelecido nos metadados durante o registro da propriedade seja o novo valor efetivo. Todos os outros participantes na lista de precedência ainda estão ativos e apenas o valor definido localmente é removido. Por exemplo, se você chamar ClearValue uma propriedade que tenha um estilo de tema, o valor do estilo do tema será aplicado como o novo valor, em vez do padrão baseado em metadados. Se você quiser definir um valor de propriedade como 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