附加属性概述Attached Properties Overview

附加属性是由 XAML 定义的概念。An attached property is a concept defined by XAML. 附加属性旨在用作可在任何对象上设置的一类全局属性。An attached property is intended to be used as a type of global property that is settable on any object. Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) 中,附加属性通常定义为没有常规属性“包装器”的依赖性属性的专用形式。In Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF), attached properties are typically defined as a specialized form of dependency property that does not have the conventional property "wrapper".

先决条件Prerequisites

本主题假定你从 Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) 类的现有依赖属性的使用者角度了解依赖属性,并且已阅读依赖属性概述This topic assumes that you understand dependency properties from the perspective of a consumer of existing dependency properties on Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) classes, and have read the Dependency Properties Overview. 若要按照本主题中的示例进行操作,还应了解 XAML 并了解如何编写 WPF 应用程序。To follow the examples in this topic, you should also understand XAML and know how to write WPF applications.

为什么使用附加属性Why Use Attached Properties

附加属性的一个用途是允许不同的子元素为实际在父元素中定义的属性指定唯一值。One purpose of an attached property is to allow different child elements to specify unique values for a property that is actually defined in a parent element. 此方案的一个具体应用是,让子元素通知父元素它们在 用户界面 (UI)user interface (UI) 中的呈现方式。A specific application of this scenario is having child elements inform the parent element of how they are to be presented in the 用户界面 (UI)user interface (UI). DockPanel.Dock 属性就是一个示例。One example is the DockPanel.Dock property. DockPanel.Dock 属性将创建为附加属性,因为该属性设计为在包含在 DockPanel中的元素上设置,而不是在 DockPanel 本身上设置。The DockPanel.Dock property is created as an attached property because it is designed to be set on elements that are contained within a DockPanel, rather than on DockPanel itself. DockPanel 类定义名为 DockProperty的静态 DependencyProperty 字段,然后提供作为附加属性公共访问器的 GetDockSetDock 方法。The DockPanel class defines the static DependencyProperty field named DockProperty, and then provides the GetDock and SetDock methods as public accessors for the attached property.

XAML 中的附加属性Attached Properties in XAML

在 XAML 中,可以使用语法 AttachedPropertyProvider.PropertyName 来设置附加属性In XAML, you set attached properties by using the syntax AttachedPropertyProvider.PropertyName

下面是如何在 XAML 中设置 DockPanel.Dock 的示例:The following is an example of how you can set DockPanel.Dock in XAML:

<DockPanel>
  <CheckBox DockPanel.Dock="Top">Hello</CheckBox>
</DockPanel>

请注意,使用情况有些类似于静态属性;始终引用拥有和注册附加属性的类型 DockPanel,而不是引用由 name 指定的任何实例。Note that the usage is somewhat similar to a static property; you always reference the type DockPanel that owns and registers the attached property, rather than referring to any instance specified by name.

此外,由于 XAML 中的附加属性是在标记中设置的属性,因此,只有设置操作具有相关性。Also, because an attached property in XAML is an attribute that you set in markup, only the set operation has any relevance. 尽管存在一些用于比较值的间接机制(如在样式中触发),但无法直接在 XAML 中直接获取属性(有关详细信息,请参阅样式设置和模板化)。You cannot directly get a property in XAML, although there are some indirect mechanisms for comparing values, such as triggers in styles (for details, see Styling and Templating).

WPF 中的附加属性实现Attached Property Implementation in WPF

Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF)中,与 UI 表示形式相关的 WPF 类型上存在的大多数附加属性都实现为依赖属性。In Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF), most of the attached properties that exist on WPF types that are related to UI presentation are implemented as dependency properties. 附加属性是 XAML 概念,而依赖属性是 WPF 概念。Attached properties are a XAML concept, whereas dependency properties are a WPF concept. 因为 WPF 附加属性是依赖属性,所以它们支持依赖属性概念(如属性元数据)和来自该属性元数据的默认值。Because WPF attached properties are dependency properties, they support dependency property concepts such as property metadata, and default values from that property metadata.

所属类型使用附加属性的方式How Attached Properties Are Used by the Owning Type

