Visão geral das propriedades de dependência

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. Em conjunto, esses serviços são normalmente conhecidos como o sistema de propriedades do WPF. Uma propriedade que tem o suporte do sistema de propriedades do WPF é conhecida como uma propriedade de dependência. Esta visão geral descreve o sistema de propriedades do WPF e as funcionalidades de uma propriedade de dependência. Isso inclui como usar as propriedades de dependência existentes no 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.

Pré-requisitos

Este tópico pressupõe que você tenha algum conhecimento básico sobre o sistema de tipos .NET e sobre programação orientada a objeto. Para seguir os exemplos deste tópico, você também deve ter noções básicas de XAML e saber como escrever aplicativos do WPF. Para obter mais informações, confira Passo a passo: Meu primeiro aplicativo de área de trabalho do WPF.

Propriedades de dependência e propriedades CLR

No WPF, as propriedades normalmente são expostas como propriedades padrão do .NET. Em um nível básico, você poderá interagir com essas propriedades diretamente e nunca saber que elas são implementadas como uma propriedade de dependência. No entanto, você deve se familiarizar com algumas ou todas as funcionalidades do sistema de propriedades do WPF para poder aproveitá-las.

A finalidade das propriedades de dependência é fornecer uma maneira para calcular o valor de uma propriedade com base no valor de outras entradas. Essas outras entradas podem incluir propriedades do sistema, como temas e preferências do usuário, mecanismos de determinação de propriedade Just-In-Time, como vinculação de dados e animações/storyboards, uso múltiplo de modelos, como recursos e estilos ou valores conhecidos por meio de relacionamentos pai-filho com outros elementos da árvore de elementos. Além disso, uma propriedade de dependência pode ser implementada para fornecer validação independente, valores padrão, retornos de chamada que monitoram alterações em outras propriedades e um sistema que pode impor valores da propriedade com base nas informações possivelmente em runtime. As classes derivadas também podem alterar algumas características específicas de uma propriedade existente, substituindo metadados de propriedades de dependência, em vez de substituir a implementação real das propriedades existentes ou criar novas propriedades.

Na referência do SDK, você pode identificar qual propriedade é uma propriedade de dependência pela presença da seção Informações sobre a propriedade de dependência na página de referência gerenciada dessa propriedade. A seção Informações sobre a propriedade de dependência inclui um link para o campo de identificador DependencyProperty da propriedade de dependência e também inclui uma lista das opções de metadados definidas 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 do WPF estendem a funcionalidade da propriedade fornecendo um tipo que dá suporte a uma propriedade, como uma implementação alternativa ao padrão de suporte da propriedade com um campo particular. O nome desse tipo é DependencyProperty. O outro tipo importante que define o sistema de propriedades do WPF é DependencyObject. DependencyObject define a classe base que pode registrar e ter uma propriedade de dependência.

A seguinte lista relaciona a terminologia usada com propriedades de dependência:

  • Propriedade de dependência: uma propriedade que tem o suporte de uma DependencyProperty.

  • Identificador de propriedade de dependência: uma instância de DependencyProperty, que é obtida como um valor retornado durante o registro de uma propriedade de dependência e, em seguida, armazenada como um membro estático de uma classe. Esse identificador é usado como um parâmetro para muitas das APIs que interagem com o sistema de propriedades do WPF.

  • “Wrapper” CLR: as implementações reais get e set da propriedade. Essas implementações incorporam o identificador de propriedade de dependência com seu uso em chamadas GetValue e SetValue, fornecendo o suporte para a propriedade usando o sistema de propriedades do WPF.

O exemplo a seguir define a propriedade de dependência IsSpinning e mostra a relação entre o identificador DependencyProperty e a propriedade à qual ele dá suporte.

public static readonly DependencyProperty IsSpinningProperty =
    DependencyProperty.Register(
    "IsSpinning", typeof(Boolean),
    typeof(MyCode)
    );
public bool IsSpinning
{
    get { return (bool)GetValue(IsSpinningProperty); }
    set { SetValue(IsSpinningProperty, value); }
}
Public Shared ReadOnly IsSpinningProperty As DependencyProperty =
    DependencyProperty.Register("IsSpinning",
                                GetType(Boolean),
                                GetType(MyCode))

