Propriedades de Dependência Personalizada

Este tópico descreve os motivos que Windows Presentation Foundation (WPF) os desenvolvedores de aplicativos e autores do componente talvez queira criar personalizado propriedade de dependênciae descreve as etapas de implementação, bem como algumas opções de implementação que podem melhorar o desempenho, facilidade de uso ou versatilidade da propriedade.

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

  • Prerequisites
  • What Is a Dependency Property?
  • Examples of Dependency Properties
  • When Should You Implement a Dependency Property?
  • Checklist for Defining a Dependency Property
  • Read-Only Dependency Properties
  • Collection-Type Dependency Properties
  • Dependency Property Security Considerations
  • Dependency Properties and Class Constructors
  • Tópicos relacionados

Prerequisites

This topic assumes that you understand dependency properties from the perspective of a consumer of existing dependency properties on WPF classes, and have read the Visão geral sobre propriedades de dependência topic. In order to follow the examples in this topic, you should also understand Extensible Application Markup Language (XAML) and know how to write WPF applications.

What Is a Dependency Property?

You can enable what would otherwise be a common language runtime (CLR) property to support styling, data binding, inheritance, animations, and default values by implementing it as a dependency property. Dependency properties are properties that are registered with the WPF property system by calling the Register method (or RegisterReadOnly), and that are backed by a DependencyProperty identifier field. Dependency properties can be used only by DependencyObject types, but DependencyObject is quite high in the WPF class hierarchy, so the majority of classes available in WPF can support dependency properties. For more information about dependency properties and some of the terminology and conventions used for describing them in this SDK, see Visão geral sobre propriedades de dependência.

Examples of Dependency Properties

Examples of dependency properties that are implemented on WPF classes include the Background property, the Width property, and the Text property, among many others. Cada propriedade de dependência exposta por uma classe tem um campo de estático público correspondente do tipo DependencyProperty exposto na mesma classe. Esse é o identificador para a propriedade de dependência. O identificador é chamado usando uma convenção: o nome da propriedade de dependência com a seqüência de caracteres Property acrescentado ao proprietário. For example, the corresponding DependencyProperty identifier field for the Background property is BackgroundProperty. The identifier stores the information about the dependency property as it was registered, and the identifier is then used later for other operations involving the dependency property, such as calling SetValue.

Conforme mencionado na Visão geral sobre propriedades de dependência, todas as propriedades de dependência no WPF (exceto a maioria anexado propriedades) também são CLR Propriedades por causa do "wrapper" implementação. Therefore, from code, you can get or set dependency properties by calling CLR accessors that define the wrappers in the same manner that you would use other CLR properties. Como um consumidor de propriedades de dependência estabelecida, você normalmente não usar o DependencyObject métodos GetValue e SetValue, que são o ponto de conexão para o sistema de propriedade subjacente. Rather, the existing implementation of the CLR properties will have already called GetValue and SetValue within the get and set wrapper implementations of the property, using the identifier field appropriately. If you are implementing a custom dependency property yourself, then you will be defining the wrapper in a similar way.

When Should You Implement a Dependency Property?

When you implement a property on a class, so long as your class derives from DependencyObject, you have the option to back your property with a DependencyProperty identifier and thus to make it a dependency property. Having your property be a dependency property is not always necessary or appropriate, and will depend on your scenario needs. Sometimes, the typical technique of backing your property with a private field is adequate. No entanto, você deve implementar a propriedade como um propriedade de dependência sempre que desejar que sua propriedade para oferecer suporte a um ou mais dos seguintes WPF recursos:

  • You want your property to be settable in a style. For more information, see Styling and Templating.

  • You want your property to support data binding. For more information about data binding dependency properties, see Como: Bind the Properties of Two Controls.

  • You want your property to be settable with a dynamic resource reference. For more information, see Visão geral sobre Recursos.

  • You want to inherit a property value automatically from a parent element in the element tree. In this case, register with the RegisterAttached method, even if you also create a property wrapper for CLR access. For more information, see Herança de Valor de Propriedade.

  • You want your property to be animatable. For more information, see Revisão de Animação.

  • You want the property system to report when the previous value of the property has been changed by actions taken by the property system, the environment, or the user, or by reading and using styles. By using property metadata, your property can specify a callback method that will be invoked each time the property system determines that your property value was definitively changed. A related concept is property value coercion. For more information, see Validação e Callbacks de Propriedade de Dependência.

  • You want to use established metadata conventions that are also used by WPF processes, such as reporting whether changing a property value should require the layout system to recompose the visuals for an element. Or you want to be able to use metadata overrides so that derived classes can change metadata-based characteristics such as the default value.

  • Você deseja propriedades de um controle personalizado para receber Visual Studio 2008 WPF Designer suporte, como Propriedades janela de edição. For more information, see Visão geral sobre criação de controles.

When you examine these scenarios, you should also consider whether you can achieve your scenario by overriding the metadata of an existing dependency property, rather than implementing a completely new property. Whether a metadata override is practical depends on your scenario and how closely that scenario resembles the implementation in existing WPF dependency properties and classes. For more information about overriding metadata on existing properties, see Metadados de Propriedade de Dependência.