尽管可以在任何对象上设置附加属性,但这并不自动意味着设置该属性会产生实际的结果,或者该值会被其他对象使用。Although attached properties are settable on any object, that does not automatically mean that setting the property will produce a tangible result, or that the value will ever be used by another object. 通常,附加属性是为了使来自各种可能的类层次结构或逻辑关系的对象都可以向用于定义附加属性的类型报告公用信息。Generally, attached properties are intended so that objects coming from a wide variety of possible class hierarchies or logical relationships can each report common information to the type that defines the attached property. 定义附加属性的类型通常采用以下模型之一:The type that defines the attached property typically follows one of these models:

  • 设计定义附加属性的类型,以便它可以是将为附加属性设置值的元素的父元素。The type that defines the attached property is designed so that it can be the parent element of the elements that will set values for the attached property. 随后,该类型将在内部逻辑中对照某些对象树结构循环访问其子对象,获取值,并以某种方式作用于这些值。The type then iterates its child objects through internal logic against some object tree structure, obtains the values, and acts on those values in some manner.

  • 定义附加属性的类型将用作各种可能的父元素和内容模型的子元素。The type that defines the attached property will be used as the child element for a variety of possible parent elements and content models.

  • 定义附加属性的类型表示一项服务。The type that defines the attached property represents a service. 其他类型为该附加属性设置值。Other types set values for the attached property. 然后,当在服务的上下文中计算设置该属性的元素时,将通过服务类的内部逻辑获取附加属性的值。Then, when the element that set the property is evaluated in the context of the service, the attached property values are obtained through internal logic of the service class.

父级定义的附加属性示例An Example of a Parent-Defined Attached Property

WPF 定义附加属性的最典型方案是:当父元素支持子元素集合时,还实现了一种行为,其中为每个子元素单独报告了行为的细节。The most typical scenario where WPF defines an attached property is when a parent element supports a child element collection, and also implements a behavior where the specifics of the behavior are reported individually for each child element.

DockPanel 定义 DockPanel.Dock 附加属性,DockPanel 具有类级别代码作为其呈现逻辑(具体而言,MeasureOverrideArrangeOverride)的一部分。DockPanel defines the DockPanel.Dock attached property, and DockPanel has class-level code as part of its rendering logic (specifically, MeasureOverride and ArrangeOverride). DockPanel 实例将始终检查其任意直接子元素是否已为 DockPanel.Dock设置了值。A DockPanel instance will always check to see whether any of its immediate child elements have set a value for DockPanel.Dock. 如果已设置,这些值将变为应用于该特定子元素的呈现逻辑的输入。If so, those values become input for the rendering logic applied to that particular child element. 嵌套的 DockPanel 实例分别处理它们自己的直接子元素集合,但该行为是特定于实现的,它是 DockPanel 处理 DockPanel.Dock 值的方式。Nested DockPanel instances each treat their own immediate child element collections, but that behavior is implementation-specific to how DockPanel processes DockPanel.Dock values. 理论上,可以有影响直接父级之外的元素的附加属性。It is theoretically possible to have attached properties that influence elements beyond the immediate parent. 如果在没有要对其执行操作的 DockPanel 父元素的元素上设置 DockPanel.Dock 附加属性,则不会引发错误或异常。If the DockPanel.Dock attached property is set on an element that has no DockPanel parent element to act upon it, no error or exception is raised. 这只是表示已设置全局属性值,但没有可使用该信息的当前 DockPanel 父级。This simply means that a global property value was set, but it has no current DockPanel parent that could consume the information.

代码中的附加属性Attached Properties in Code

WPF 中的附加属性没有用于轻松获取/设置访问的典型 CLR "包装器" 方法。Attached properties in WPF do not have the typical CLR "wrapper" methods for easy get/set access. 这是因为,附加属性不一定是设置了属性的实例的 CLR 命名空间的一部分。This is because the attached property is not necessarily part of the CLR namespace for instances where the property is set. 但是,分析 XAML 时,XAML 处理器必须能够设置这些值。However, a XAML processor must be able to set those values when XAML is parsed. 若要支持有效的附加属性用法,附加属性的所有者类型必须以Get_PropertyName_Set_PropertyName_ 形式实现专用访问器方法。To support an effective attached property usage, the owner type of the attached property must implement dedicated accessor methods in the form Get_PropertyName_ and Set_PropertyName_. 这些专用访问器方法对在代码中设置附加属性也很有帮助。These dedicated accessor methods are also useful to get or set the attached property in code. 从代码的角度来看,附加属性类似于具有方法访问器而不是属性访问器的支持字段,且支持字段可在任何对象上存在,无需专门定义。From a code perspective, an attached property is similar to a backing field that has method accessors instead of property accessors, and that backing field can exist on any object rather than needing to be specifically defined.