Public Property IsSpinning() As Boolean
    Get
        Return CBool(GetValue(IsSpinningProperty))
    End Get
    Set(ByVal 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 as razões para seu uso, 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 de XAML a seguir especifica a cor da tela de fundo de um botão como vermelho. Este exemplo ilustra um caso em que o valor de cadeia de caracteres simples para um atributo XAML é o tipo convertido pelo analisador XAML do WPF em um tipo WPF (uma Color, por meio de um SolidColorBrush) no código gerado.

<Button Background="Red" Content="Button!"/>

O XAML dá suporte a uma variedade de formatos de sintaxe para a configuração de propriedades. A sintaxe a ser usada para determinada propriedade dependerá do tipo de valor usado por uma propriedade, bem como outros fatores, como a presença de um conversor de tipo. Para obter mais informações sobre a sintaxe XAML para configuração de propriedade, consulte XAML em WPF e sintaxe XAML em detalhes.

Como um exemplo de sintaxe não atributo, o exemplo de XAML a seguir mostra outra tela de fundo do botão. Desta vez, em vez de configurar uma cor sólida simples, a tela de fundo é definida como uma imagem, com um elemento que representa a imagem e a origem dessa imagem especificada como um atributo do elemento aninhado. Este é um exemplo de sintaxe de elemento de propriedade.

<Button Content="Button!">
  <Button.Background>
    <ImageBrush ImageSource="wavy.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 implementação definida exposta pelo "wrapper" do CLR.

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

Obter um valor da propriedade também é basicamente uma chamada à implementação get do “wrapper”:

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

Você também pode chamar as APIs GetValue do sistema de propriedades e SetValue diretamente. Isso normalmente não é necessário se você estiver usando propriedades existentes (os wrappers são mais convenientes e fornecem melhor exposição da propriedade para ferramentas de desenvolvedor), mas chamar as APIs diretamente é apropriado para determinados cenários.

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

Uma propriedade de dependência fornece uma funcionalidade que estende a funcionalidade de uma propriedade, em vez de uma propriedade que tem o suporte de um campo. Normalmente, essa funcionalidade representa ou dá suporte a uma das seguintes funcionalidades específicas:

Recursos

Um valor da propriedade de dependência pode ser definido com uma referência a um recurso. Normalmente, os recursos são especificados com o valor da propriedade Resources de um elemento raiz da página ou do aplicativo (essas localizações permitem o acesso mais conveniente ao recurso). O exemplo a seguir mostra como definir um recurso SolidColorBrush.

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

Depois de definir o recurso, você pode referenciar o recurso e usá-lo para fornecer um valor da propriedade:

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

Esse recurso específico é referenciado como uma Extensão de Marcação de DynamicResource (no XAML do WPF, é possível usar uma referência de recurso estático ou dinâmico). Para usar uma referência de recurso dinâmico, é necessário fazer a definição para uma propriedade de dependência e, portanto, é especificamente o uso da referência de recurso dinâmico que é habilitado pelo sistema de propriedades do WPF. Para obter mais informações, consulte Recursos XAML.

Observação

Os recursos serão tratados como um valor local, o que significa que, se você definir outro valor local, eliminará a referência do 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 da propriedade final é adiada até o tempo de execução, no qual o valor é obtido de uma fonte de dados.

O exemplo a seguir define a propriedade Content 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 de origem desejada por XPath na fonte de dados.

<Button Content="{Binding XPath=Team/@TeamName}"/>

Observação

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

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

Estilos

Estilos e modelos são dois dos principais cenários que motivam o uso de propriedades de dependência. Os estilos são particularmente úteis para definir propriedades que definem a interface do usuário (UI) do aplicativo. Normalmente, os estilos são definidos como recursos no XAML. Os estilos interagem com o sistema de propriedades porque geralmente contêm “setters” para propriedades específicas, bem como “gatilhos” que alteram um valor da propriedade com base no valor em tempo real de outra propriedade.

O exemplo a seguir cria um estilo simples (que seria definido dentro de um dicionário, não mostrado) e, em seguida, aplica esse estilo diretamente à propriedade de Style um ResourcesButton. 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}">I am green!</Button>

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

Animações

As propriedades de dependência podem ser animadas. Quando uma animação é aplicada e está em execução, o valor animado opera com uma precedência maior do que qualquer valor (como um valor local) que a propriedade, de outro modo, tem.

O exemplo a seguir anima a Background em uma propriedade Button (tecnicamente, a Background é animada usando a sintaxe de elemento de propriedade para especificar um SolidColorBrush em branco como a Background e, em seguida, a propriedade Color desse SolidColorBrush é a propriedade animada diretamente).

<Button>I am animated
  <Button.Background>
    <SolidColorBrush x:Name="AnimBrush"/>
  </Button.Background>
  <Button.Triggers>
    <EventTrigger RoutedEvent="Button.Loaded">
      <BeginStoryboard>
        <Storyboard>
          <ColorAnimation
            Storyboard.TargetName="AnimBrush" 
            Storyboard.TargetProperty="(SolidColorBrush.Color)"
            From="Red" To="Green" Duration="0:0:5" 
            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 dos storyboards.

Substituições de metadados

É possível alterar alguns comportamentos de uma propriedade de dependência substituindo os metadados dessa propriedade ao derivar da classe que originalmente registra a propriedade de dependência. A substituição dos metadados depende do identificador DependencyProperty. A substituição de metadados 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 que são herdadas de classes base por tipo.

