Precedência do valor da propriedade de 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 aspetos do sistema de propriedades se aplicam ao valor efetivo de uma propriedade.
Pré-requisitos
Este tópico pressupõe que o leitor compreenda as propriedades de dependência da perspetiva de um utilizador das propriedades de dependência existentes em classes WPF, e tenha lido a 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 WPF
O sistema de propriedades WPF oferece uma maneira poderosa de fazer com que o valor das propriedades de dependência seja determinado por uma variedade de fatores, permitindo recursos como validação de propriedade em tempo real, vinculação tardia e notificação de propriedades relacionadas de alterações em valores para outras propriedades. A ordem exata e a lógica usadas para determinar os valores de propriedade de dependência são razoavelmente complexas. Conhecer essa ordem ajudará você a evitar configurações de propriedade desnecessárias e também pode esclarecer a confusão sobre exatamente por que algumas tentativas de influenciar ou antecipar um valor de propriedade de dependência não acabaram resultando no valor esperado.
As propriedades de dependência podem ser "definidas" em vários locais
Segue-se 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 se aplique — 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, você pode esperar que o valor será honrado, mesmo acima de quaisquer estilos ou modelos de controle. Aqui no exemplo, Background é definido como Vermelho localmente. Portanto, o estilo definido neste escopo, embora seja um estilo implícito que, de outra forma, se aplicaria a todos os elementos desse tipo nesse escopo, não é a maior precedência para dar à propriedade Background seu valor. Se removeres o valor local de vermelho dessa instância de botão, o estilo terá precedência e o botão obterá o valor de fundo desse estilo. Dentro do estilo, os gatilhos têm precedência, então o botão será azul se o mouse estiver sobre ele, e verde caso contrário.
Lista de Precedência de Definição de Propriedade de Dependência
A seguir está 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. Esta lista expande algumas das generalizações feitas no Dependency Properties Overview.
Coerção do sistema de propriedade. Para obter detalhes sobre coerção, consulte Coerção, animação e valor base mais adiante neste tópico.
Animações ativas ou animações com um comportamento de retenção. Para ter qualquer efeito prático, uma animação de uma propriedade deve ser capaz de ter precedência sobre o valor base (não animado), mesmo que esse valor tenha sido definido localmente. Para mais informações, consulte Coerção, animação e valor base mais adiante neste tópico.
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 API SetValue 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 agirá na precedência como se um valor direto tivesse sido definido.
Propriedades do modelo TemplatedParent. Um elemento tem um TemplatedParent se tiver sido criado como parte de um modelo (um ControlTemplate ou DataTemplate). Para obter detalhes sobre quando isso se aplica, consulte TemplatedParent mais adiante neste tópico. No modelo, aplica-se a seguinte precedência:
Gatilhos do modelo TemplatedParent.
Conjuntos de propriedades (normalmente por meio de atributos XAML) no modelo TemplatedParent.
Estilo implícito. Aplica-se apenas à propriedade
Style
. A propriedadeStyle
é 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 prossegue nos temas.Gatilhos de estilo. Os gatilhos dentro de estilos de página ou aplicativo (esses estilos podem ser estilos explícitos ou implícitos, mas não dos estilos padrão, que têm precedência menor).
Gatilhos de modelo. Qualquer gatilho de um modelo dentro de um estilo ou um modelo aplicado diretamente.
Delineadores de estilo Valores de um Setter nos estilos de uma página ou aplicação.
Estilo padrão (tema). Para obter detalhes sobre quando isso se aplica e como os estilos de tema se relacionam com os modelos dentro dos estilos de tema, consulte Estilos padrão (tema) mais adiante neste tópico. Dentro de um estilo padrão, aplica-se a seguinte ordem de precedência:
Gatilhos ativados no estilo de tema.
Setters no estilo do tema.
Herança. Algumas propriedades de dependência herdam seus valores de elemento pai para elemento filho, de modo que eles não precisam ser definidos especificamente em cada elemento em todo um aplicativo. Para obter detalhes, consulte Herança do Valor da Propriedade.
Valor padrão dos metadados da propriedade de dependência. Qualquer propriedade de dependência pode ter um valor padrão, conforme estabelecido pelo registro do sistema de propriedade 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 a Metadados da Propriedade de Dependência para obter mais informações. Como a herança é verificada antes do valor padrão, para uma propriedade herdada, um valor padrão do elemento pai tem precedência sobre um elemento filho. Consequentemente, se uma propriedade hereditária não for definida em nenhum lugar, o valor padrão especificado no pai ou na raiz será usado em vez do valor padrão do elemento do filho.
TemplatedParent
TemplatedParent como um item de precedência não se aplica a nenhuma propriedade de um elemento que você declara diretamente na marcação de aplicativo padrão. O conceito TemplatedParent existe apenas para itens filho dentro de uma árvore visual que surgem por meio da aplicação do modelo. Quando o sistema de propriedades pesquisa um valor no modelo TemplatedParent, ele está pesquisando o modelo que criou esse elemento. Os valores de propriedade do modelo TemplatedParent geralmente agem como se fossem 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 obter detalhes, consulte TemplatedParent.
A propriedade Style
A ordem de pesquisa descrita anteriormente aplica-se a todas as propriedades de dependência possíveis, exceto uma: a propriedade Style. A propriedade Style é única na medida em que não pode ser estilizada, portanto, os itens de precedência 5 a 8 não se aplicam. Além disso, animar ou coagir Style não é recomendado (e animar Style exigiria uma classe de animação personalizada). Isso deixa três maneiras pelas quais a propriedade Style pode ser definida:
Estilo explícito. A propriedade Style é definida diretamente. Na maioria dos cenários, o estilo não é definido diretamente, mas sim referenciado como um recurso, por chave explícita. Neste caso, a própria propriedade Style age como se fosse um valor local, item de precedência 3.
Estilo implícito. A propriedade Style não é definida diretamente. No entanto, o Style existe em algum nível na sequência de pesquisa de recursos (página, aplicativo) e é digitado usando uma chave de recurso que corresponde ao tipo ao qual o estilo deve ser aplicado. Neste caso, a própria propriedade Style atua por uma precedência identificada na sequência como item 5. Essa condição pode ser detetada usando DependencyPropertyHelper contra a propriedade Style e procurando ImplicitStyleReference nos resultados.
Estilo padrão, também conhecido como estilo de tema . A propriedade Style não é definida diretamente e, na verdade, será interpretada como
null
até o tempo de execução. Nesse caso, o estilo vem da avaliação do tema durante o 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 classe derivada de MyButton
Button
não usará implicitamente um estilo para Button
.
Estilos padrão (tema)
Cada controle fornecido com o WPF tem um estilo padrão. Esse estilo padrão varia potencialmente de acordo com o tema, e é por isso que esse estilo padrão às vezes é chamado de estilo de tema.
As informações mais importantes encontradas num estilo predefinido para um controlo são o seu template de controlo, que existe no estilo do tema como um definidor para a sua propriedade Template. Se não houvesse nenhum modelo de estilos padrão, um controle sem um modelo personalizado como parte de um estilo personalizado não teria aparência visual. O modelo do estilo padrão dá à 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 podem influenciar a aparência visual do controle sem substituir completamente o modelo. Por exemplo, considere a aparência visual padrão de um controle Thumb, que é um componente de um ScrollBar.
Um Thumb tem certas propriedades personalizáveis. O modelo padrão de um Thumb cria uma estrutura básica e uma árvore visual com vários componentes Border aninhados para criar uma aparência de bisel. Se uma propriedade que faz parte do modelo se destina a ser exposta para personalização pela classe Thumb, essa propriedade deve ser exposta por um TemplateBinding, dentro do modelo. No caso de Thumb, várias propriedades dessas bordas compartilham um modelo que se liga a propriedades como Background ou BorderThickness. Mas certas outras propriedades ou arranjos visuais são codificados no modelo de controle ou estão vinculados a valores que vêm diretamente do tema e não podem ser alterados sem substituir o modelo inteiro. Geralmente, se uma propriedade vem de um pai de modelo e não é exposta através de uma ligação de modelo, ela não pode ser ajustada por meio de estilos porque não há uma maneira fácil de apontá-la. Mas essa propriedade ainda pode 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 chave em suas definições. No entanto, quando os temas são aplicados a uma determinada ocorrência de elemento, a pesquisa de temas para esse tipo é realizada verificando a propriedade DefaultStyleKey em um controle. Isso contrasta com o uso do Tipo literal, como fazem os estilos implícitos. 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 sim 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 outra forma, não têm um estilo (ou, mais importante, não têm um modelo dentro desse estilo e, portanto, não teriam nenhuma aparência visual padrão). Assim, você pode derivar MyButton
de Button e ainda obterá o Button modelo padrão. Se tu fosses o autor do controlo de MyButton
e quisesses um comportamento diferente, poderias substituir os metadados da propriedade de dependência para DefaultStyleKey em MyButton
, para retornar uma chave diferente, e depois definir os estilos de tema relevantes, incluindo o modelo para MyButton
que deves incluir com o teu controlo MyButton
. Para obter mais detalhes sobre temas, estilos e criação de controles, consulte Visão geral da criação de controle .
Referências de recursos dinâmicos e vinculação
As referências de recursos dinâmicos e as operações vinculativas respeitam a precedência do local em que são definidas. Por exemplo, um recurso dinâmico aplicado a um valor local atua conforme o item 3 da precedência, uma vinculação para um definidor de propriedade dentro de um estilo de tema aplica-se no item 9 da precedência, e assim por diante. Como as referências de recursos dinâmicos e a vinculação devem ser capazes de obter valores do estado de tempo de execução do aplicativo, isso implica que o processo real de determinar a precedência do valor da propriedade para qualquer propriedade específica também se estende para o tempo de execução.
As referências de recursos dinâmicos não são estritamente parte do sistema de propriedades, mas elas têm uma ordem de pesquisa própria que interage com a sequência listada acima. Essa precedência está documentada mais detalhadamente nos Recursos XAML. O resumo básico dessa precedência é: elemento à raiz da página, aplicação, tema, sistema.
Recursos dinâmicos e ligações têm a precedência de onde foram definidos, mas o valor é adiado. Uma consequência disso é que, se você definir um recurso dinâmico ou vincular a um valor local, qualquer alteração no valor local substituirá totalmente o recurso dinâmico ou a vinculação. Mesmo se você chamar o método ClearValue para limpar o valor definido localmente, o recurso dinâmico ou a associação não será restaurado. Na verdade, se se chamar ClearValue numa propriedade que tenha um recurso dinâmico ou uma ligação presente (sem valor local literal), esses elementos também são limpos pela chamada ClearValue.
DefinirValorAtual
O método SetCurrentValue é outra maneira de definir uma propriedade, mas não está na ordem de precedência. Em vez disso, SetCurrentValue permite alterar 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 é definida por um gatilho e, em seguida, atribuído outro valor via SetCurrentValue, o sistema de propriedades ainda respeita o gatilho e a propriedade mudará se a ação do gatilho ocorrer. SetCurrentValue permite alterar o valor da propriedade sem lhe atribuir uma origem com maior precedência. Da mesma forma, pode usar SetCurrentValue para alterar o valor de uma propriedade sem substituir uma vinculação.
Coerção, animações e valor base
A coerção e a animação atuam sobre um valor que é denominado como o "valor base" em todo este SDK. O valor base é, portanto, qualquer valor que seja determinado através da avaliação ascendente nos itens até que o item 2 seja alcançado.
Para uma animação, o valor base pode ter um efeito sobre o valor animado, se essa animação não especificar tanto "From" como "To" para certos comportamentos, ou se a animação deliberadamente reverter para o valor base quando concluída. Para ver isso na prática, execute o Exemplo de Valores de Destino de Animação: De, Para e Por. Tente definir os valores locais da altura do retângulo no exemplo, de modo que o valor local inicial seja diferente de qualquer valor "De" na animação. Você notará que as animações começam imediatamente usando os valores "De" e substituem o valor base uma vez iniciado. A animação pode indicar o retorno ao valor encontrado antes da animação uma vez concluída, ao especificar Stop FillBehavior. Posteriormente, utiliza-se a precedência normal para a determinação do valor de base.
Várias animações podem ser aplicadas a uma única propriedade, com cada uma dessas animações possivelmente tendo sido definida a partir de pontos diferentes na precedência do valor. No entanto, essas animações potencialmente comporão seus valores, em vez de apenas aplicar a animação da precedência mais alta. Isso depende exatamente de como as animações são definidas e do tipo de valor que está sendo animado. Para obter mais informações sobre como animar propriedades, consulte Visão Geral da Animação.
A coerção aplica-se ao nível mais alto de todos. Mesmo uma animação já em execução está sujeita a coerção de valor. Certas propriedades de dependência existentes no WPF têm coerção incorporada. 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. Você também pode substituir o comportamento de coerção de propriedades existentes substituindo os metadados nessa 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 à medida que essas restrições existem no momento, mas o valor base ainda é mantido. Portanto, se as restrições na coerção forem posteriormente levantadas, a coerção retornará o valor mais próximo possível desse valor base e, potencialmente, a influência da coerção sobre 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 de propriedade de dependência.
Comportamentos desencadeantes
Os controlos geralmente definem comportamentos de ativação como parte do seu estilo predefinido em temas. Definir propriedades locais em controles pode impedir que os gatilhos sejam capazes de responder a eventos controlados pelo usuário visual ou comportamentalmente. O uso mais comum de um gatilho de propriedade é para propriedades de controle ou estado, como IsSelected. Por exemplo, por predefinição, quando um Button está desativado (o gatilho para IsEnabled é false
), o valor Foreground no estilo do tema é o que faz com que o controlo pareça "acinzentado". Mas se você tiver definido um valor de Foreground local, essa cor cinza normal será anulada em precedência pelo seu conjunto de propriedades local, mesmo nesse cenário acionado por propriedade. Tenha cuidado ao definir valores para propriedades que tenham comportamentos de gatilho no nível do tema e certifique-se de que você não está interferindo indevidamente na experiência do usuário pretendida para esse controle.
ClearValue e precedência de valor
O método ClearValue fornece um meio conveniente para limpar qualquer valor aplicado localmente de uma propriedade de dependência definida em um elemento. No entanto, chamar ClearValue não é uma garantia de que o valor por defeito estabelecido nos metadados durante o registo de propriedade seja o novo valor efetivo. Todos os outros participantes na precedência de valor ainda estão ativos. Apenas o valor definido localmente foi removido da sequência de precedência. Por exemplo, se você chamar ClearValue em 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 quiser retirar todos os participantes do valor da propriedade do processo e definir o valor como padrão de metadados registrados, você 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.
Ver também
- DependencyObject
- DependencyProperty
- Visão geral das propriedades de dependência
- propriedades de dependência personalizadas
- Propriedade de Dependência: Retornos de Chamada e Validação
.NET Desktop feedback