Precedência do valor da propriedade de dependência (WPF .NET)
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 utiliza uma variedade de fatores para determinar o valor das propriedades de dependência, como a validação de propriedades em tempo real, a vinculação tardia, e as notificações de alteração 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 Background do 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 propriedade Background
é definida localmente como Red
. No entanto, o estilo implícito declarado no escopo do botão tenta definir a propriedade Background
como Blue
. E, quando o mouse está sobre o botão, o gatilho no estilo implícito tenta definir a propriedade Background
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 valor Background
do estilo. Dentro de um estilo, os gatilhos têm precedência, portanto, o botão será amarelo quando o ponteiro do mouse passar sobre ele e azul, caso contrário. O exemplo substitui o valor padrão ControlTemplate do botão porque o modelo padrão possui um valor fixo de mouseover Background
.
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.
Sistema de coerção de propriedade. Para mais informações sobre coerção, veja Coerção e animações.
animações ativas ou animações com um comportamento de manutenção. 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 and animations.
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 à API SetValue 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.
valores da propriedade de modelo TemplatedParent. Um elemento terá um TemplatedParent se 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 é:Gatilhos.
Conjuntos de propriedades, normalmente por meio de atributos XAML.
estilos implícitos. Aplica-se somente à propriedade Style. O valor
Style
é qualquer recurso de estilo com um valor TargetType 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.gatilhos Style. 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.
Template aciona. Um gatilho de modelo é um gatilho de um modelo diretamente aplicado ou de um modelo dentro de um estilo. O estilo deve existir dentro da página ou aplicativo.
Valores de configurador de estilo. Um valor de definidor de estilo é um valor aplicado por um Setter dentro de um estilo. O estilo deve existir dentro da página ou aplicativo.
estilos padrão, também conhecidos como estilos de tema . Para obter mais informações, consulte Tema (estilos padrão). Em um estilo padrão, a ordem de precedência é:
Gatilhos ativos.
Setters.
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.
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 dessa propriedade (incluindo o valor padrão) com base no tipo específico. 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
A precedência TemplatedParent não se aplica às propriedades dos elementos declarados diretamente na marcação padrão do aplicativo. O conceito de TemplatedParent
existe apenas para itens filho dentro de uma árvore visual que surge por meio da aplicação de um modelo. Quando o sistema de propriedades pesquisa o modelo especificado pelo TemplatedParent
para obter os valores de propriedade de um elemento, ele está pesquisando o modelo que criou o elemento. Os valores de propriedade do modelo de TemplatedParent
geralmente atuam como se fossem valores definidos localmente no elemento, mas com precedência menor 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 propriedade Style. A propriedade Style
é exclusiva porque ela não pode ser estilizada. Não é recomendável coagir ou animar a propriedade Style
(e animar a propriedade Style
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 propriedade Style
:
Estilo explícito. A propriedade
Style
de um elemento é definida diretamente. O valor da propriedadeStyle
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, estilos explícitos não são definidos como embutidos e, em vez disso, são referenciados explicitamente como recurso, por exemplo,Style="{StaticResource myResourceKey}"
.Estilo implícito. A propriedade
Style
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 tipoMyButton
mesmo queMyButton
seja derivado deButton
. O valor da propriedadeStyle
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 método DependencyPropertyHelper.GetValueSource, passando a propriedadeStyle
e verificandoImplicitStyleReference
nos resultados.estilo padrão, também conhecido como estilo de tema . A propriedade
Style
de um elemento não é definida diretamente. Em vez disso, ele vem da avaliação de tema em tempo de execução feita pelo mecanismo de apresentação do WPF. Antes do tempo de execução, o valor da propriedadeStyle
énull
. O valor da propriedadeStyle
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, razão pela qual 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 configurador 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 controle Thumb, que é um componente ScrollBar.
Um controle Thumb tem determinadas propriedades personalizáveis. O modelo padrão de um controle Thumb
cria uma estrutura básica, ou visual, com vários componentes Border aninhados para criar um efeito biselado. No modelo, as propriedades destinadas a serem personalizáveis pela classe Thumb
são expostas por meio de TemplateBinding. O modelo padrão para o controle Thumb
tem várias propriedades de borda que compartilham um vínculo de modelo com propriedades como Background ou BorderThickness. Mas, quando os valores para propriedades ou arranjos visuais são codificados em código 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 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á-la. 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 um TargetType em suas definições. A avaliação de tema em tempo de execução associa a TargetType
de um estilo padrão à propriedade DefaultStyleKey de um controle. Por outro lado, o comportamento de consulta para estilos implícitos usa 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ê desejar uma representação visual diferente para MyButton
, poderá substituir os metadados da propriedade de dependência para DefaultStyleKey
em MyButton
. Em seguida, defina o estilo padrão relevante, incluindo um modelo, que será incorporado com seu controle MyButton
. 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 precedência conforme o 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 dinâmica de recursos aplicada a um definidor de propriedade em 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 associação devem obter valores do estado de tempo de execução do aplicativo, o processo para determinar a precedência do valor de qualquer propriedade 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 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 método ClearValue para limpar o valor definido localmente, o recurso dinâmico ou a associação não serão restaurados. Na verdade, se você chamar ClearValue
em uma propriedade que tenha um recurso dinâmico ou associação (sem valor literal local), o recurso dinâmico ou associação será limpo.
DefinirValorAtual
O método SetCurrentValue é 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 sobrescrever 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 From e To 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 diferem de qualquer valor From
. As animações de exemplo começam imediatamente usando o valor From
em vez do valor base. Ao especificar Stop como o FillBehavior, ao concluir, uma animação redefinirá 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 Visão geral da animação.
A coerção está no topo 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 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 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 os retornos de chamada e a validação de propriedade de dependência em e.
Comportamentos de ativação
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 é desabilitado, um gatilho de estilo de tema (IsEnabled
é false
) define o valor Foreground para fazer com que a Button
pareça esmaeçada. Se você tiver definido um valor de Foreground
local, o valor da propriedade local de precedência mais alta anulará o estilo de tema Foreground
valor, 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 método ClearValue limpa qualquer valor aplicado localmente de uma propriedade de dependência para um elemento. Porém, chamar 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 da lista de precedência ainda estão ativos e somente o valor definido localmente é removido. Por exemplo, se você chamar ClearValue
em 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
- DependencyObject
- DependencyProperty
- Visão geral das propriedades de dependência
- Propriedades de Dependência Personalizadas
- Chamadas de retorno de Propriedades de Dependência e Validação
.NET Desktop feedback