Visão geral das propriedades de dependência (WPF .NET)

O WPF (Windows Presentation Foundation) fornece um conjunto de serviços que podem ser usados para estender a funcionalidade de uma propriedade de um tipo. Coletivamente, esses serviços são chamados de sistema de propriedade WPF. Uma propriedade que é apoiada pelo sistema de propriedades WPF é conhecida como uma propriedade de dependência. Esta visão geral descreve o sistema de propriedades WPF e os recursos de uma propriedade de dependência, incluindo como usar propriedades de dependência existentes em XAML e no código. Esta visão geral também apresenta aspectos especializados de propriedades de dependência, como metadados de propriedades de dependência e como criar sua própria propriedade de dependência em uma classe personalizada.

Importante

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

Pré-requisitos

Este artigo pressupõe conhecimento básico do sistema de tipo .NET e programação orientada a objeto. Para seguir os exemplos neste artigo, ajuda a entender XAML e saber como escrever aplicativos WPF. Para obter mais informações, consulte Tutorial: Criar um novo aplicativo WPF com .NET.

Propriedades de dependência e propriedades CLR

As propriedades do WPF são normalmente expostas como propriedades padrão do .NET. Você pode interagir com essas propriedades em um nível básico e nunca saber que elas são implementadas como uma propriedade de dependência. No entanto, a familiaridade com alguns ou todos os recursos do sistema de propriedades WPF, ajudará você a tirar proveito desses recursos.

O objetivo das propriedades de dependência é fornecer uma maneira de calcular o valor de uma propriedade com base no valor de outras entradas, como:

  • Propriedades do sistema, como temas e preferência do usuário.
  • Mecanismos de determinação de propriedades just-in-time, como vinculação de dados e animações/storyboards.
  • Modelos de uso múltiplo, como recursos e estilos.
  • Valores conhecidos por meio de relacionamentos pai-filho com outros elementos na árvore de elementos.

Além disso, uma propriedade de dependência pode fornecer:

  • Validação autossuficiente.
  • Valores padrão.
  • Retornos de chamada que monitoram alterações em outras propriedades.
  • Um sistema que pode coagir valores de propriedade com base em informações de tempo de execução.

As classes derivadas podem alterar algumas características de uma propriedade existente substituindo os metadados de uma propriedade de dependência, em vez de substituir a implementação real de propriedades existentes ou criar novas propriedades.

Na referência do SDK, você pode identificar uma propriedade de dependência pela presença de uma seção Informações de Propriedade de Dependência na página de referência gerenciada dessa propriedade. A seção Informações da propriedade de dependência inclui um link para o DependencyProperty campo identificador dessa propriedade de dependência. Ele também inclui a lista de opções de metadados para essa propriedade, informações de substituição por classe e outros detalhes.

As propriedades de dependência dão suporte às propriedades CLR

As propriedades de dependência e o sistema de propriedades WPF estendem a funcionalidade da propriedade fornecendo um tipo que faz backup de uma propriedade, como uma alternativa ao padrão de backup de uma propriedade com um campo privado. O nome desse tipo é DependencyProperty. O outro tipo importante que define o sistema de propriedades WPF é DependencyObject, que define a classe base que pode registrar e possuir uma propriedade de dependência.

Aqui estão algumas terminologias comumente usadas:

  • Propriedade de dependência, que é uma propriedade que é apoiada por um DependencyPropertyarquivo .

  • Identificador de propriedade de dependência, que é uma instância obtida como um valor de retorno ao registrar uma propriedade de dependência e, em seguida, armazenada como um membro estático de uma DependencyProperty classe. Muitas das APIs que interagem com o sistema de propriedades WPF usam o identificador de propriedade de dependência como um parâmetro.

  • CLR "wrapper", que é o get e set implementações para a propriedade. Essas implementações incorporam o identificador de propriedade de dependência usando-o GetValue nas chamadas e SetValue . Dessa forma, o sistema de propriedades WPF fornece o suporte para a propriedade.

O exemplo a seguir define a propriedade dependency para mostrar a relação do DependencyProperty identificador com a IsSpinning propriedade que ele apoia.

public static readonly DependencyProperty IsSpinningProperty = DependencyProperty.Register(
    "IsSpinning", typeof(bool),
    typeof(MainWindow)
    );

public bool IsSpinning
{
    get => (bool)GetValue(IsSpinningProperty);
    set => SetValue(IsSpinningProperty, value);
}
Public Shared ReadOnly IsSpinningProperty As DependencyProperty =
    DependencyProperty.Register("IsSpinning", GetType(Boolean), GetType(MainWindow))

