子对象元素(接上文)
字典
System.Windows.ResourceDictionary是WPF中经常使用的集合,它实现了System.Collections.IDictionary接口,该接口支持在程序代码中添加、移除和枚举键/值对,当我们需要哈希表的时候就可以使用它。在XAML中,我们可以向任何实现了这个接口的集合中添加键/值对,如下例:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="1" A="255" R="255" G="255" B="255" />
<Color x:Key="1" A="0" R="0" G="0" B="0" />
</ResourceDictionary>
定义在二级命名空间x中的Key关键字使得我们可以在每一个Color上附加一个键值(Color并不包含Key属性),因此,下面的C#代码与上面的XAML代码等价:
System.Windows.ResourceDictionary d = new System.Windows.ResourceDictionary();
System.Windows.Media.Color color1 = new System.Windows.Media.Color();
System.Windows.Media.Color color2 = new System.Windows.Media.Color();
color1.A = 255; color1.R = 255; color1.G = 255; color1.B = 255;
color2.A = 0; color1.R = 0; color1.G = 0; color1.B = 0;
d.Add("1", color1);
d.Add("2", color2);
在XAML中由x:Key指定的值,除使用了扩展标记外,总是被当作字符串处理,而不会进行任何类型转换。
更多的类型转换
子对象元素通常是纯文本,如下例:
<SolidColorBrush>White</SolidColorBrush>
它等价于
<SolidColorBrush Color="White" />
尽管Color不是SolidColorBrush的内容属性,但由于存在将“White”或“#FFFFFF”这样的字符串转换为SolidColorBrush的类型转换器,所以第一个代码可以执行。
类型转换器对XAML的可读性起到了巨大作用,但是它也使得XAML在映射.NET对象实例的时候有一点难以理解。根据之前的介绍的知识,我们不能在XAML中声明一个抽象类的元素,因为它无法被实例化。然而尽管System.Windows.Media.Brush是SolidColorBrush、GradientBrush以及其它具体画刷的抽象类,我们仍旧可以如下简化的方式来表示上面的含义:
<Brush>White</Brush>
画刷的类型转换器可以将“White”转换到SolidColorBrush,此处即为使用基类Brush对象引用子类SolidColorBrush对象的情形,因此看起来似乎是构造了抽象类,但实际上不是。
对XAML的扩展
由于XAML的目的是和.NET类型系统一同工作,因此我们可以使用任何.NET对象(甚至是通过互操作使用COM对象),当然也包括我们自己定义的。虽然这些对象不一定是和用户界面有关,但是它们必须是“说明性友好”的,比如一个没有默认构造方法的类将不能直接用于XAML(这样的类就是“说明性不友好”的)。
WPF程序集通过标记XmlnsDefinitionAttribute来将.NET命名空间映射到XML命名空间,但对于那些不是专门为XAML设计的,或者那些没有使用这个特性的.NET类型,在XAML中应当如何使用它们呢?下面的例子展示了正确的用法:
例:C#
System.Collections.Hashtable h = new System.Collections.Hashtable();
h.Add("key1", 7);
h.Add("key2", 23);
XAML
<collections:Hashtable
xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<sys:Int32 x:Key="key1">7</sys:Int32>
<sys:Int32 x:Key="key2">23</sys:Int32>
</collections:Hashtable>
clr-namespace指令使得我们可以在XAML中直接使用.NET命名空间,后面的assemly仅在该.NET命名空间与XAML文档处于不同程序集时才需要,一般情况下仅需要程序集的简单名称,当然我们也可以使用System.Refection.Assembly.Load方法所支持的规范表示法,该表示法包含了如版本号和/或公钥串等额外的信息。
上例中,子元素之所以能够以x:Key语法添加到HashTable中,是因为HashTable类和其他一些类已经实现了IDictionary接口。另外,可以使用System.Int32是因为已经存在了将字符串转换到整数的类型转换器。(这与Windows Form中从字符串转换到VS属性网格的类型转换机制是相同的)
XAML处理子对象元素的规则
为了避免上述三种子对象元素的二义性,XAML解析器/编译器在遇到子元素的时候,将会按照下列规则处理:
1. 如果类型实现了IList接口,则为每个子元素调用IList.Add方法;
2. 否则,如果类型实现了IDictionary接口,则为每个子元素调用IDictionary.Add方法,并以x:Key的属性值为键值对的键、以元素的值为键值对的值;
3. 否则,如果父元素支持内容属性(由ContentPropertyAttribute特性标识),且子元素兼容该属性,则将子元素作为该属性的值;
4. 否则,如果子元素是纯文本,且存在将它转换到父类型的类型转换器(且没有在父元素上设置任何属性),则使用类型转换器将子元素转换为父元素;
5. 否则,报错。规则1和2对应上述“集合项”,规则3对应上述“内容属性”,规则4对应上述“更多的类型转换”。