Checklist for Defining a Dependency Property

Defining a dependency property consists of four distinct concepts. These concepts are not necessarily strict procedural steps, because some of these end up being combined as single lines of code in the implementation:

  • (Optional) Create property metadata for the dependency property.

  • Register the property name with the property system, specifying an owner type and the type of the property value. Also specify the property metadata, if used.

  • Define a DependencyProperty identifier as a public static readonly field on the owner type.

  • Definir um CLR "empacotador" propriedade cujo nome corresponde ao nome da propriedade de dependência. Implementar a CLR "empacotador" propriedade get e set acessadores para conectar-se com a propriedade de dependência que faz o proprietário.

Registering the Property with the Property System

In order for your property to be a dependency property, you must register that property into a table maintained by the property system, and give it a unique identifier that is used as the qualifier for later property system operations. These operations might be internal operations, or your own code calling property system APIs. To register the property, you call the Register method within the body of your class (inside the class, but outside of any member definitions). The identifier field is also provided by the Register method call, as the return value. O motivo que o Register chamada é feita fora do outro membro definições é porque você usa esse valor de retorno para atribuir e criar um public static readonly campo do tipo DependencyProperty como parte da sua turma. This field becomes the identifier for your dependency property.

Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty = DependencyProperty.Register("AquariumGraphic", GetType(Uri), GetType(AquariumObject), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf OnUriChanged)))
public static readonly DependencyProperty AquariumGraphicProperty = DependencyProperty.Register(
  "AquariumGraphic",
  typeof(Uri),
  typeof(AquariumObject),
  new FrameworkPropertyMetadata(null,
      FrameworkPropertyMetadataOptions.AffectsRender, 
      new PropertyChangedCallback(OnUriChanged)
  )
);

Dependency Property Name Conventions

There are established naming conventions regarding dependency properties that you must follow in all but exceptional circumstances.

The dependency property itself will have a basic name, "AquariumGraphic" as in this example, which is given as the first parameter of Register. That name must be unique within each registering type. Dependency properties inherited through base types are considered to be already part of the registering type; names of inherited properties cannot be registered again. However, there is a technique for adding a class as owner of a dependency property even when that dependency property is not inherited; for details, see Metadados de Propriedade de Dependência.

When you create the identifier field, name this field by the name of the property as you registered it, plus the suffix Property. Este campo é o identificador para a propriedade de dependência e será usado posteriormente como uma entrada para o SetValue e GetValue chamadas que serão feitas no invólucros, por qualquer outro acesso a código para a propriedade pelo seu próprio código, por qualquer acesso de código externo que você permitir, o sistema de propriedade e potencialmente XAML processadores.

Observação

Defining the dependency property in the class body is the typical implementation, but it is also possible to define a dependency property in the class static constructor.This approach might make sense if you need more than one line of code to initialize the dependency property.

Implementing the "Wrapper"

Your wrapper implementation should call GetValue in the get implementation, and SetValue in the set implementation (the original registration call and field are shown here too for clarity).

In all but exceptional circumstances, your wrapper implementations should perform only the GetValue and SetValue actions, respectively. The reason for this is discussed in the topic Carregando XAML e Propriedades de Dependência.

All existing public dependency properties that are provided on the WPF classes use this simple wrapper implementation model; most of the complexity of how dependency properties work is either inherently a behavior of the property system, or is implemented through other concepts such as coercion or property change callbacks through property metadata.


Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty = DependencyProperty.Register("AquariumGraphic", GetType(Uri), GetType(AquariumObject), New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf OnUriChanged)))
Public Property AquariumGraphic() As Uri
    Get
        Return CType(GetValue(AquariumGraphicProperty), Uri)
    End Get
    Set(ByVal value As Uri)
        SetValue(AquariumGraphicProperty, value)
    End Set
End Property

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); }
}

Again, by convention, the name of the wrapper property must be the same as the name chosen and given as first parameter of the Register call that registered the property. If your property does not follow the convention, this does not necessarily disable all possible uses, but you will encounter several notable issues:

  • Certain aspects of styles and templates will not work.

  • Most tools and designers must rely on the naming conventions to properly serialize XAML, or to provide designer environment assistance at a per-property level.

  • The current implementation of the WPF XAML loader bypasses the wrappers entirely, and relies on the naming convention when processing attribute values. For more information, see Carregando XAML e Propriedades de Dependência.

Property Metadata for a New Dependency Property

When you register a dependency property, the registration through the property system creates a metadata object that stores property characteristics. Many of these characteristics have defaults that are set if the property is registered with the simple signatures of Register. Other signatures of Register allow you to specify the metadata that you want as you register the property. The most common metadata given for dependency properties is to give them a default value that is applied on new instances that use the property.

