Partilhar via


Propriedades de Dependência Personalizada

Este tópico descreve as razões que desenvolvedores de aplicativos e autores de componentes Windows Presentation Foundation (WPF) podem querer criar propriedade de dependência personalizada, e descreve os passos da implementação assim como algumas opções de implementação que podem melhorar a performance, usabilidade, ou versatilidade da propriedade.

Este tópico contém as seguintes seções.

  • Pré-requisitos
  • O que é uma Propriedade de Dependência?
  • Exemplos de Propriedades de Dependência
  • Quando Você Deve Implementar uma Propriedade de Dependência?
  • Passos Necessários para Definir uma Propriedade de Dependência
  • Read-Only Dependency Properties
  • Propriedade de Dependência de Tipos de Coleção
  • Considerações de Segurança de Propriedade de Dependência
  • Propriedades de Dependência e Construtores de Classe
  • Tópicos relacionados

Pré-requisitos

Este tópico assume que você entende propriedades de dependência de uma perspectiva de um consumidor de propriedades de dependência existentes em classes WPF , e já leu o tópico Visão geral sobre propriedades de dependência. Para que você siga os exemplos deste tópico, você também deve entender Extensible Application Markup Language (XAML) e saber como escrever aplicações WPF.

O que é uma Propriedade de Dependência?

Você pode habilitar o que de outra forma seria uma propriedade common language runtime (CLR) para suportar estilização, associação de dados, herança, animação, e valores padrão ao implementá-las como propriedade de dependência. Propriedades de dependência são propriedades que são registradas com o sistema de propriedade WPF chamando o método Register (ou RegisterReadOnly), e que são suportados por um campo identificador DependencyProperty. Propriedades de dependência podem ser usadas somente por tipos DependencyObject , mas DependencyObject é bem alto na hierarquia de classes WPF , então a maioria das classes disponíveis em WPF podem suportar propriedades de dependência. Para mais informações sobre propriedades de dependência e algumas das terminologias e convenções usadas para descrevê-las neste SDK, consulte Visão geral sobre propriedades de dependência.

Exemplos de Propriedades de Dependência

Exemplos de propriedades de dependência que são implementadas nas classes WPF incluem a propriedade Background, a propriedade Width , e a propriedade Text, entre várias outras. Cada propriedade de dependência exposta por uma classe tem um campo estático público correspondente do tipo DependencyProperty exposto na mesma classe. Isso é o identificador para a propriedade de dependência. O identificador é denominado usando uma convenção: o nome do propriedade de dependência com a seqüência de caracteres Property anexado a ele. Por exemplo, o campo identificador correspondente DependencyProperty para a propriedade Background é BackgroundProperty. O identificador armazena a informação sobre a propriedade de dependência da forma que ela foi registrada, e o identificador é então usado posteriormente para outras operações envolvendo a propriedade de dependência, tal como chamar SetValue.

Como mencionado em Visão geral sobre propriedades de dependência, todas propriedade de dependência em WPF (exceto a maioria das propriedades anexas) também são propriedades CLR por causa da implementação do wrapper. Portanto, do código, você pode pegar ou definir propriedade de dependência chamando assessores CLR que definem os wrappers da mesma maneira que você usaria outra propriedade CLR. Como um consumidor de propriedade de dependência estabelecidas, você não usa tipicamente os métodos DependencyObject GetValue e SetValue, que são os pontos de conexão para o sistema de propriedades subjacente. Ao invés disso, a implementação existente das propriedades CLR terão chamado GetValue e SetValue dentro das implementações wrapper get e set da propriedade, usando o campo identificador de modo adequado. Se você está implementando uma propriedade de dependência personalizada, então você irá definir um wrapper de maneira similar.

Quando Você Deve Implementar uma Propriedade de Dependência?

