Asp.Net教程,WinForm教程,Asp.Net MVC,vs2008教程,vs2010教程,Silverlight技术,源码下载,Asp.Net视频教程
全站热门标签
vs2010 Silverlight 存储过程 水晶报表 LINQ ADO.NET JavaScript DataGridView GridView AjaxPro DevExpress 面向对象 Extjs XML HTML教程 Oracle jQuery WPF MVC 分页 Office2010 GDI+ Visual C++2010 Dojo MySQL VB.NET WCF4.0 ASPxGridView WinForm textbox Sql2005 cookie Discuz!NT checkbox WCF SQL经典语句 T-SQL F# asp.net VS2008 SQL Ajax DropDownList VS2008新特性 TreeView Access Flex 页面执行时间 字符串 DataSet VB2005 回调 动画 C#时间 游戏 ASP.NET MVC
Framework C#技术 VB.NET VC.NET WCFWPF
当前位置: 主页 > WinForm教程 > WPF >

WPF自定义标记扩展

时间:2012-02-07 16:10来源:未知 作者:admin 点击:

 在使用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编译器是如何对XAML进行解析的讲起。

  无论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}}"/>
  可以看到,XAML编译器对属性赋值进行分析的方式主要会根据其是否是标记扩展而分为使用转化或调用标记扩展的ProvideValue()函数两种。这两种方法之间的最大不同在于ProvideValue()函数可以根据上下文提供更复杂的实例创建或引用逻辑。另外,标记扩展允许软件开发人员在XAML中使用带有一个参数的非默认构造函数。这也是标记扩展的一个优点。

 

二.WPF中的标记扩展

  在开始讲解之前,您最好得到WPF的实现代码。虽然说本文会提供必要的代码片断,但能从全局层面上分析可能会给您更多的收获。在“从Dispatcher.PushFrame()说起”一文中,我们已经介绍了如何获得.net的源码,而在“资源下载”一文中,我们也提供了这些源码的下载地址。

  首先来看看比较典型的标记扩展{x:Type}的实现:

 1 [MarkupExtensionReturnType(typeof(System.Type)),
2 TypeConverter(typeof(TypeExtensionConverter))]
3 public class TypeExtension : MarkupExtension
4 {
5     ……
6     public TypeExtension(System.Type type)
7     {
8         ……
9         this._type = type;
10     }
11
12     public override object ProvideValue(IServiceProvider serviceProvider)
13     {
14         if (this._type == null)
15         {
16             ……
17             IXamlTypeResolver service = serviceProvider.GetService(
18 typeof(IXamlTypeResolver)) as IXamlTypeResolver;
19             ……
20             this._type = service.Resolve(this._typeName);
21             ……
22         }
23         return this._type;
24     }
25
26     [ConstructorArgument("type"), DefaultValue((string) null)]
27     public System.Type Type
28     {
29         get { return this._type; }
30         set
31         {
32             ……
33             this._type = value;
34             this._typeName = null;
35         }
36     }
37     ……
38 }  首先来看看最重要的组成ProvideValue()函数。该函数首先会通过GetService()函数得到IXamlTypeResolver服务。该服务所提供的Resolve()函数会根据TypeName属性所记录的字符串解析出TypeName属性所指定的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()函数所返回的类型。

Tags:WPF
责任编辑:admin
返回顶部
------分隔线----------------------------
推荐内容
骆驼户外男 真皮磨砂日常休闲鞋 低帮 2011秋冬新款 专柜正品特价 骆驼户外男 真皮磨砂日常休闲鞋 低帮 2011秋冬新款 专柜正品特价