下面的示例演示如何在代码中设置附加属性。The following example shows how you can set an attached property in code. 在此示例中,myCheckBoxCheckBox 类的实例。In this example, myCheckBox is an instance of the CheckBox class.

DockPanel myDockPanel = new DockPanel();
CheckBox myCheckBox = new CheckBox();
myCheckBox.Content = "Hello";
myDockPanel.Children.Add(myCheckBox);
DockPanel.SetDock(myCheckBox, Dock.Top);
Dim myDockPanel As New DockPanel()
Dim myCheckBox As New CheckBox()
myCheckBox.Content = "Hello"
myDockPanel.Children.Add(myCheckBox)
DockPanel.SetDock(myCheckBox, Dock.Top)

与 XAML 情况类似,如果 myCheckBox 尚未添加为第三行代码 myDockPanel 的子元素,则第四行代码不会引发异常,但属性值不会与 DockPanel 父代交互,从而没.Similar to the XAML case, if myCheckBox had not already been added as a child element of myDockPanel by the third line of code, the fourth line of code would not raise an exception, but the property value would not interact with a DockPanel parent and thus would do nothing. 只有在子元素上设置的 DockPanel.Dock 值与 DockPanel 父元素的存在相结合将导致呈现的应用程序中出现有效的行为。Only a DockPanel.Dock value set on a child element combined with the presence of a DockPanel parent element will cause an effective behavior in the rendered application. (在这种情况下,可以设置附加属性,然后附加到树。(In this case, you could set the attached property, then attach to the tree. 或者,可以先附加到该树中,然后设置附加属性。Or you could attach to the tree then set the attached property. 这两种操作顺序结果都相同。)Either action order provides the same result.)

附加属性元数据Attached Property Metadata

注册属性时,FrameworkPropertyMetadata 设置为指定属性的特性,例如属性是否影响呈现、度量等。When registering the property, FrameworkPropertyMetadata is set to specify characteristics of the property, such as whether the property affects rendering, measurement, and so on. 附加属性的元数据通常与依赖属性上的元数据基本上都相同。Metadata for an attached property is generally no different than on a dependency property. 如果在附加属性元数据替代中指定默认值,该值将成为替代类实例上显式附加属性的默认值。If you specify a default value in an override to attached property metadata, that value becomes the default value of the implicit attached property on instances of the overriding class. 具体而言,当某些进程通过该属性的 Get 方法访问器请求附加属性值,并指定在其中指定元数据的类的示例时,将报告默认值,而不会设置该附加属性的值。Specifically, your default value is reported if some process queries for the value of an attached property through the Get method accessor for that property, specifying an instance of the class where you specified the metadata, and the value for that attached property was otherwise not set.

如果希望对属性启用属性值继承,应使用附加属性,而不是非附加的依赖属性。If you want to enable property value inheritance on a property, you should use attached properties rather than non-attached dependency properties. 有关详细信息,请参阅属性值继承For details, see Property Value Inheritance.

自定义附加属性Custom Attached Properties

何时创建附加属性When to Create an Attached Property

当需要有一个可用于定义类之外的其他类的属性设置机制时,建议创建附加属性。You might create an attached property when there is a reason to have a property setting mechanism available for classes other than the defining class. 对于这一情况,最常见的方案是布局。The most common scenario for this is layout. 现有布局属性的示例包括 DockPanel.DockPanel.ZIndexCanvas.TopExamples of existing layout properties are DockPanel.Dock, Panel.ZIndex, and Canvas.Top. 这里启用的方案是,作为布局控制元素的子元素存在的元素能够分别向其布局父级元素表达布局要求,其中每个元素都设置一个被父级定义为附加属性的属性值。The scenario enabled here is that elements that exist as child elements to layout-controlling elements are able to express layout requirements to their layout parent elements individually, each setting a property value that the parent defined as an attached property.

