Partilhar via


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

O Windows Presentation Foundation (WPF) fornece um conjunto de serviços que podem ser usados para estender a funcionalidade da 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 em código. Esta visão geral também apresenta aspetos especializados das propriedades de dependência, como metadados de propriedade de dependência e como criar sua própria propriedade de dependência em uma classe personalizada.

Pré-requisitos

Este artigo pressupõe conhecimento básico do sistema de tipo .NET e programação orientada a objetos. Para seguir os exemplos neste artigo, ele ajuda a entender XAML e saber como escrever aplicativos WPF. Para obter mais informações, consulte Tutorial: Criar um novo aplicativo WPF com o .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, irá ajudá-lo 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 propriedade just-in-time, como associação de dados e animações/storyboards.
  • Modelos de uso múltiplo, como recursos e estilos.
  • Valores conhecidos através de relações 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.
  • Funções de retorno que monitorizam alterações noutras 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 sobre a propriedade de dependência na página de referência gerenciada dessa propriedade. A secção de Informações sobre Propriedades de Dependência inclui um link para o campo identificador DependencyProperty dessa propriedade de dependência. Inclui também a lista de opções de metadados para essa propriedade, as informações de substituição por classe e outros detalhes.

Propriedades de dependência suportam propriedades CLR

As propriedades de dependência e o sistema de propriedades WPF expandem a funcionalidade da propriedade, fornecendo um tipo que serve de suporte a uma propriedade, como uma alternativa ao padrão usual de suportar uma propriedade com um campo privado. O nome deste 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 suportada por um DependencyProperty.

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

  • CLR "wrapper", que são as implementações get e set para a propriedade. Essas implementações incorporam o identificador de propriedade de dependência usando-o nas chamadas GetValue e SetValue. Desta forma, o sistema de propriedades WPF fornece o apoio necessário para a propriedade.

O exemplo a seguir define a propriedade de dependência IsSpinning para mostrar a relação do identificador de DependencyProperty com a propriedade que ela suporta.

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 o campo de apoio DependencyProperty é 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.

Definindo valores de propriedade

Você pode definir propriedades em código ou em XAML.

Definindo valores de 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 em tipo pelo analisador WPF XAML em um tipo WPF. No código gerado, o tipo WPF é um Color, por meio de um SolidColorBrush.

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

O XAML oferece suporte a várias formas 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 sintaxe XAML para definir propriedades, consulte XAML no 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 Background do botão para 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>

Definindo propriedades no código

Definir valores de propriedade de dependência no código normalmente é apenas uma chamada para a implementação de set exposta pelo "wrapper" 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 get "wrapper".

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

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

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

Funcionalidade de uma propriedade proporcionada por uma propriedade dependente

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 suporta 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 valor da propriedade Resources de um elemento raiz de página ou do aplicativo, uma vez que esses locais oferecem acesso conveniente ao recurso. Neste exemplo, definimos um recurso SolidColorBrush:

<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 propriedade Background:

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

No WPF XAML, você pode usar uma referência de recurso estático ou dinâmico. Este 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 de valor da propriedade de dependência.

Vinculação de dados

Uma propriedade de dependência pode fazer referência a um valor por meio da associaçã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 do objeto Binding no código. Com a associaçã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 propriedade Content para 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 ligação especifica a propriedade de origem na fonte de dados com 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 vinculação. Para obter detalhes, consulte a precedência de valor da propriedade de dependência .

As propriedades de dependência, ou a classe DependencyObject, não suportam nativamente INotifyPropertyChanged para notificação de alterações no valor da propriedade de origem para operações de vinculação de dados DependencyObject. 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. Os estilos são normalmente definidos como recursos em 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 dicionário Resources (não mostrado). Em seguida, esse estilo é aplicado diretamente à propriedade Style para um Button. O setter dentro do estilo define a propriedade Background para um Button estilizado 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, consulte Styling and templating.

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 propriedade Background de um Button. Tecnicamente, a sintaxe do elemento de propriedade define uma SolidColorBrush em branco como o Backgrounde a propriedade Color 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 como animar propriedades, consulte Visão geral da animação e Visão geral de 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 sobrescrição de metadados depende do identificador DependencyProperty 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 para uma propriedade de dependência DefaultStyleKey. A sobreposição de metadados para essa propriedade de dependência específica faz parte de um padrão de implementação para criar controles que possam utilizar estilos padrão dos 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 para uma propriedade de dependência.

Herança do valor da 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 da 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 Dependency Property Information para essa propriedade de dependência na referência do SDK.

O exemplo a seguir mostra uma associação que inclui a propriedade DataContext para especificar a origem da ligaçã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 o 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 mais informações, consulte o item Herança de valor de propriedade.

Integração com o designer do WPF

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

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 forma 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 à propriedade Background de qualquer botão, mas especifica um botão com uma propriedade Background definida localmente. Tecnicamente, esse botão tem sua propriedade Background definida duas vezes, embora apenas um valor se aplique — o valor com a maior precedência. Um valor definido localmente tem a maior precedência, exceto para uma animação em execução, que não existe aqui. Assim, o segundo botão usa o valor definido localmente para a propriedade Background, em vez do valor definido pelo estilo. O primeiro botão não tem valor local ou outro valor com precedência maior do que um definidor de estilo e, portanto, utiliza o valor do definidor de estilo para a propriedade Background.

<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 existe precedência de propriedade de dependência?

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

Observação

Um número de propriedades definidas em elementos WPF não são propriedades de dependência, porque as propriedades de dependência eram normalmente implementadas apenas 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.

Saiba mais sobre propriedades de dependência

  • Os desenvolvedores de componentes ou desenvolvedores de aplicativos podem querer 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 detetá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 do Common Language Runtime e não é necessariamente uma propriedade dependente. O principal objetivo de uma propriedade anexada é permitir que elementos filho relatem valores de propriedade para 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 é permitir que um elemento filho informe os elementos pai sobre como apresentá-los na interface de utilizador. Para obter exemplos, consulte Dock e Left. Para obter mais informações, consulte Visão geral das propriedades anexadas.

Ver também