Quando você implementa uma propriedade numa classe, desde que sua classe derive de DependencyObject, você tem a opção de suportar sua propriedade com um identificador DependencyProperty e portanto torná-la uma propriedade de dependência. Fazer sua propriedade uma propriedade de dependência não é sempre necessário ou apropriado, e isto depende das necessidades do seu cenário. As vezes, a técnica típica de suportar sua propriedade com um campo privado é adequado. Entretanto, se você deve implementar sua propriedade como propriedade de dependência sempre que você quiser que sua propriedade suporte uma ou mais das seguintes funcionalidades WPF:

  • Você quer que sua propriedade seja definível em um estilo. Para obter mais informações, consulte Styling and Templating.

  • Você quer que sua propriedade suporte associação de dados. Para mais informações sobre associação de dados de propriedade de dependência, consulte Como: Bind the Properties of Two Controls.

  • Você quer que sua propriedade seja definível com uma referência de recurso dinâmico. Para obter mais informações, consulte Visão geral sobre Recursos.

  • Você quer herdar o valor da propriedade automaticamente de um elemento pai na árvore de elementos. Neste caso, registre com o método RegisterAttached , mesmo que você também crie o wrapper da propriedade para o acesso CLR. Para obter mais informações, consulte Herança de Valor de Propriedade.

  • Você quer que sua propriedade seja animável. Para obter mais informações, consulte Revisão de Animação.

  • Você quer que o sistema de propriedades reporte quando o valor anterior da propriedade mudou por ação tomada pelo sistema de propriedade, pelo ambiente, ou pelo usuário, ou pela leitura e uso de estilos. Usando meta dados de propriedade, sua propriedade pode especificar um método callback que será invocado cada vez que o sistema de propriedades determine que seu valor de propriedade foi definitivamente mudado. Um conceito relacionado é coerção de valor da propriedade. Para obter mais informações, consulte Validação e Callbacks de Propriedade de Dependência.

  • Você quer usar uma convenção estabelecida de meta dados também é usada por processos WPF , tal como reportar se uma mudança no valor da propriedade deva requisitar ao sistema de layout para recompor a visualização de um elemento. Ou você quer ser capaz de sobrepor meta dados de forma que classes derivadas possam modificar características baseadas em meta dados tal como o valor padrão.

  • You want properties of a custom control to receive Visual Studio 2008 WPF Designer support, such as Properties window editing. Para obter mais informações, consulte Visão geral sobre criação de controles.

Quando você examina esses cenários, você também deve considerar se você pode alcançar seu cenário sobrepondo os meta dados de uma propriedade de dependência existente, ao invés de implementar uma propriedade completamente nova. Se uma sobreposição de meta dados é prática depende do seu cenário e quão próximo esse cenário lembra a implementação nas propriedade de dependência e classes WPF existentes. Para mais informações sobre sobreposição de meta dados em propriedades existentes, consulte Metadados de Propriedade de Dependência.

Passos Necessários para Definir uma Propriedade de Dependência

Definir uma propriedade de dependência consiste de quatro conceitos distintos. Esses conceitos não são necessariamente estritamente passos procedurais, porque alguns deles acabam sendo combinados em linhas únicas de código na implementação:

  • (Opcional) Crie meta dados de propriedade para a propriedade de dependência.

  • Registre o nome da propriedade com o sistema de propriedades, especificando o tipo dono e o tipo do valor da propriedade. Também especifique os meta dados da propriedade, se usados.

  • Define um identificador DependencyProperty como um campo public static readonly no tipo dono.

  • Define uma propriedade wrapper CLR cujo nome corresponde ao nome da propriedade de dependência. Implementar o CLRpropriedade "empacotador" get e set acessadores para se conectar com a propriedade de dependência que ele faz.

Registrando a Propriedade com o Sistema de Propriedades

Para sua propriedade ser uma propriedade de dependência, você deve registrar a propriedade na tabela mantida pelo sistema de propriedades, e dá-la um identificador único que é usado como o qualificador para operações posteriores do sistema de propriedades. Essas operações podem ser operações internas, ou o seu próprio código chamando o sistema de propriedades APIs. Para registrar a propriedade, você chama o método Register dentro do corpo de sua classe (dentro da classe, mas fora de qualquer definição de membros). O campo identificante também é fornecido pela chamada do método Register , como um valor de retorno. A razão para que a chamada Register é realizada fora das definições de outros membros é porque você usa esse valor de retorno para atribuir e criar um campo de tipo public static readonly DependencyProperty como parte de sua classe. Esse campo se torna o identificador para sua propriedade de dependência.