使用附加属性的另一种情况是,你的类表示一种服务,且你希望类能够以更透明的方式继承该服务。Another scenario for using an attached property is when your class represents a service, and you want classes to be able to integrate the service more transparently.

但另一种情况是接收 Visual Studio WPF 设计器支持,如 "属性" 窗口编辑。Yet another scenario is to receive Visual Studio WPF Designer support, such as Properties window editing. 有关详细信息,请参阅控件创作概述For more information, see Control Authoring Overview.

如前文所述,如果想要使用属性值继承,你应该注册为附加属性。As mentioned before, you should register as an attached property if you want to use property value inheritance.

如何创建附加属性How to Create an Attached Property

如果你的类将严格定义附加属性以用于其他类型,则该类不必从 DependencyObject派生。If your class is defining the attached property strictly for use on other types, then the class does not have to derive from DependencyObject. 但是,如果遵循使附加属性也是依赖属性的总体 WPF 模型,则确实需要从 DependencyObject 派生。But you do need to derive from DependencyObject if you follow the overall WPF model of having your attached property also be a dependency property.

通过声明类型 DependencyPropertypublic static readonly 字段,将附加属性定义为依赖属性。Define your attached property as a dependency property by declaring a public static readonly field of type DependencyProperty. 使用 RegisterAttached 方法的返回值定义此字段。You define this field by using the return value of the RegisterAttached method. 字段名称必须与附加属性名称相匹配,并追加字符串 Property,才能遵循建立的、标识字段与它们所表示的属性的命名的 WPF 模式。The field name must match the attached property name, appended with the string Property, to follow the established WPF pattern of naming the identifying fields versus the properties that they represent. 附加属性提供程序还必须提供静态Get_PropertyName_Set_PropertyName_ 方法作为附加属性的访问器;如果无法执行此操作,则会导致属性系统无法使用附加属性。The attached property provider must also provide static Get_PropertyName_ and Set_PropertyName_ methods as accessors for the attached property; failing to do this will result in the property system being unable to use your attached property.

备注

如果省略附加属性的 get 访问器,则属性上的数据绑定将无法在设计工具(如 Visual Studio 和 Blend for Visual Studio)中工作。If you omit the attached property's get accessor, data binding on the property will not work in design tools, such as Visual Studio and Blend for Visual Studio.

Get 访问器The Get Accessor

Get_PropertyName_ 访问器的签名必须是:The signature for the Get_PropertyName_ accessor must be:

public static object GetPropertyName(object target)

  • target 对象在实现中可以指定为更具体的类型。The target object can be specified as a more specific type in your implementation. 例如,DockPanel.GetDock 方法将参数键入为 UIElement,因为附加属性只用于在 UIElement 实例上进行设置。For example, the DockPanel.GetDock method types the parameter as UIElement, because the attached property is only intended to be set on UIElement instances.

  • 返回值在实现中可以指定为更具体的类型。The return value can be specified as a more specific type in your implementation. 例如,GetDock 方法将其类型设置为 Dock,因为该值只能设置为该枚举。For example, the GetDock method types it as Dock, because the value can only be set to that enumeration.

Set 访问器The Set Accessor

Set_PropertyName_ 访问器的签名必须是:The signature for the Set_PropertyName_ accessor must be:

public static void SetPropertyName(object target, object value)

  • target 对象在实现中可以指定为更具体的类型。The target object can be specified as a more specific type in your implementation. 例如,SetDock 方法将其类型设置为 UIElement,因为附加属性只应在 UIElement 实例上设置。For example, the SetDock method types it as UIElement, because the attached property is only intended to be set on UIElement instances.

  • value 对象在实现中可以指定为更具体的类型。The value object can be specified as a more specific type in your implementation. 例如,SetDock 方法将其类型设置为 Dock,因为该值只能设置为该枚举。For example, the SetDock method types it as Dock, because the value can only be set to that enumeration. 请记住,此方法的值是 XAML 加载器在标记中的附加属性用法中遇到附加属性时的输入。Remember that the value for this method is the input coming from the XAML loader when it encounters your attached property in an attached property usage in markup. 该输入是在标记中指定为 XAML 属性值的值。That input is the value specified as a XAML attribute value in markup. 因此必须存在可用于你所使用的类型的类型转换、值序列化程序或标记扩展支持,以便可以从属性值(最终仅仅是一个字符串)创建相应的类型。Therefore there must be type conversion, value serializer, or markup extension support for the type you use, such that the appropriate type can be created from the attribute value (which is ultimately just a string).

