|
在使用WPF进行编程的过程中,我们常常需要使用XAML的标记扩展:{Binding},{x:Null}等等。那么为什么WPF提供了XAML标记扩展这一功能,我们又如何创建自定义的标记扩展呢。这就是本文将要讨论的内容。 一.从标记扩展的分析说起 在WPF中,软件开发人员需要以类似于XML的格式编写XAML。如下面代码所示: 1 <Window …>2 <StackPanel …>3 <TextBlock …/>4 </StackPanel>5 </Window>复制代码 1 <Window …>2 <StackPanel>3 <TextBlock Text="{Binding src:DataSource.Description}"/>4 </StackPanel>5 </Window>复制代码 无论XAML的最终表示形式是怎样,编译器在处理XAML文件时所得到的都是一个个字符串。一个XML元素的开始常常表示类型实例,而以属性(Attribute)或子元素所表示的XML组成则是在对该类型实例的属性进行设置。在分析对XML属性(Attribute)进行赋值的字符串时,XAML处理器会根据字符串的内容决定自身的分析逻辑。 对于普通的属性赋值字符串,XAML处理器会根据属性的类型决定是否需要执行对字符串的转化。如果属性的类型不是字符串,那么XAML处理器会调用相应的转化逻辑,如对于枚举类型的属性,XAML处理器将通过Enum的Parse方法得到相应类型的数值。而对于自定义类型,XAML会根据该自定义类型声明或属性声明上所标明的TypeConverter将字符串转换为该自定义类型。 也就是说,可以被XAML编译器正确解释的自定义类型需要满足如下条件:属性的类型需要是值类型,具有默认构造函数的类型或者标明了专用类型转换器的类型,即标明了特性TypeConverterAttribute。 如果一个类型不能提供满足上面条件的实线,那该怎么办呢?解决问题的方法就是使用XAML标记扩展。XAML编译器会按照如下方式分析XAML标记扩展:如果XAML处理器遇到一个大括号,或者遇到一个从MarkupExtension派生的对象元素时,那么XAML编译器将按照标记扩展分析该字符串,直至遇到表示结束的花括号。首先,编译器会根据字符串决定标记扩展所对应的MarkupExtension类派生类。接下来,编译器将按照下面的规则对扩展标记字符串进行处理:1) 逗号代表各个标记的分隔符。2) 如果分隔的标记没有任何等号赋值,那么它将被视为构造函数的参数。这些参数需要与构造函数的参数个数匹配。如果两个构造函数的参数个数相同,那么XAML编译器将无法分析。该行为没有定义。3) 如果每个标记都包含等号,那么XAML处理器将首先调用默认构造函数并对这些属性进行赋值。4) 如果标记扩展同时使用了构造函数参数以及属性赋值,那么XAML处理器内部将调用对应的构造函数并对属性进行赋值。最后,编译器会在应用程序加载时调用该类型的ProvideValue()函数,用来定义该标记应该返回哪个对象。该函数调用会传入有关当前上下文的信息,以允许ProvideValue()函数根据该上下文创建相应的对象。 如果标记扩展之间存在着嵌套,那么XAML编译器将首先计算标记扩展的最内层,如下面示例将首先计算x:Static: 1 <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.Control}}"/>
二.WPF中的标记扩展 在开始讲解之前,您最好得到WPF的实现代码。虽然说本文会提供必要的代码片断,但能从全局层面上分析可能会给您更多的收获。在“从Dispatcher.PushFrame()说起”一文中,我们已经介绍了如何获得.net的源码,而在“资源下载”一文中,我们也提供了这些源码的下载地址。 首先来看看比较典型的标记扩展{x:Type}的实现: 1 [MarkupExtensionReturnType(typeof(System.Type)), 标记扩展{x:Type}的实现所展示的ProvideValue()函数实现是标记扩展实现中的典型实现。通过GetService()函数所可能得到的常用服务有:IProvideValueTarget服务,以知晓标记扩展所在的目标元素和属性;IUriContext,即可获得当前上下文中的基准Uri;IXamlTypeResolver,用来将XAML元素名称解析为.net类型实例,最典型的例子就是x:Type标记扩展。 同时上面所展示的代码使用了三个特性:ConstructorArgument、TypeConverter以及MarkupExtensionReturnType。接下来,我们就来看看这三个特性各自的功能。 首先就是ConstructorArgument特性。该特性用来提示XAML编译器标记扩展中所标示的构造函数参数实际上与哪个属性相对应。通过该特性所关联的属性则必须是一个可读写的属性。 那么问题接踵而至:ConstructorArgument特性是使用在类型为Type的属性之上,而XAML编译器所输入的则是字符串类型。为了解决这种类型上的不匹配,标记扩展TypeExtension使用了另一个特性TypeConverter提示XAML编译器使用类型转换器类型TypeExtensionConverter处理标记扩展声明中所标示的字符串类型参数。 最后一个要提及的特性就是MarkupExtensionReturnType。该特性用来标明ProvideValue()函数所返回的类型。 |





骆驼户外男 真皮磨砂日常休闲鞋 低帮 2011秋冬新款 专柜正品特价