public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
  "AquariumGraphic",
  typeof(Uri),
  typeof(AquariumObject),
  new FrameworkPropertyMetadata(null,
      FrameworkPropertyMetadataOptions.AffectsRender, 
      new PropertyChangedCallback(OnUriChanged)
  )
);

Convenções de Nome de Propriedade de Dependência

Existem convenções estabelecidas de nomeação de propriedade de dependência que você deve seguir sempre, exceto em circunstâncias excepcionais.

A propriedade de dependência por si só terá um nome básico, "AquariumGraphic" como neste exemplo, o qual é dado como o primeiro parâmetro de Register. O nome deve ser único dentro de cada tipo registrante. Propriedade de dependência herdadas dos tipos base são considerados como partes do tipo registrante; nomes de propriedades herdadas não podem ser registrados novamente. Entretanto, existe uma técnica para adicionar uma classe como dona da propriedade de dependência mesmo quando aquela propriedade de dependência não é herdada; para detalhes, consulte Metadados de Propriedade de Dependência.

Quando você cria um campo identificador, nomeie este campo pelo nome da propriedade como você a registrou mais o sufixo Property. Este campo é seu identificador para a propriedade de dependência, e será usado mais tarde como entrada para as chamadas SetValue e GetValue que você irá realizar nos wrappers, em qualquer outro código de acesso à propriedade em seu próprio código, ou em qualquer código externo que você permita, ou pelo sistema de propriedades, e potencialmente por processadores XAML.

ObservaçãoObservação:

Definindo a propriedade de dependência no corpo da classe é a implementação típica, mas também é possível definir a propriedade de dependência no construtor da classe estática. Essa abordagem pode fazer sentido se você precisa de mais de uma linha de código para inicializar a propriedade de dependência.

Implementando o Wrapper

Sua implementação do wrapper deve chamar GetValue na implementação do get , e SetValue na implementação do set (a chamada do registro original e o campo são mostrados aqui para melhor compreensão).

Em casos não excepcionais, suas implementações de wrapper devem realizar somente as ações GetValue e SetValue , respectivamente. A razão para isto é discutida no tópico Carregando XAML e Propriedades de Dependência.

Todas as propriedades de dependência pública existentes que são fornecidas nas classes WPF usam este modelo simples de implementação de wrapper; a maioria da complexidade em como as propriedades de dependência funcionam é inerente ao comportamento do sistema de propriedades, ou é implementada por meio de outros conceitos tais como coerção ou callbacks de mudanças de propriedade por meio de meta dados de propriedades.

public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
  "AquariumGraphic",
  typeof(Uri),
  typeof(AquariumObject),
  new FrameworkPropertyMetadata(null,
      FrameworkPropertyMetadataOptions.AffectsRender, 
      new PropertyChangedCallback(OnUriChanged)
  )
);
public Uri AquariumGraphic
{
  get { return (Uri)GetValue(AquariumGraphicProperty); }
  set { SetValue(AquariumGraphicProperty, value); }
}

Novamente, por convenção, o nome da propriedade wrapper deve ser o mesmo nome escolhido e dado como o primeiro parâmetro da chamada Register que registrou a propriedade. Se sua propriedade não atende a convenção, isso não necessariamente desabilita todos os usos possíveis, mas você encontrará vários casos notáveis:

  • Certos aspectos de estilos e modelos não funcionarão.

  • A maioria das ferramentas e desenvolvedores devem depender na convenção de nomes para serealizar adequadamente XAML, ou prover assistência de ambiente de desenvolvedor em nível de propriedade.

  • A implementação atual do carregador WPF XAML sobrepassa o wrapper inteiramente, e depende na convenção de nomes quando processando os valores dos atributos. Para obter mais informações, consulte Carregando XAML e Propriedades de Dependência.

Meta dados Adequados para uma Nova Propriedade de Dependência