下面的示例演示了依赖属性注册(使用 RegisterAttached 方法)以及Get_PropertyName_Set_PropertyName_ 访问器。The following example shows the dependency property registration (using the RegisterAttached method), as well as the Get_PropertyName_ and Set_PropertyName_ accessors. 在此示例中,附加属性名称为 IsBubbleSourceIn the example, the attached property name is IsBubbleSource. 因此,访问器必须名为 GetIsBubbleSourceSetIsBubbleSourceTherefore, the accessors must be named GetIsBubbleSource and SetIsBubbleSource.

public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached(
  "IsBubbleSource",
  typeof(Boolean),
  typeof(AquariumObject),
  new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender)
);
public static void SetIsBubbleSource(UIElement element, Boolean value)
{
  element.SetValue(IsBubbleSourceProperty, value);
}
public static Boolean GetIsBubbleSource(UIElement element)
{
  return (Boolean)element.GetValue(IsBubbleSourceProperty);
}
Public Shared ReadOnly IsBubbleSourceProperty As DependencyProperty = DependencyProperty.RegisterAttached("IsBubbleSource", GetType(Boolean), GetType(AquariumObject), New FrameworkPropertyMetadata(False, FrameworkPropertyMetadataOptions.AffectsRender))
Public Shared Sub SetIsBubbleSource(ByVal element As UIElement, ByVal value As Boolean)
    element.SetValue(IsBubbleSourceProperty, value)
End Sub
Public Shared Function GetIsBubbleSource(ByVal element As UIElement) As Boolean
    Return CType(element.GetValue(IsBubbleSourceProperty), Boolean)
End Function

附加属性特性Attached Property Attributes

WPF 定义了多个 .NET 特性,旨在向反射进程提供附加属性的相关信息,并定义了反射的典型用户和属性信息(如设计器)。WPF defines several .NET attributes that are intended to provide information about attached properties to reflection processes, and to typical users of reflection and property information such as designers. 由于附加属性的类型没有范围限制,因此设计者需要一种方法来避免用户查看全局列表时,看到使用 XAML 的特定技术实现中定义的所有附加属性。Because attached properties have a type of unlimited scope, designers need a way to avoid overwhelming users with a global list of all the attached properties that are defined in a particular technology implementation that uses XAML. WPF 为附加属性定义的 .NET 特性可用于确定应该在 "属性" 窗口中显示给定附加属性的情况的作用域。The .NET attributes that WPF defines for attached properties can be used to scope the situations where a given attached property should be shown in a properties window. 你还可考虑对自己的自定义附加属性应用这些特性。You might consider applying these attributes for your own custom attached properties also. .NET 特性的目的和语法在相应的参考页上进行了说明:The purpose and syntax of the .NET attributes is described on the appropriate reference pages:

了解有关附加属性的详细信息Learning More About Attached Properties

  • 有关如何创建附加属性的详细信息,请参阅注册附加属性For more information on creating an attached property, see Register an Attached Property.

  • 有关依赖属性和附加属性的更多高级使用方案,请参阅自定义依赖属性For more advanced usage scenarios for dependency properties and attached properties, see Custom Dependency Properties.

  • 还可将属性注册为附加属性和依赖属性,但仍需公开“包装器”实现。You can also register a property as an attached property, and as a dependency property, but then still expose "wrapper" implementations. 在这种情况下,属性可在该元素上设置,也可通过 XAML 附加属性语法在任何元素上设置。In this case, the property can be set either on that element, or on any element through the XAML attached property syntax. FrameworkElement.FlowDirection使用适用于标准和附加用法的适当方案的属性示例。An example of a property with an appropriate scenario for both standard and attached usages is FrameworkElement.FlowDirection.

请参阅See also