Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Desenvolvedores de aplicativos e autores de componentes do WPF (Windows Presentation Foundation) podem criar propriedades de dependência personalizadas para estender a funcionalidade de suas propriedades. Ao contrário de uma propriedade CLR (Common Language Runtime), uma propriedade de dependência adiciona suporte para estilo, associação de dados, herança, animações e valores padrão. Background, Widthe Text são exemplos de propriedades de dependência existentes em classes WPF. Este artigo descreve como implementar propriedades de dependência personalizadas e apresenta opções para melhorar o desempenho, a usabilidade e a versatilidade.
Pré-requisitos
O artigo pressupõe um conhecimento básico das propriedades de dependência e que você leu visão geral das propriedades de dependência. Para seguir os exemplos neste artigo, ele ajuda se você estiver familiarizado com XAML (Extensible Application Markup Language) e saber como escrever aplicativos WPF.
Identificador de propriedade de dependência
As propriedades de dependência são propriedades registradas no sistema de propriedades do WPF por meio de chamadas Register ou RegisterReadOnly. O Register método retorna uma DependencyProperty instância que contém o nome registrado e as características de uma propriedade de dependência. Você atribuirá a DependencyProperty instância a um campo readonly estático, conhecido como identificador de propriedade de dependência, que por convenção é nomeado <property name>Property. Por exemplo, o campo identificador da Background propriedade é sempre BackgroundProperty.
O identificador de propriedade de dependência é usado como um campo de backup para obter ou definir valores de propriedade, em vez do padrão padrão de fazer backup de uma propriedade com um campo privado. Não só o sistema de propriedades usa o identificador, os processadores XAML podem usá-lo e seu código (e possivelmente código externo) pode acessar propriedades de dependência por meio de seus identificadores.
As propriedades de dependência só podem ser aplicadas a classes derivadas de DependencyObject tipos. A maioria das classes do WPF dá suporte a propriedades de dependência, pois DependencyObject está próxima à raiz da hierarquia de classes do WPF. Para obter mais informações sobre as propriedades de dependência e a terminologia e as convenções usadas para descrevê-las, consulte a visão geral das propriedades de dependência.
Encapsulamentos de propriedade de dependência
As propriedades de dependência do WPF que não são propriedades anexadas são expostas por um wrapper CLR que implementa get e set acessadores. Usando um wrapper de propriedade, consumidores podem obter ou definir valores de propriedades de dependência da mesma forma que fariam com qualquer outra propriedade CLR. Os acessadores get e set interagem com o sistema de propriedades subjacente por meio de chamadas DependencyObject.GetValue e DependencyObject.SetValue, passando o identificador de propriedade de dependência como um parâmetro. Os consumidores de propriedades de dependência normalmente não chamam os métodos GetValue ou SetValue diretamente, mas se você estiver implementando uma propriedade de dependência personalizada, usará esses métodos no wrapper.
Quando implementar uma propriedade de dependência
Quando você implementa uma propriedade em uma classe que deriva de DependencyObject, você a torna uma propriedade de dependência ao associá-la a um identificador DependencyProperty. Se é benéfico criar uma propriedade de dependência depende do seu cenário. Embora o backup de sua propriedade com um campo privado seja adequado para alguns cenários, considere implementar uma propriedade de dependência se você quiser que sua propriedade dê suporte a um ou mais dos seguintes recursos do WPF:
Propriedades que são configuráveis em um estilo. Para obter mais informações, consulte Estilos e modelos.
Propriedades que dão suporte à associação de dados. Para obter mais informações sobre propriedades de dependência de associação de dados, consulte Associar as propriedades de dois controles
Propriedades que são configuráveis por meio de referências de recursos dinâmicos. Para obter mais informações, consulte recursos XAML.
Propriedades que herdam automaticamente seu valor de um elemento pai na árvore de elementos. Para isso, você precisará se registrar usando RegisterAttached, mesmo que também crie um wrapper de propriedade para acesso clr. Para obter mais informações, consulte Herança do valor da propriedade.
Propriedades que são animatáveis. Para obter mais informações, consulte a visão geral da animação
Notificação pelo sistema de propriedades do WPF quando um valor de propriedade é alterado. As alterações podem ocorrer devido a ações do sistema de propriedades, ambiente, usuário ou estilos. Sua propriedade pode especificar uma função de callback nos metadados da propriedade, que será invocada sempre que o sistema de propriedades determinar que o valor da sua propriedade mudou. Um conceito relacionado é a coerção do valor da propriedade. Para obter mais informações, consulte funções de retorno e validação da propriedade de dependência.
Acesso aos metadados de propriedade de dependência, que são lidos pelos processos do WPF. Por exemplo, você pode usar metadados de propriedade para:
Especifique se um valor de propriedade de dependência alterado deve fazer com que o sistema de layout recompose visuais para um elemento.
Defina o valor padrão de uma propriedade de dependência substituindo metadados em classes derivadas.
Suporte ao designer WPF do Visual Studio, como editar as propriedades de um controle personalizado na janela Propriedades . Para obter mais informações, consulte a visão geral da criação de controle
Para alguns cenários, substituir os metadados de uma propriedade de dependência existente é uma opção melhor do que implementar uma nova propriedade de dependência. Se uma substituição de metadados é prática depende do seu cenário e de quão próximo esse cenário se assemelha à implementação de propriedades e classes de dependência do WPF existentes. Para obter mais informações sobre como substituir metadados em propriedades de dependência existentes, consulte Metadados da Propriedade de Dependência.
Lista de verificação para criar uma propriedade de dependência
Siga estas etapas para criar uma propriedade de dependência. Algumas das etapas podem ser combinadas e implementadas em uma única linha de código.
(Opcional) Criar metadados de propriedade de dependência.
Registre a propriedade de dependência com o sistema de propriedades, especificando um nome de propriedade, um tipo de proprietário, o tipo de valor da propriedade e, opcionalmente, metadados de propriedade.
Defina um identificador DependencyProperty como um campo
public static readonlyno tipo proprietário. O nome do campo do identificador é o nome da propriedade com o sufixoPropertyacrescentado.Defina uma propriedade de wrapper CLR com o mesmo nome do nome da propriedade de dependência. No wrapper CLR, implemente
getesetacessadores que se conectam com a propriedade de dependência associada ao wrapper.
Registrando a propriedade
Para que sua propriedade seja uma propriedade de dependência, você deve registrá-la no sistema de propriedades. Para registrar sua propriedade, chame o Register método de dentro do corpo da sua classe, mas fora de qualquer definição de membro. O Register método retorna um identificador de propriedade de dependência exclusivo que você usará ao chamar a API do sistema de propriedades. O motivo pelo qual a Register chamada é feita fora das definições de membro é porque você atribui o valor retornado a um public static readonly campo do tipo DependencyProperty. Esse campo, que você criará em sua classe, é o identificador para sua propriedade de dependência. No exemplo a seguir, o primeiro argumento de Register nomeia a propriedade de dependência AquariumGraphic.
// Register a dependency property with the specified property name,
// property type, owner type, and property metadata. Store the dependency
// property identifier as a public static readonly member of the class.
public static readonly DependencyProperty AquariumGraphicProperty =
DependencyProperty.Register(
name: "AquariumGraphic",
propertyType: typeof(Uri),
ownerType: typeof(Aquarium),
typeMetadata: new FrameworkPropertyMetadata(
defaultValue: new Uri("http://www.contoso.com/aquarium-graphic.jpg"),
flags: FrameworkPropertyMetadataOptions.AffectsRender,
propertyChangedCallback: new PropertyChangedCallback(OnUriChanged))
);
' Register a dependency property with the specified property name,
' property type, owner type, and property metadata. Store the dependency
' property identifier as a public static readonly member of the class.
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty =
DependencyProperty.Register(
name:="AquariumGraphic",
propertyType:=GetType(Uri),
ownerType:=GetType(Aquarium),
typeMetadata:=New FrameworkPropertyMetadata(
defaultValue:=New Uri("http://www.contoso.com/aquarium-graphic.jpg"),
flags:=FrameworkPropertyMetadataOptions.AffectsRender,
propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnUriChanged)))
Observação
Definir a propriedade de dependência no corpo da classe é a implementação típica, mas também é possível definir uma propriedade de dependência no construtor estático de classe. Essa abordagem poderá fazer sentido se você precisar de mais de uma linha de código para inicializar a propriedade de dependência.
Nomenclatura de propriedade de dependência
A convenção de nomenclatura estabelecida para propriedades de dependência é obrigatória para o comportamento normal do sistema de propriedades. O nome do campo de identificador que você cria deve ser o nome registrado da propriedade com o sufixo Property.
Um nome de propriedade de dependência deve ser exclusivo dentro da classe de registro. As propriedades de dependência herdadas por meio de um tipo base já foram registradas e não podem ser registradas por um tipo derivado. No entanto, você pode usar uma propriedade de dependência que foi registrada por um tipo diferente, mesmo um tipo do qual sua classe não herda, adicionando sua classe como proprietário da propriedade de dependência. Para obter mais informações sobre como adicionar uma classe como proprietário, consulte metadados de propriedade de dependência.
Implementando um envolvedor de propriedade
Por convenção, o nome da propriedade wrapper deve ser o mesmo que o primeiro parâmetro da Register chamada, que é o nome da propriedade de dependência. A implementação do wrapper fará chamadas para GetValue no acessador get e para SetValue no acessador set (para propriedades de leitura/gravação). O exemplo a seguir mostra um wrapper após a chamada de registro e a declaração de campo do identificador. Todas as propriedades de dependência pública em classes WPF usam um modelo de wrapper semelhante.
// Register a dependency property with the specified property name,
// property type, owner type, and property metadata. Store the dependency
// property identifier as a public static readonly member of the class.
public static readonly DependencyProperty AquariumGraphicProperty =
DependencyProperty.Register(
name: "AquariumGraphic",
propertyType: typeof(Uri),
ownerType: typeof(Aquarium),
typeMetadata: new FrameworkPropertyMetadata(
defaultValue: new Uri("http://www.contoso.com/aquarium-graphic.jpg"),
flags: FrameworkPropertyMetadataOptions.AffectsRender,
propertyChangedCallback: new PropertyChangedCallback(OnUriChanged))
);
// Declare a read-write property wrapper.
public Uri AquariumGraphic
{
get => (Uri)GetValue(AquariumGraphicProperty);
set => SetValue(AquariumGraphicProperty, value);
}
' Register a dependency property with the specified property name,
' property type, owner type, and property metadata. Store the dependency
' property identifier as a public static readonly member of the class.
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty =
DependencyProperty.Register(
name:="AquariumGraphic",
propertyType:=GetType(Uri),
ownerType:=GetType(Aquarium),
typeMetadata:=New FrameworkPropertyMetadata(
defaultValue:=New Uri("http://www.contoso.com/aquarium-graphic.jpg"),
flags:=FrameworkPropertyMetadataOptions.AffectsRender,
propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnUriChanged)))
' Declare a read-write property wrapper.
Public Property AquariumGraphic As Uri
Get
Return CType(GetValue(AquariumGraphicProperty), Uri)
End Get
Set
SetValue(AquariumGraphicProperty, Value)
End Set
End Property
Exceto em casos raros, a implementação do wrapper só deve conter GetValue e SetValue código. Pelos motivos por trás disso, consulte Implicações para propriedades de dependência personalizadas.
Se sua propriedade não seguir as convenções de nomenclatura estabelecidas, você poderá encontrar esses problemas:
Alguns aspectos de estilos e modelos não funcionarão.
A maioria das ferramentas e designers depende das convenções de nomenclatura para serializar corretamente o XAML e fornecer assistência ao ambiente do designer em um nível por propriedade.
A implementação atual do carregador XAML do WPF ignora totalmente os wrappers e depende da convenção de nomenclatura para processar valores de atributo. Para obter mais informações, consulte as propriedades de carregamento e dependência XAML.
Metadados de propriedade de dependência
Quando você registra uma propriedade de dependência, o sistema de propriedades cria um objeto de metadados para armazenar características de propriedade. As sobrecargas do Register método permitem que você especifique metadados de propriedade durante o registro, por exemplo Register(String, Type, Type, PropertyMetadata). Um uso comum de metadados de propriedade é aplicar um valor padrão personalizado para novas instâncias que usam uma propriedade de dependência. Se você não fornecer metadados de propriedade, o sistema de propriedades atribuirá valores padrão a muitas das características da propriedade de dependência.
Se você estiver criando uma propriedade de dependência em uma classe derivada FrameworkElement, poderá usar a classe FrameworkPropertyMetadata de metadados mais especializada em vez de sua classe PropertyMetadatabase. Várias FrameworkPropertyMetadata assinaturas de construtor permitem especificar diferentes combinações de características de metadados. Se você quiser apenas especificar um valor padrão, use FrameworkPropertyMetadata(Object) e passe o valor padrão para o Object parâmetro. Verifique se o tipo de valor corresponde ao propertyType especificado na Register chamada.
Algumas FrameworkPropertyMetadata sobrecargas permitem especificar sinalizadores de opção de metadados para sua propriedade. O sistema de propriedades converte esses sinalizadores em propriedades discretas e os valores dos sinalizadores são usados pelos processos do WPF, como o motor de layout.
Definindo sinalizadores de metadados
Considere o seguinte ao definir sinalizadores de metadados:
Se o valor da propriedade (ou alterações nele) afetar como o sistema de layout renderiza um elemento de interface do usuário, defina um ou mais dos seguintes sinalizadores:
AffectsMeasure, que indica que uma alteração no valor da propriedade requer uma alteração na renderização da interface do usuário, especificamente o espaço ocupado por um objeto dentro de seu pai. Por exemplo, defina esse sinalizador de metadados para uma
Widthpropriedade.AffectsArrange, que indica que uma alteração no valor da propriedade requer uma alteração na renderização da interface do usuário, especificamente a posição de um objeto dentro de seu componente pai. Normalmente, o objeto também não altera o tamanho. Por exemplo, defina esse sinalizador de metadados para uma
Alignmentpropriedade.AffectsRender, que indica que ocorreu uma alteração que não afeta o layout e a medida, mas ainda requer outra renderização. Por exemplo, defina esse sinalizador para uma
Backgroundpropriedade ou qualquer outra propriedade que afete a cor de um elemento.
Você também pode usar esses sinalizadores como entradas para suas implementações de substituição dos retornos de chamada do sistema de propriedades (ou layout). Por exemplo, você pode usar um OnPropertyChanged retorno de chamada para chamar InvalidateArrange quando uma propriedade da instância relatar uma alteração de valor e tiver AffectsArrange definido os metadados.
Algumas propriedades afetam as características de renderização de seu elemento pai de outras maneiras. Por exemplo, as alterações na MinOrphanLines propriedade podem alterar a renderização geral de um documento de fluxo. Use AffectsParentArrange ou AffectsParentMeasure para sinalizar as ações dos pais nas suas próprias propriedades.
Por padrão, as propriedades de dependência dão suporte à associação de dados. No entanto, você pode usar IsDataBindingAllowed para desabilitar a associação de dados quando não houver nenhum cenário realista para ela ou quando o desempenho da associação de dados for problemático, como em objetos grandes.
Embora o modo de associação de dados padrão para propriedades de dependência seja OneWay, você pode alterar o modo de associação de uma associação específica para TwoWay. Para obter mais informações, consulte Direção de associação. Como autor de propriedade de dependência, você pode até optar por fazer a vinculação bidirecional o modo padrão. Um exemplo de uma propriedade de dependência existente que usa a associação de dados bidirecional é MenuItem.IsSubmenuOpen, que tem um estado baseado em outras propriedades e chamadas de método. O cenário para
IsSubmenuOpené que sua lógica de configuração e a composição de MenuItem interagem com o estilo de tema padrão. TextBox.Text é outra propriedade de dependência do WPF que usa a associação bidirecional por padrão.Você pode habilitar a herança de propriedade para sua propriedade de dependência definindo o Inherits sinalizador. A herança de propriedade é útil para cenários em que elementos pai e filho têm uma propriedade em comum e faz sentido que o elemento filho herde o valor pai da propriedade comum. Um exemplo de uma propriedade herdável é DataContext, que dá suporte a operações de associação que usam o cenário de detalhes mestres
Defina o Journal indicador para indicar que sua propriedade de dependência deve ser detectada ou utilizada pelos serviços de registro de navegação. Por exemplo, a SelectedIndex propriedade define o
Journalsinalizador para recomendar aos aplicativos que mantenham um histórico de registro dos itens selecionados.
Propriedades de dependência somente leitura
Você pode definir uma propriedade de dependência como de leitura somente. Um cenário típico é uma propriedade de dependência que armazena o estado interno. Por exemplo, IsMouseOver é somente leitura porque seu estado só deve ser determinado pela entrada do mouse. Para obter mais informações, consulte as propriedades de dependência somente leitura.
Propriedades de dependência do tipo coleção
As propriedades de dependência do tipo coleção têm problemas extras de implementação a serem considerados, como a definição de um valor padrão para tipos de referência e suporte à associação de dados para elementos de coleção. Para obter mais informações, consulte Propriedades de dependência do tipo coleção.
Segurança da propriedade de dependência
Normalmente, você declarará propriedades de dependência como propriedades públicas e DependencyProperty campos de identificador como public static readonly campos. Se você especificar um nível de acesso mais restritivo, como protected, uma propriedade de dependência ainda poderá ser acessada por meio de seu identificador em combinação com APIs do sistema de propriedades. Até mesmo um campo identificador protegido é potencialmente acessível por meio de relatórios de metadados do WPF ou APIs de determinação de valor, como LocalValueEnumerator. Para obter mais informações, consulte segurança da propriedade de dependência.
Para propriedades de dependência somente leitura, o valor retornado de RegisterReadOnly é DependencyPropertyKey, e normalmente, você não fará com que DependencyPropertyKey seja um membro public de sua classe. Como o sistema de propriedades do WPF não propaga o DependencyPropertyKey para fora do seu código, uma propriedade de dependência somente leitura tem melhor set segurança do que uma propriedade de dependência de leitura e escrita.
Propriedades de dependência e construtores de classe
Há um princípio geral na programação de código gerenciado, geralmente imposta por ferramentas de análise de código, que os construtores de classe não devem chamar métodos virtuais. Isso ocorre porque os construtores base podem ser chamados durante a inicialização de um construtor de classe derivada e um método virtual chamado por um construtor base pode ser executado antes da inicialização completa da classe derivada. Quando você deriva de uma classe que já deriva de DependencyObject, o próprio sistema de propriedades chama e expõe métodos virtuais internamente. Esses métodos virtuais fazem parte dos serviços do sistema de propriedades do WPF. Substituir os métodos permite que classes derivadas participem da determinação de valor. Para evitar possíveis problemas com a inicialização de runtime, você não deve definir valores de propriedade de dependência dentro de construtores de classes, a menos que você siga um padrão de construtor específico. Para obter mais informações, consulte padrões de construtor seguro para DependencyObjects.
Consulte também
.NET Desktop feedback