Quando você registra uma propriedade de dependência, o registro por meio do sistema de propriedades cria um objeto de meta dados que armazena as características da propriedade. Muitas dessas características têm padrões que são definidos se a propriedade é registrada com as assinaturas simples de Register. Outras assinaturas de Register permitem que você especifique os meta dados que você quer quando você registra a propriedade. Os meta dados mais comuns dados para propriedade de dependência é dá-los um valor padrão que é aplicado em novas instâncias que usam a propriedade.

Se você está criando a propriedade de dependência que existe em uma classe derivada de FrameworkElement, você pode usar a classe de meta dados mais especializada FrameworkPropertyMetadata ao invés da classe base PropertyMetadata. O construtor para a classe FrameworkPropertyMetadata possui várias assinaturas onde você pode especificar várias características de meta dados em combinação. Se você quer especificar somente o valor padrão, use a assinatura que recebe o parâmetro único do tipo Object. Passe o parâmetro de objeto como um valor padrão de tipo específico para sua propriedade (o valor padrão fornecido deve ser do tipo que você forneceu como o parâmetro propertyType na chamada Register).

Para FrameworkPropertyMetadata, você também pode especificar sinalizadores de opção de meta dados para sua propriedade. Esses sinalizadores são convertidos em propriedades discretas nos meta dados de propriedades após o registro e são usados para comunicar certas condições para outros processos como o gerenciador de layout.

Definindo Sinalizadores de Meta dados Apropriados.

  • Se sua propriedade (ou alterações em seu valor) afeta o interface do usuário (UI)e em particular afeta como o sistema de layout deve dimensionar ou processar o elemento em uma página, conjunto um ou mais dos seguintes sinalizadores: AffectsMeasure, AffectsArrange, AffectsRender.

    • AffectsMeasure indica que uma mudança nesta propriedade requer uma mudança na renderização UI onde o objeto contêiner possa requerer mais ou menos espaço dentro do pai. Por exemplo, uma propriedade "Largura" deve ter essa sinalização definida.

    • AffectsArrange indica que uma mudança nesta propriedade requer uma mudança na renderização UI que tipicamente não necessita de mudança no espaço dedicado, mas indica que o posicionamento dentro do espaço foi modificado. Por exemplo, uma propriedade "Alinhamento" deve ter esta sinalização definida.

    • AffectsRender indica que alguma outra mudança ocorreu que não irá afetar o layout ou medida, mas não requer outro renderizador. Um exemplo seria a propriedade que muda a cor de um elemento existente, como "Pano de Fundo".

    • Essas sinalizações são frequentemente utilizadas como um protocolo nos meta dados para suas próprias implementações das substituições do sistema de propriedades ou callbacks de layout. Por exemplo, você pode ter um callback OnPropertyChanged que irá chamar InvalidateArrange se alguma propriedade da instância reportar uma mudança de valor e tem AffectsArrange como true em seus meta dados.

  • Algumas propriedades podem afetar as características de renderização do elemento pai contido, de uma maneira geral as mudanças no espaço requerido mencionado anteriormente. Um exemplo é a propriedade MinOrphanLines usada no modelo de documento de fluxo, onde mudanças nesta propriedade podem mudar a renderização como um todo do documento de fluxo que contém os parágrafos. Use AffectsParentArrange ou AffectsParentMeasure para identificar casos similares em suas próprias propriedades.

  • Por padrão, propriedades de dependência suportando associação de dados. Você pode deliberadamente desabilitar associação de dados, para casos onde não há cenário realista para associação de dados, ou quando a performance na associação de dados para objetos grandes é identificada como um problema.

  • Por padrão, a associação de dados Mode para propriedades de dependência é OneWay. Você sempre pode modificar a associação para ser TwoWay por instância de associação; para detalhes, consulte Como: Especificar a Direção do Vínculo. Porém, como o autor de propriedade de dependência, você pode escolher fazer a propriedade usar o modo associação TwoWay por padrão. Um exemplo de uma propriedade de dependência existente é MenuItem.IsSubmenuOpen; o cenário para esta propriedade é que a definição lógica IsSubmenuOpen e a composição do MenuItem interagem com o estilo de tema padrão. A propriedade lógica IsSubmenuOpen usa associação de dados nativamente para manter o estado da propriedade de acordo com o estado das propriedades e das chamadas de método. Outro exemplo de propriedade que associa TwoWay por padrão é TextBox.Text.

  • Você também pode habilitar a herança de propriedade em uma propriedade de dependência personalizada ao definir a sinalização Inherits. Heranças de propriedades é útil em cenários onde os elementos pai e elementos filhos possuem propriedades em comum, e isto faz sentido para os elementos filhos terem aquele valor de propriedade particular definido para o mesmo valor que o pai definiu. Um exemplo de propriedade herdável é DataContext, que é usada para operações de associação para permitir o importante cenário detalhe-mestre para apresentação de dados. Ao fazer DataContext herdável, qualquer elemento filho também herda aquele contexto de dados. Causa de herança de valor da propriedade, você pode especificar um contexto de dados na raiz do aplicativo ou página e não precisará respecify-lo para ligações em todos os elementos filho possíveis. DataContext também é um mercadoria exemplo para ilustrar que a herança substitui o valor padrão, mas ela pode sempre ser definido localmente em qualquer elemento filho específico; para obter detalhes, consulte Como: Use the Master-Detail Pattern with Hierarchical Data. Herança de valor de propriedade tem um possível custo de performance, e portanto deve ser usado pouco; para detalhes, consulte Herança de Valor de Propriedade.

  • Defina a bandeira Journal para indicar se sua propriedade de dependência deva ser detectada ou usada para navegação em serviços de journaling. Um exemplo é a propriedade SelectedIndex; qualquer item selecionado em um controle de seleção deve ser persistido quando o histórico do journaling é navegado.

