本篇,我们再继续看一种特殊的 Dependency 属性: Attached 属性。 Attached 属性可以非常高效地 Attach 到其他的对象中。
我们仍然用前面的一个简单 XAML 代码为例:
现在,如果需要对 StackPanel 及其子元素设置字体大小,应该如何做呢?在 Window 元素中,它有一个属性 FontSize ,可以直接设置。但是, StackPanel 自己本身并没有 FontSize 这样的属性。这就该 Attached 属性出场了。这里我们需要用定义在 TextElement 元素中的 Attached 属性 FontSize 来设置 StackPanel 的字体。
这样, StackPanel 的子元素就能通过属性值继承得到新的 FontSize 属性。对于这样的 XAML 代码, XAML 编译器或者解析器看到这种语法 时,就要求 TextElement (有时也称为 Attached 属性提供者)有相应的静态方法 SetFontSize 来设置对应的属性值。因此,上面的 Attached 属性设置代码,可以如下用 C# 实现:
从这里的代码可以看出, Attached 属性并不神秘。只是调用方法把元素和不相关的属性关联起来。而 SetFontSize 实现也比较简单。 它只是调用 了 Dependency 属性访问函数所调用的 DependencyObject.SetValue 方法。注意调用的对象是传入的 DependencyObject ,而不是当前的实例:
同样地, Attached 属性也定义了对应的 GetXXX 函数。它调用的 DependencyObject.GetValue 方法:
与普通的 Dependency 属性一样,这些 GetXXX 和 SetXXX 方法除了实现对 GetValue 和 SetValue 的调用,不能做任何其他额外的工作。
其实,在 WPF 应用中, Attached 属性更多的用来控制 UI 的布局。除了前面的 StackPanel ,还有 Grid 等等。
补充说明:上面的代码还有一个问题需要说明。我们设置 StackPanel 的字体属性时用的是 TextElement 元素。为什么不用其他的元素 Control 、 Button 呢?
这个问题的关键之处在于 Dependency 属性的注册方法。我曾在 Dependency 属性 [1] 做过简单的说明。我们看看 Element 的 FontSizeProperty 属性的注册代码:
这里与我们前面的 IsDefault 属性类似,只是 RisterAttached 方法优化了 Attached 属性需要的属性元数据的处理过程。
另一方面, Control 的 FontSize 属性是在 TextElement 元素已经注册的属性之上调用 AddOwner 方法,获取一个完全相同的实例引用:
所以,在实现 Attached 属性时我们使用的是 TextElement ,而不是 Control 等等。