Public Property IsSpinning As Boolean
    Get
        Return GetValue(IsSpinningProperty)
    End Get
    Set(value As Boolean)
        SetValue(IsSpinningProperty, value)
    End Set
End Property

A convenção de nomenclatura da propriedade e de seu campo DependencyProperty de suporte é importante. O nome do campo é sempre o nome da propriedade, com o sufixo Property acrescentado. Para obter mais informações sobre essa convenção e os motivos para ela, consulte Propriedades de dependência personalizadas.

Configurando valores da propriedade

É possível definir propriedades no código ou em XAML.

Configurando valores da propriedade em XAML

O exemplo XAML a seguir define a cor do plano de fundo de um botão como vermelho. O valor da cadeia de caracteres para o atributo XAML é convertido pelo analisador XAML do WPF em um tipo WPF. No código gerado, o tipo WPF é um , por meio de um ColorSolidColorBrusharquivo .

<Button Content="I am red" Background="Red"/>

O XAML oferece suporte a vários formulários de sintaxe para definir propriedades. A sintaxe a ser usada para uma determinada propriedade depende do tipo de valor que uma propriedade usa e de outros fatores, como a presença de um conversor de tipo. Para obter mais informações sobre a sintaxe XAML para definir propriedades, consulte XAML em WPF e sintaxe XAML Em detalhes.

O exemplo XAML a seguir mostra outro plano de fundo de botão que usa sintaxe de elemento de propriedade em vez de sintaxe de atributo. Em vez de definir uma cor sólida simples, o XAML define a propriedade button Background como uma imagem. Um elemento representa essa imagem e um atributo do elemento aninhado especifica a origem da imagem.

<Button Content="I have an image background">
    <Button.Background>
        <ImageBrush ImageSource="stripes.jpg"/>
    </Button.Background>
</Button>

Configurando propriedades no código

A definição de valores de propriedade de dependência no código normalmente é apenas uma chamada para a set implementação exposta pelo "wrapper" do CLR:

Button myButton = new();
myButton.Width = 200.0;
Dim myButton As New Button With {
    .Width = 200.0
}

Obter um valor de propriedade é essencialmente uma chamada para a implementação do get "wrapper":

double whatWidth = myButton.Width;
Dim whatWidth As Double = myButton.Width

Você também pode chamar as APIs GetValue do sistema de propriedades e SetValue diretamente. Chamar as APIs diretamente é apropriado para alguns cenários, mas geralmente não quando você está usando propriedades existentes. Normalmente, os invólucros são mais convenientes e fornecem melhor exposição da propriedade para ferramentas de desenvolvedor.

As propriedades também podem ser definidas em XAML e, em seguida, acessadas no código posteriormente, por meio do code-behind. Para obter detalhes, consulte Code-behind e XAML no WPF.

Funcionalidade de propriedade fornecida por uma propriedade de dependência

Ao contrário de uma propriedade que é apoiada por um campo, uma propriedade de dependência estende a funcionalidade de uma propriedade. Muitas vezes, a funcionalidade adicionada representa ou oferece suporte a um destes recursos:

Recursos

Você pode definir um valor de propriedade de dependência fazendo referência a um recurso. Os recursos são normalmente especificados como o Resources valor da propriedade de um elemento raiz da página ou do aplicativo, já que esses locais oferecem acesso conveniente ao recurso. Neste exemplo, definimos um SolidColorBrush recurso:

<StackPanel.Resources>
    <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
</StackPanel.Resources>

Agora que o recurso está definido, podemos fazer referência ao recurso para fornecer um valor para a Background propriedade:

<Button Background="{DynamicResource MyBrush}" Content="I am gold" />

No WPF XAML, você pode usar uma referência de recurso estático ou dinâmico. Esse recurso específico é referenciado como um DynamicResource. Uma referência de recurso dinâmico só pode ser usada para definir uma propriedade de dependência, portanto, é especificamente o uso de referência de recurso dinâmico que é habilitado pelo sistema de propriedades WPF. Para obter mais informações, consulte Recursos XAML.

Observação

Os recursos são tratados como um valor local, o que significa que, se você definir outro valor local, eliminará a referência de recurso. Para obter mais informações, consulte Precedência do valor da propriedade de dependência.

Vinculação de dados

Uma propriedade de dependência pode referenciar um valor por meio da vinculação de dados. A associação de dados funciona por meio de uma sintaxe de extensão de marcação específica em XAML ou pelo objeto Binding no código. Com a vinculação de dados, a determinação do valor final da propriedade é adiada até o tempo de execução, momento em que o valor é obtido de uma fonte de dados.