Read-Only Dependency Properties

Você pode definir uma propriedade de dependência que é somente leitura. Entretanto, os cenários pelos quais você definiria sua propriedade como somente leitura são de certo modo diferentes, assim como é o procedimento para registrá-las no sistema de propriedades e exposição de identificadores. Para obter mais informações, consulte Read-Only Dependency Properties.

Propriedade de Dependência de Tipos de Coleção

Propriedade de dependência de Tipos de Coleção têm algumas questões adicionais de implementação a serem consideradas. Para obter detalhes, consulte:Propriedade de Dependência de Tipos de Coleção.

Considerações de Segurança de Propriedade de Dependência

Propriedade de dependência devem ser declaradas como propriedades públicas. Campos identificadores de propriedade de dependência devem ser declarados como campos públicos estáticos. Mesmo que você tente declarar outros níveis de acesso (tal como protegido), uma propriedade de dependência sempre pode ser acessada por meio do identificador em combinação com o sistema de propriedades APIs. Mesmo um campo identificador protegido é potencialmente acessível por causa das reportagens dos meta dados ou determinação de valor APIs que é parte do sistema de propriedades, tal como LocalValueEnumerator. Para obter mais informações, consulte Segurança de Propriedades de Dependência.

Propriedades de Dependência e Construtores de Classe

Existe um princípio geral na programação de código gerenciado (frequentemente policiado por ferramentas de análise de código como o FxCop) que construtores de classes não devem chamar métodos virtuais. Isto é porque construtores podem ser chamados como inicialização base de um construtor de classe derivada, e entrar no método virtual por meio do construtor por incorrer em um estado de inicialização incompleta da instância do objeto sendo construído. Quando você deriva de alguma classe que já deriva de DependencyObject, você deve sempre estar atento que o sistema de propriedades chama e expõe os métodos virtuais internamente. Esses métodos virtuais são partes do serviços do sistema de propriedades WPF. Substituir os métodos permite derivar classes para participar na determinação do valor. Para evitar potenciais problemas na inicialização em tempo de execução, você não deve definir valores de propriedade de dependência dentro dos construtores de classes, a não ser que você siga um padrão muito específico de construtor. Para obter detalhes, consulte:Safe Constructor Patterns for DependencyObjects.

Consulte também

Conceitos

Visão geral sobre propriedades de dependência

Metadados de Propriedade de Dependência

Visão geral sobre criação de controles

Propriedade de Dependência de Tipos de Coleção

Segurança de Propriedades de Dependência

Carregando XAML e Propriedades de Dependência

Safe Constructor Patterns for DependencyObjects