O exemplo a seguir substitui os metadados de uma propriedade de dependência DefaultStyleKey. A substituição dos metadados dessa propriedade de dependência específica faz parte de um padrão de implementação que cria 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 obter metadados de propriedades, consulte Metadados de propriedades 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 do valor da propriedade não está habilitado globalmente em todas as propriedades de dependência, pois o tempo de cálculo da herança causa algum impacto de desempenho. A herança do valor da propriedade normalmente é habilitada apenas em propriedades nas quais um cenário específico sugere que a herança do valor da propriedade é apropriada. Você pode determinar se uma propriedade de dependência tem herança examinando a seção Informações da propriedade de dependência dessa propriedade de dependência na referência do SDK.

O exemplo a seguir mostra uma associação e define a propriedade DataContext que especifica a origem da associação, que não foi mostrada no exemplo de associação anterior. As associações seguintes nos objetos filho não precisam especificar a origem; elas podem usar o valor herdado do DataContext no objeto StackPanel pai. (Como alternativa, um objeto filho pode optar por especificar diretamente seu próprio DataContext ou uma Source na Binding e deliberadamente não usar o valor herdado para o contexto de dados de suas associações.)

<StackPanel Canvas.Top="50" DataContext="{Binding Source={StaticResource XmlTeamsSource}}">
  <Button Content="{Binding XPath=Team/@TeamName}"/>
</StackPanel>

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

Integração com o WPF Designer

Um controle personalizado com propriedades que são implementadas como propriedades de dependência receberá suporte apropriado do WPF Designer para Visual Studio. Um exemplo é a capacidade de editar propriedades de dependência diretas e anexadas com a janela Propriedades. Para obter mais informações, consulte Visão geral da criação de controle.

Precedência do valor da propriedade de dependência

Ao obter o valor de uma propriedade de dependência, possivelmente, você está obtendo um valor que foi definido nessa propriedade por meio de uma das outras entradas baseadas em propriedade que participam do sistema de propriedades do WPF. A precedência do valor da propriedade de dependência existe para que uma variedade de cenários de como as propriedades obtêm seus valores possam interagir de forma previsível.

Considere o exemplo a seguir. O exemplo inclui um estilo que se aplica a todos os botões e suas propriedades Background, mas, em seguida, especifica também um botão com um valor Background definido localmente.

Observação

A documentação do SDK usa os termos “valor local” ou “valor definido localmente” ocasionalmente quando trata de propriedades de dependência. Um valor definido localmente é um valor da propriedade definido diretamente em uma instância de objeto no código ou como um atributo em um elemento no XAML.

Em princípio, no primeiro botão, a propriedade é definida duas vezes, mas apenas um valor é aplicado: o valor com a precedência mais alta. Um valor definido localmente tem a precedência mais alta (exceto em uma animação em execução; porém, nenhuma animação se aplica neste exemplo) e, portanto, em vez do valor de setter de estilo, o valor definido localmente é usado para a tela de fundo no primeiro botão. O segundo botão não tem nenhum valor local (e nenhum outro valor com uma precedência mais alta do que um setter de estilo) e, portanto, a tela de fundo do botão é obtida do setter de estilo.

<StackPanel>
  <StackPanel.Resources>
    <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
     <Setter Property="Background" Value="Red"/>
    </Style>
  </StackPanel.Resources>
  <Button Background="Green">I am NOT red!</Button>
  <Button>I am styled red</Button>
</StackPanel>

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

Normalmente, você não gostaria que os estilos sempre se aplicassem e obscurecessem até mesmo um valor definido localmente de um elemento individual (caso contrário, seria difícil usar estilos ou elementos em geral). Portanto, os valores obtidos de estilos operam com uma precedência mais baixa que o valor definido localmente. Para obter uma lista mais completa das propriedades de dependência e da possível origem de um valor efetivo de propriedade de dependência, consulte Precedência do valor da propriedade de dependência.

Observação

Há uma série de propriedades definidas em elementos do WPF que não são propriedades de dependência. De modo geral, as propriedades foram implementadas como propriedades de dependência somente quando houve a necessidade de dar suporte a, pelo menos, um dos cenários habilitados pelo sistema de propriedades: vinculação de dados, estilos, animação, suporte ao valor padrão, herança, propriedades anexadas ou invalidação.

Mais informações sobre propriedades 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 CLR (Common Language Runtime) e não é necessariamente uma propriedade de dependência. A finalidade típica 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 possuam essa propriedade como parte das listagens de membros da classe. Um cenário principal é permitir que os elementos filho informem ao pai como eles devem ser apresentados na interface do usuário; Para obter um exemplo, consulte Dock ou Left. Para obter detalhes, consulte Visão geral das propriedades anexadas.

  • Os desenvolvedores de componentes ou de aplicativos podem desejar criar sua própria propriedade de dependência, a fim de habilitar funcionalidades como vinculação de dados ou suporte a estilos ou para o suporte à invalidação e coerção de valor. Para obter detalhes, consulte Propriedades de dependência personalizadas.

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

Confira também