附加属性 是一个 XAML 概念。 附加属性允许在对象上设置其他属性/值对,但属性不是原始对象定义的一部分。 附加属性通常定义为依赖属性的专用形式,该依赖属性在所有者类型的对象模型中没有传统的属性包装器。
先决条件
假设你了解依赖项属性的基本概念,并已阅读 依赖项属性概述。
XAML 中的附加属性
在 XAML 中,通过使用语法 AttachedPropertyProvider.PropertyName设置附加属性。 下面是如何在 XAML 中设置 Canvas.Left 的示例。
<Canvas>
<Button Canvas.Left="50">Hello</Button>
</Canvas>
注释
我们只是使用 Canvas.Left 作为示例附加属性,而无需充分解释为何要使用它。 若要详细了解 Canvas.Left 以及 Canvas 如何处理其布局子元素,请参阅 Canvas 参考主题,或查看 使用 XAML定义布局。
为何使用附加属性?
附加属性是一种突破编码约定的方法,这些约定在运行时可能阻止处于关系中的不同对象彼此信息交流。 当然,可以将属性放在通用基类上,以便每个对象只能获取和设置该属性。 但最终,你可能想要执行此操作的场景数量将膨胀到具有可共享属性的基类。 它甚至可能引入一种情况,在这种情况下,从数百个后代中,只有两个试图使用该属性。 这不是很好的类设计。 为了解决此问题,附加属性概念使对象能够为其自己的类结构未定义的属性赋值。 定义类可以在对象树中创建各种对象后,在运行时从子对象读取值。
例如,子元素可以使用附加属性来告知其父元素如何在 UI 中显示它们。 这就是 Canvas.Left 附加属性的情况。 Canvas.Left 被创建为附加属性,因为它是在包含于 Canvas 元素内的其他元素上设置的,而不是直接在 Canvas 本身上设置的。 然后,任何可能的子元素会使用 Canvas.Left 和 Canvas.Top 来在 Canvas 布局容器父元素中指定其布局偏移量。 通过附加属性,这样可以在不将基元素的对象模型与大量仅适用于某个特定布局容器的属性混杂的情况下实现此功能。 相反,许多布局容器实现其自己的附加属性集。
为了实现附加属性,Canvas 类定义了一个名为 Canvas.LeftProperty的静态 DependencyProperty 字段。 然后,Canvas 提供 SetLeft 和 GetLeft 方法,作为附加属性的公共访问器,从而支持在 XAML 中进行设置并在运行时访问值。 对于 XAML 和依赖属性系统,这组 API 满足一种模式,该模式为附加属性启用特定的 XAML 语法,并将值存储在依赖属性存储中。
拥有类型如何使用附加属性
尽管可以在任何 XAML 元素(或任何基础 DependencyObject)上设置附加属性,但这并不自动意味着设置属性会产生有形的结果,或该值会被访问。 定义附加属性的类型通常遵循以下方案之一:
- 定义附加属性的类型是其他对象关系中的父对象。 子对象将设置附加属性的值。 附加属性所有者类型具有一些先天行为,这些行为循环访问其子元素、获取值,并在对象生存期的某个时间点对这些值执行操作(布局操作、SizeChanged等)。
- 定义附加属性的类型用作各种可能的父元素和内容模型的子元素,但信息不一定是布局信息。
- 附加属性将信息报告给服务,而不是向另一个 UI 元素报告信息。
有关这些方案和拥有类型的详细信息,请参阅 自定义附加属性的“Canvas.Left 的详细内容”部分。
代码中的附加属性
附加属性没有像其他依赖属性那样的典型属性包装器,因此不能轻松获取和设置。 这是因为在属性被设置的情况下,附加属性不一定是以代码为中心的对象模型的一部分。 (虽然不常见,但允许定义一个属性,该属性既是附加属性,可以被其他类型自行设置,同时在其所属类型上也具有传统的属性用法。)
可通过两种方法在代码中设置附加属性:使用属性系统 API 或使用 XAML 模式访问器。 这些技术在最终结果方面几乎等效,因此使用哪种技术主要是编码样式的问题。
使用属性系统
Windows 运行时的附加属性作为依赖属性实现,以便这些值可以存储在属性系统的共享依赖属性存储中。 因此,附加属性会在拥有类上公开一个依赖属性标识符。
若要在代码中设置附加属性,请调用 SetValue 方法,并传递用作该附加属性标识符的 DependencyProperty 字段。 (还传递要设置的值。)
若要在代码中获取附加属性的值,请调用 GetValue 方法,并再次传递用作标识符的 DependencyProperty 字段。
使用 XAML 访问器模式
当 XAML 被解析为对象树时,XAML 处理器必须能够设置附加属性值。 附加属性的所有者类型必须实现以 GetPropertyName 和 SetPropertyName形式命名的专用访问器方法。 这些专用访问器方法也是在代码中获取或设置附加属性的一种方法。 从代码的角度来看,附加属性类似于具有方法访问器而不是属性访问器的后盾字段,并且支持字段可以存在于任何对象上,而无需专门定义。
下一个示例演示如何通过 XAML 访问器 API 在代码中设置附加属性。 在此示例中,myCheckBox
是 CheckBox 类的实例。 最后一行是实际设置值的代码;前面的行只是建立实例及其父子关系。 如果使用属性系统,则未注释的最后一行是语法。 使用 XAML 访问器模式时,注释的最后一行代表语法。
Canvas myC = new Canvas();
CheckBox myCheckBox = new CheckBox();
myCheckBox.Content = "Hello";
myC.Children.Add(myCheckBox);
myCheckBox.SetValue(Canvas.TopProperty,75);
//Canvas.SetTop(myCheckBox, 75);
Dim myC As Canvas = New Canvas()
Dim myCheckBox As CheckBox= New CheckBox()
myCheckBox.Content = "Hello"
myC.Children.Add(myCheckBox)
myCheckBox.SetValue(Canvas.TopProperty,75)
' Canvas.SetTop(myCheckBox, 75)
Canvas myC;
CheckBox myCheckBox;
myCheckBox.Content(winrt::box_value(L"Hello"));
myC.Children().Append(myCheckBox);
myCheckBox.SetValue(Canvas::TopProperty(), winrt::box_value(75));
// Canvas::SetTop(myCheckBox, 75);
Canvas^ myC = ref new Canvas();
CheckBox^ myCheckBox = ref new CheckBox();
myCheckBox->Content="Hello";
myC->Children->Append(myCheckBox);
myCheckBox->SetValue(Canvas::TopProperty,75);
// Canvas::SetTop(myCheckBox, 75);
自定义附加属性
有关如何定义自定义附加属性的代码示例,以及有关使用附加属性的方案的详细信息,请参阅 自定义附加属性。
附加属性引用的特殊语法
附加属性名称中的点是标识模式的关键部分。 当语法或情况将点视为具有其他含义时,有时存在歧义。 例如,在绑定路径中,点被视为对象模型的遍历方式。 在大多数情况下,涉及此类歧义,附加属性有一种特殊的语法,使内部点仍可解析为 所有者。属性 附加属性的分隔符。
- 若要将附加属性指定为动画的目标路径的一部分,请将附加的属性名称括在括号(“()”中,例如“(Canvas.Left)”。 有关详细信息,请参阅 属性路径语法。
警告
Windows 运行时 XAML 实现的现有限制是无法对自定义附加属性进行动画处理。
- 若要将附加属性指定为从资源文件到 x:Uid的资源引用的目标属性,请使用一种特殊语法,即在方括号(“[]”)中插入代码样式的完全限定 : 声明,以故意打破作用域。 例如,假设存在元素
<TextBlock x:Uid="Title" />
,则针对该实例的 Canvas.Top 值的资源文件中的资源键为“Title”。[using:Windows.UI.Xaml.Controls]Canvas.Top”。 有关资源文件和 XAML 的详细信息,请参阅 快速入门:翻译 UI 资源。