O exemplo a seguir define a Content propriedade de um Button, usando uma associação declarada em XAML. A associação usa um contexto de dados herdado e uma fonte de dados XmlDataProvider (não mostrada). A própria associação especifica a propriedade source dentro da fonte de dados por XPath.

<Button Content="{Binding Source={StaticResource TestData}, XPath=test[1]/@text}"/>

Observação

As ligações são tratadas como um valor local, o que significa que, se você definir outro valor local, eliminará a associação. Para obter detalhes, consulte Precedência do valor da propriedade de dependência.

As propriedades de dependência, ou a DependencyObject classe, não oferecem suporte INotifyPropertyChanged nativo para notificação de alterações no DependencyObject valor da propriedade de origem para operações de vinculação de dados. Para obter mais informações sobre como criar propriedades para uso na vinculação de dados que podem relatar alterações em um destino de vinculação de dados, consulte Visão geral da vinculação de dados.

Estilos

Estilos e modelos são motivos convincentes para usar propriedades de dependência. Os estilos são particularmente úteis para definir propriedades que definem a interface do usuário do aplicativo. Normalmente, os estilos são definidos como recursos no XAML. Os estilos interagem com o sistema de propriedades porque normalmente contêm "setters" para propriedades específicas e "triggers" que alteram um valor de propriedade com base no valor de tempo de execução de outra propriedade.

O exemplo a seguir cria um estilo simples, que seria definido dentro de um Resources dicionário (não mostrado). Em seguida, esse estilo é aplicado diretamente à propriedade de Style um Buttonarquivo . O setter no estilo define a propriedade Background de um Button com estilo como verde.

<Style x:Key="GreenButtonStyle">
    <Setter Property="Control.Background" Value="Green"/>
</Style>
<Button Style="{StaticResource GreenButtonStyle}" Content="I am green"/>

Para obter mais informações, confira Estilo e modelagem.

Animações

As propriedades de dependência podem ser animadas. Quando uma animação aplicada é executada, o valor animado tem precedência maior do que qualquer outro valor de propriedade, incluindo um valor local.

O exemplo a seguir anima a Background propriedade de um Buttonarquivo . Tecnicamente, a sintaxe do elemento de propriedade define um espaço em branco SolidColorBrush como o Background, e a Color propriedade do SolidColorBrush é animada.

<Button Content="I am animated">
    <Button.Background>
        <SolidColorBrush x:Name="AnimBrush"/>
    </Button.Background>
    <Button.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <ColorAnimation
                        Storyboard.TargetName="AnimBrush" 
                        Storyboard.TargetProperty="(SolidColorBrush.Color)"
                        From="Blue" To="White" Duration="0:0:1" 
                        AutoReverse="True" RepeatBehavior="Forever" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Button.Triggers>
</Button>

Para obter mais informações sobre propriedades de animação, consulte Visão geral da animação e Visão geral dos storyboards.

Substituições de metadados

Você pode alterar comportamentos específicos de uma propriedade de dependência substituindo seus metadados quando deriva da classe que registrou originalmente a propriedade de dependência. A substituição de metadados depende do DependencyProperty identificador e não requer a reimplementação da propriedade. A alteração de metadados é tratada nativamente pelo sistema de propriedades. Cada classe potencialmente contém metadados individuais para todas as propriedades herdadas de classes base, em uma base por tipo.

O exemplo a seguir substitui metadados de uma DefaultStyleKey propriedade de dependência. A substituição de metadados para essa propriedade de dependência específica faz parte de um padrão de implementação para criar controles que podem usar estilos padrão de temas.

public class SpinnerControl : ItemsControl
{
    static SpinnerControl() => DefaultStyleKeyProperty.OverrideMetadata(
            typeof(SpinnerControl),
            new FrameworkPropertyMetadata(typeof(SpinnerControl))
        );
}
Public Class SpinnerControl
    Inherits ItemsControl
    Shared Sub New()
        DefaultStyleKeyProperty.OverrideMetadata(GetType(SpinnerControl), New FrameworkPropertyMetadata(GetType(SpinnerControl)))
    End Sub
End Class

Para obter mais informações sobre como substituir ou acessar metadados para propriedades de dependência, consulte Substituir metadados de uma propriedade de dependência.

Herança do valor de propriedade

Um elemento pode herdar o valor de uma propriedade de dependência de seu pai na árvore de objetos.

Observação

