书名:WPF专业编程指南
作者:李应保
出版社:电子工业出版社
出版时间:2010-01
ISBN:9787121100116
一、附加属性
- 附加属性(Attached Property)其实是相关属性的另外一种形式。
所有从DependencyObject类中派生出来的类,都可以使用附加属性。
二、引入附加属性的原因
- 为什么要引入附加属性呢?
可以看这样一个例子:若有一些图形,比如说矩形,要在视窗上显示出来。
若使用Canvas排版,那么要告诉Canvas该矩形的左上角坐标。
一种实现方式是定义一个基类,其中含有x,y坐标,然后把具体的图形类从该基类中派生出来,这样所有的图形在Canvas上的位置就确定了。
然而,这种解决方案,在WPF中会行不通:WPF支持多种排版。
比如说,同样的图形元素,现在要改在Grid上显示了,这时,就不能使用左上角x,y坐标,而要指出图形在Grid中的行列号。
又如,同样的图形要在DockPanel中显示时,所要给出的居然是Top、Left、Right或Bottom这样的相对位置。显然,由于WPF排版的多样性,过去所用的解决问题的方法已经不够用了。
三、解决办法
WPF引入了附加属性来解决这个问题:图形元素,比如矩形,就不需要预先说明它们在窗口内的位置,当要在某个特定的排版环境中显示时,可以把排版类的某些相关属性引入进来,从而确定自己在窗口中的位置。
在Grid中显示矩形的例子如下:
<Window x:Class="Yingbao.Chapter4.AttachedPropertyExample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Yingbao.Chapter4" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height ="*"/>
<RowDefinition Height ="2*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width ="*"/>
<ColumnDefinition Width ="2*"/>
</Grid.ColumnDefinitions>
<Rectangle Stroke ="Aqua" Fill ="Brown " Grid.Column ="0"
Grid.Row ="0"/>
<Rectangle Stroke ="Blue" Fill ="Yellow " Grid.Column ="1"
Grid.Row ="0"/>
<Rectangle Stroke ="Aqua" Fill ="Brown " Grid.Column ="1"
Grid.Row ="1"/>
<Rectangle Stroke ="Blue" Fill ="Yellow " Grid.Column ="0"
Grid.Row ="1"/>
</Grid>
</Window>
-
本例定义了一个2行2列的Grid,用4个矩形填充这4个网格;
每个矩形在Grid中的位置,是由Grid.Column和Grid.Row确定的。
显然,这里的Grid.Column和Grid.Row不是矩形的属性,而是Grid的属性。
为什么可以把Grid的属性用到矩形Rectangle中来呢?
难道WPF有什么魔法不成?
在Grid中显示的矩形如图4-5所示。
图4-5 在Grid中显示矩形 -
问题的关键在于Rectangle类具有DependencyObject作为它的基类,图4-6是矩形Rectangle类在WPF中的完整类继承树,由图4-6可见,DependencyObject是其中的一个基类,换句话说,Rectangle是DependencyObject(这是UML的通常表述)。
图4-6 WPF中矩形(Rectangle)的继承树 当我们在XAML中使用下面的语句时:
<Rectangle Stroke ="Aqua" Fill ="Brown " Grid.Column ="0" Grid.Row
="0"/>
设置Grid.Column=“0”,实际上调用的是DependencyObject中的(Grid.Column, “0”)方法。
类似地,要读取附加属性的值,可以调用DependencyObject中的GetValue方法。
笔者认为DependencyObject中应该有一个维护相关属性和值之间的哈希表。
由此可见,要把相关属性引入到某个类中,这个类必须是DependencyObject,这一点非常重要。其次,如要使相关属性能被引入到DependencyObject类中,则一定要在注册该相关属性时。注册相关属性为附加属性的方法是调用DependencyProperty类中的RegisterAttached方法。