If you are creating a dependency property that exists on a derived class of FrameworkElement, you can use the more specialized metadata class FrameworkPropertyMetadata rather than the base PropertyMetadata class. The constructor for the FrameworkPropertyMetadata class has several signatures where you can specify various metadata characteristics in combination. If you want to specify the default value only, use the signature that takes a single parameter of type Object. Pass that object parameter as a type-specific default value for your property (the default value provided must be the type you provided as the propertyType parameter in the Register call).

For FrameworkPropertyMetadata, you can also specify metadata option flags for your property. These flags are converted into discrete properties on the property metadata after registration and are used to communicate certain conditionals to other processes such as the layout engine.

Setting Appropriate Metadata Flags

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

    • AffectsMeasure indicates that a change to this property requires a change to UI rendering where the containing object might require more or less space within the parent. For example, a "Width" property should have this flag set.

    • AffectsArrange indicates that a change to this property requires a change to UI rendering that typically does not require a change in the dedicated space, but does indicate that the positioning within the space has changed. For example, an "Alignment" property should have this flag set.

    • AffectsRender indicates that some other change has occurred that will not affect layout and measure, but does require another render. An example would be a property that changes a color of an existing element, such as "Background".

    • These flags are often used as a protocol in metadata for your own override implementations of property system or layout callbacks. For instance, you might have an OnPropertyChanged callback that will call InvalidateArrange if any property of the instance reports a value change and has AffectsArrange as true in its metadata.

  • Some properties may affect the rendering characteristics of the containing parent element, in ways above and beyond the changes in required size mentioned above. An example is the MinOrphanLines property used in the flow document model, where changes to that property can change the overall rendering of the flow document that contains the paragraph. Use AffectsParentArrange or AffectsParentMeasure to identify similar cases in your own properties.

  • By default, dependency properties support data binding. You can deliberately disable data binding, for cases where there is no realistic scenario for data binding, or where performance in data binding for a large object is recognized as a problem.

  • By default, data binding Mode for dependency properties defaults to OneWay. You can always change the binding to be TwoWay per binding instance; for details, see Como: Especificar a Direção do Vínculo. But as the dependency property author, you can choose to make the property use TwoWay binding mode by default. An example of an existing dependency property is MenuItem.IsSubmenuOpen; the scenario for this property is that the IsSubmenuOpen setting logic and the compositing of MenuItem interact with the default theme style. The IsSubmenuOpen property logic uses data binding natively to maintain the state of the property in accordance to other state properties and method calls. Another example property that binds TwoWay by default is TextBox.Text.

  • You can also enable property inheritance in a custom dependency property by setting the Inherits flag. Property inheritance is useful for a scenario where parent elements and child elements have a property in common, and it makes sense for the child elements to have that particular property value set to the same value as the parent set it. An example inheritable property is DataContext, which is used for binding operations to enable the important master-detail scenario for data presentation. By making DataContext inheritable, any child elements inherit that data context also. Devido à herança de valor de propriedade, você pode especificar um contexto de dados na raiz do aplicativo ou página e não é necessário respecify-la para vinculações de todos os elementos filho possíveis. DataContexttambém é um bom exemplo para ilustrar que o valor padrão de substituições de herança, mas sempre pode ser definido localmente em qualquer elemento filho específico; Para obter detalhes, consulte Como: Use the Master-Detail Pattern with Hierarchical Data. Property value inheritance does have a possible performance cost, and thus should be used sparingly; for details, see Herança de Valor de Propriedade.

  • Set the Journal flag to indicate if your dependency property should be detected or used by navigation journaling services. An example is the SelectedIndex property; any item selected in a selection control should be persisted when the journaling history is navigated.

Read-Only Dependency Properties

You can define a dependency property that is read-only. However, the scenarios for why you might define your property as read-only are somewhat different, as is the procedure for registering them with the property system and exposing the identifier. For more information, see Read-Only Dependency Properties.

Collection-Type Dependency Properties

Collection-type dependency properties have some additional implementation issues to consider. For details, see Propriedade de Dependência de Tipos de Coleção.

Dependency Property Security Considerations

Dependency properties should be declared as public properties. Dependency property identifier fields should be declared as public static fields. Even if you attempt to declare other access levels (such as protected), a dependency property can always be accessed through the identifier in combination with the property system APIs. Even a protected identifier field is potentially accessible because of metadata reporting or value determination APIs that are part of the property system, such as LocalValueEnumerator. For more information, see Segurança de Propriedades de Dependência.

Dependency Properties and Class Constructors

There is a general principle in managed code programming (often enforced by code analysis tools such as FxCop) that class constructors should not call virtual methods. This is because constructors can be called as base initialization of a derived class constructor, and entering the virtual method through the constructor might occur at an incomplete initialization state of the object instance being constructed. When you derive from any class that already derives from DependencyObject, you should be aware that the property system itself calls and exposes virtual methods internally. These virtual methods are part of the WPF property system services. Overriding the methods enables derived classes to participate in value determination. To avoid potential issues with runtime initialization, you should not set dependency property values within constructors of classes, unless you follow a very specific constructor pattern. For details, see 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