O comportamento de herança de valor de propriedade não está habilitado globalmente para todas as propriedades de dependência, porque o tempo de cálculo para herança afeta o desempenho. Normalmente, a herança do valor da propriedade só é habilitada em cenários que sugerem aplicabilidade. Você pode verificar se uma propriedade de dependência herda examinando a seção Informações da propriedade de dependência para essa propriedade de dependência na referência do SDK.

O exemplo a seguir mostra uma associação que inclui a propriedade para especificar a DataContext origem da associação. Portanto, as associações em objetos filho não precisam especificar a origem e podem usar o valor herdado de DataContext no objeto pai StackPanel . Ou, um objeto filho pode especificar diretamente seu próprio DataContext ou um Source no Binding, e não usar o valor herdado.

<StackPanel Canvas.Top="50" DataContext="{Binding Source={StaticResource TestData}}">
    <Button Content="{Binding XPath=test[2]/@text}"/>
</StackPanel>

Para obter mais informações, consulte Herança de valor de propriedade.

Integração com o WPF Designer

Controles personalizados com propriedades implementadas como propriedades de dependência se integram bem com o WPF Designer para Visual Studio. Um exemplo é a capacidade de editar propriedades de dependência diretas e anexadas na janela Propriedades . Para obter mais informações, consulte Visão geral sobre a criação de controles.

Precedência do valor da propriedade de dependência

Qualquer uma das entradas baseadas em propriedade dentro do sistema de propriedades WPF pode definir o valor de uma propriedade de dependência. A precedência do valor da propriedade de dependência existe para que os vários cenários de como as propriedades obtêm seus valores interajam de maneira previsível.

Observação

A documentação do SDK às vezes usa o termo "valor local" ou "valor definido localmente" ao discutir propriedades de dependência. Um valor definido localmente é um valor de propriedade definido diretamente em uma instância de objeto no código ou como um atributo de elemento em XAML.

O próximo exemplo inclui um estilo que se aplica à Background propriedade de qualquer botão, mas especifica um botão com uma propriedade definida Background localmente. Tecnicamente, esse botão tem sua Background propriedade definida duas vezes, embora apenas um valor se aplique — o valor com a precedência mais alta. Um valor definido localmente tem a precedência mais alta, exceto para uma animação em execução, que não existe aqui. Portanto, o segundo botão usa o valor definido localmente para a Background propriedade, em vez do valor do setter de estilo. O primeiro botão não tem nenhum valor local ou outro valor com precedência maior do que um setter de estilo e, portanto, usa o valor de setter de estilo para a Background propriedade.

<StackPanel>
    <StackPanel.Resources>
        <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
            <Setter Property="Background" Value="Orange"/>
        </Style>
    </StackPanel.Resources>
    <Button>I am styled orange</Button>
    <Button Background="Pink">I am locally set to pink (not styled orange)</Button>
</StackPanel>

Por que a precedência da propriedade de dependência existe?

Os valores definidos localmente têm precedência sobre os valores do setter de estilo, o que oferece suporte ao controle local das propriedades do elemento. Para obter detalhes, consulte Precedência do valor da propriedade de dependência.

Observação

Várias propriedades definidas em elementos WPF não são propriedades de dependência, porque as propriedades de dependência geralmente eram implementadas somente quando um recurso do sistema de propriedades WPF era necessário. Os recursos incluem vinculação de dados, estilo, animação, suporte a valores padrão, herança, propriedades anexadas e invalidação.

Mais informações sobre propriedades de dependência

  • Os desenvolvedores de componentes ou aplicativos podem desejar criar sua própria propriedade de dependência para adicionar recursos, como vinculação de dados ou suporte a estilos, ou suporte a invalidação e coerção de valor. Para obter mais informações, consulte Propriedades de dependência personalizadas.

  • Considere as propriedades de dependência como propriedades públicas, acessíveis ou detectáveis por qualquer chamador com acesso a uma instância. Para obter mais informações, consulte Segurança de propriedade de dependência.

  • Uma propriedade anexada é um tipo de propriedade que dá suporte a uma sintaxe especializada em XAML. Uma propriedade anexada geralmente não tem uma correspondência 1:1 com uma propriedade Common Language Runtime e não é necessariamente uma propriedade de dependência. O objetivo principal de uma propriedade anexada é permitir que elementos filho relatem valores de propriedade a um elemento pai, mesmo que o elemento pai e o elemento filho não incluam essa propriedade como parte das listagens de membros da classe. Um cenário principal é habilitar um elemento filho para informar aos elementos pai como apresentá-los na interface do usuário. Para obter exemplos, consulte Dock e Left. Para obter mais informações, consulte Visão geral das propriedades anexadas.

Confira também