XAML中,关于特性语法部分,讲到了一个很重要的知识点——类型转换器。经过几天的啃书(WPF揭秘、XAML概述、XAML语法详述)+攻关,慢慢开始理解了该XAML语法概念和用法,现总结如下:
先来看类型转换的概念:
特性语法中讲到,特性值(属性或事件)必须为双引号扩起来的纯文本形式的字符串,亦或者是Double等其他基元值,对于XAML处理器来说,都是本文字符串。
XAML 处理器需要两条信息才能处理特性值。 第一条信息是要设置的属性的值类型(什么叫“属性的值类型”?即该属性表示的对象元素在WPF类库中对应的属性类的类型),用于定义特性值并且在 XAML 中进行处理的任何字符串最终都必须转换或解析为该类型的值。 如果该值为 XAML 分析器能够识别的基元(如数字值),则将尝试直接转换该字符串。 如果该值为枚举,则使用字符串检查是否有名称与该枚举中的命名常量匹配。 如果该值既不是分析器所能识别的基元,也不是枚举,则所涉及的类型必须能够根据转换的字符串提供该类型的实例(即XAML标记中该属性的字符串值必须满足后台对应属性的值类型的构造函数,能否生成对应值类型的实例)。 这个时候,就是依靠类型转换器来实现此功能,这就是类型转换的概念。
那WPF是如何实现将XAML中属性的字符串值转换为后台属性的值类型实例呢?其机理是什么?我们来看个示例。
Button元素对象有一个Margin属性,用来设置元素的外边距,Margin属性在后台映射为FrameworkElement.Margin 属性,其C#语法定义为:
这里我们看到Margin属性的值类型为Thickness,我们继续在MSDN中查一下Thickness类的定义,得知:Thickness为一结构类型,用于描述矩形周围框架的粗细,它具有四个 Double类型属性值分别描述矩形的四个边(Left、Top、Right 和 Bottom)。 其语法定义为:
[TypeConverterAttribute(typeof(ThicknessConverter))]
public struct Thickness : IEquatable<Thickness>
我们看到Thickness类是由TypeConvertAttribute特性类来声明的,即在实例化Thickness实例对象时,指定了通过ThicknessConverter类型转换器来实现其他类型对象实例与Thickness类型实例对象之间的转换。根据上面XAML标记中Margin特性值为“10,20,10,30”,正好与Thickness的Left、Top、Right 和 Bottom四个属性值对应。据此,XAML解析器对Margin特性的字符串值在后台进行类型转换,由ThicknessConverter类型转换器返回Thickness对象实例。
在MSDN中,直接说明了Margin属性的XAML 属性元素用法和XAML 特性用法:
<object.Margin>
<Thickness Left="left" Top="top" Right="right" Bottom="bottom"/>
</object.Margin>
</object>
- or -
<object Margin="left,top"/>
- or -
<object Margin="thicknessReference"/>
通过以上示例,我们可以看出,如果采用属性元素用法,标记代码要冗长得多,而采用特性用法,则简化很多。因此,我们可以总结出,类型转换器实际上是一个帮助程序类,用于XAML中的类型转换行为,正是因为属性的值类型Thickness提供了ThicknessConverter类型转换器,我们才能简化设置Margin属性值。这就是类型转换器的工作原理。
一般来说,使用类型转换字符串还是更复杂的等效语法是对编码风格的选择。因此,当我们在XAML中需要对某个属性的字符串值进行特性处理行为时,我们需要在MSDN中确认该属性的值类型否绑定了类型转换器TypeConverterAttribute,如存在,则可采用特性用法,由XAML处理器自己去进行类型转换。