说明:本系列基本上是《WPF揭秘》的读书笔记。在结构安排与文章内容上参照《WPF揭秘》的编排,对内容进行了总结并加入一些个人理解。
本系列记录了个人学习
学习过程由XAML语言开始
作为一种由Xml衍生的语言,首先我们了解一下XAML在WPF与Silverlight中命名空间的定义方式。XAML使用XML命名空间来表示.NET的命名空间。首先看一下VS等开发工具集成的WPF项目模板中XAML的命名空间。
VS2008 SP1中文版的WPF3.5模板/ Expression Blend 3中文版的WPF模板
Window1.xaml
<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> |
App.xaml
<Application x:Class="WpfApplication1.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Window1.xaml"> |
VS2008 SP1中文版的Sivlerlight 3模板/Expression Blend 3的Sivlerlight程序模板
MainPage.xaml
<UserControl x:Class="SilverlightApplication1.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"> |
App.xaml
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="SilverlightApplication1.App"> |
而MainPage.xaml稍有不同:
下面开始分析这些XAML的命名空间:
对于WPF来说:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
此命名空间是XML(XAML)文档的命名空间,用于验证该XAML(XML)文件中元素(element)是否属于WPF/Silverlight中定义的。这样定义后除非另有说明,否则XAML的节点元素的名称都能映射到.NET中类名(元素的Attribute会对应到.NET中相应类的Property或Event名称)。
这个XMAL命名空间映射到如下.NET命名空间:
System.Windows System.Windows.Automation System.Windows.Controls System.Windows.Controls.Primitives System.Windows.Data System.Windows.Documents System.Windows.Forms.Integration System.Windows.Ink System.Windows.Input System.Windows.Media System.Windows.Media.Animation System.Windows.Media.Effects System.Windows.Media.Imaging System.Windows.Media.Media3d System.Windows.Media.TextFormatting System.Windows.Navigation System.Windows.Shapes |
由于一个XML命名空间会映射到如上多个.NET命名空间,所以这些.NET命名空间不能存在名称相同的类来,以保证在同一XAML命名空间下的类不会出现冲突。这样多对一映射的目的是为了避免在XAML需要引入过多的命名空间。XAML命名空间到.NET Framework的映射是在WPF中通过硬编码完成的。
接着来看第二个命名空间:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
除了第一个默认命名空间,其它命名空间都需要有一个唯一的前缀,这个命名空间使用了x(xmlns:x)作为前缀。这个XAML的命名空间映射到System.Windows.Markup,其定义了XAML解析器中的一些关键字/特殊的指令,它们通常作为xml的特性(Attribute)出现。这些关键字控制元素如何被提供给过程式代码。这一命名空间不专门针对WPF,该命名空间下的元素也不是都具体映射到某一个类。这个命名空间的别名x并不是强制的,而是一种约定俗成的做法,可以修改为自己喜欢的值。
从设计上看,这种使用主(无前缀)次(有前缀)命名空间的方式在Xml中比较常见。
对于Silverlight来说:
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation |
此XML默认命名空间映射到整个Silverlight类库的命名空间。
同样这个使用x作为前缀的XML命名空间也映射到Silverlight类库的类型及其中的属性,用于验证XAML中Element、Attribute的。另外一个比较特殊的d开头的命名空间:
其是用于在Visual Studio或Blend等开发工具中进行智能感知,这使开发工具对于Silverlight的智能感知与WPF的智能感知有所区别(Silverlight智能感知基于Silverlight自己相应版本的类型)。
这个主命名空间与别名为x的辅助命名空间的关系是怎样的呢?XAML是一个语言定义,而WPF/Silverlight是将XAML用作语言的一个实现,WPF/Silverlight使用了XAML的一个严格子集。XAML语言指定某些语言元素,其中的每个元素都应当可以通过针对XAML命名空间执行的XAML处理器来进行访问。XAML的WPF/Silverlight实现及其预定义的编程模型通常对自己的XAML词汇表使用默认的XML命名空间。
XMAL中命名空间的使用在.NET 3.0版本开始增加的另一项技术Windows Workflow中也有体现,我们看一段用于工作流的XAML的命名空间的定义:
<SequentialWorkflowActivity xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="WorkflowConsoleApplication1.Workflow2" Name="Workflow2"> |
同样为了避免不同操作实例在名称上的冲突,通过xmlns顶一个命名空间。主命名空间定义了XAML中可以作为节点元素的一些类,这个命名空间被映射到.NET Framework中System.Workflow.Activites命名空间。前导符为x的辅助命名空间被映射到可以解释XAML中Attribute等成员的类上。像x:Name这样的元素将有XAML来解释。同理其中的x:Class就是通过XAML通知WF编译程序在处理时要按照到这个Attribute的值(即WorkflowConsoleApplication1.Workflow2)来生成类型名称。
下表列出了XAML语言命名空间(即x:)支持的关键字(表格摘自《WPF揭秘》)
关键字 |
作用位置 |
含义 / 描述 |
x:Class |
根元素的Attribute |
为根元素定义一个派生自元素类型的类,可以在前面加上.NET命名空间作为前缀(可选) |
x:ClassModifier |
根元素特性,需与x:Class一起使用 |
定义由x:Class指定的类的可见性(该类默认是可见的)。该特性值必须根据使用的过程语言指定(如C#的public或internal等) |
x:Code |
任何位置元素,需与x:Class一起使用 |
嵌入过程式代码,会被插入由x:Class指定的类中 |
x:FieldModifier |
非根元素的任意元素的Attribute,需与x:Name或等效关键字一同使用(注1) |
定义生成的元素(默认是内部元素)字段的可见性,与x:ClassModifier一样,该值必须根据过程语言来指定。(如C#中的public、private等) |
x:Key |
父元素实现了IDictionary的元素的Attribute |
当被添加到父元素的字典里时,请为该项指定键名 |
x:Name |
非根元素的任意元素的Attribute,需与x:Class一起使用 |
为给元素生成的字段选择一个名称,这样它就可以在过程式代码中被引用 |
x:Shared |
Resource-Dictionary对象中的元素的Attribute,只有在XAML编译后才可使用 |
可以被设置为false来避免在多个地方共享同资源实例,在第8章中有所讲解 |
x:Subclass |
根元素的特性,需与x:Class一起使用 |
为保存XAML内容的x:Class类指定一个子类,可以用.NET命名空间作为可选前缀(用于那些没有提供部分类支持的语言) |
x:TypeArguments |
根元素的特性,需与x:Class一起使用 |
使根类成为泛型(如List<T>)且带指定的范型参数实例(如List<Int32>或List<String>),可以设置一个用逗号分割的泛型参数代码清单,如果某类型不在默认的命名空间里,需要加上XML命名空间前缀 |
x:Uid |
元素的特性 |
为元素添加一个本地化ID |
x:XData |
用于某个IXmlSerializable类型属性值的元素 |
对XAML解析器透明的任一个XML数据岛 |
以下标记扩展(非关键字)也位于XAML命名空间,也采用x:前缀,它们在介绍标记扩展的文章中有详细说明,完成起见也列于此。
扩 展 |
含 义 |
x:Array |
代表一个.NET数组。x:Array元素的子元素都是数组元素。它必须与x:Type一起使用,用于定义数组类型 |
x:Null |
表示一个空引用 |
x:Static |
引用在过程式代码中定义的任何一个静态的属性、常量或枚举值。在XAML编译后,这也可以是同一个程序集中的一个非公共成员。如果在默认的命名空间中没有该类型,Member字符串(即设置给这个标记扩展的参数值)必须有XML命名空间前缀 |
x:Type |
表示System.Type的一个实例,就像C#中的typeof操作符。如果在默认的命名空间中没有该类型,TypeName字符串必须有XML命名空间前缀 |
下面对命名空间中出现的一些属性做进一步分析:
x:Class="SilverlightApplication1.App" |
前文已经提到,这个语句定义了这个XAML页所使用的部分类的名称,为什么可以这样写呢?在上文说过,x:Class就是通过XAML通知编译程序在处理时要对照到这个Attribute的值生成类型名称。这样写就可以在编译时将这个类与后台代码页中的类合在一起。
后置代码文件生成的类:
namespace SilverlightApplication1 { public partial class App : Application { } } |
注意当不需要在代码隐藏文件中编写代码时(即不需要隐藏代码文件时),可以不用通过x:Class设置类的名称,编译器会自动为其指定一个名称。
x:Class属性用来通知XAML编译器,让它基于XAML文件产生一个类的定义,其属性值决定了产生类的名称,这个类型继承自根类型(即当前XAML的根元素)。就相当于使用如下的C#语句来声明一个这个类的对象。(只不过这个构造过程由XAML解释器隐式完成。)
Window1 window = new Window1(); |
接下来我们从XAML的命名空间与.NET程序集的映射方式说起。之所以这些.NET程序集(包括WPF,Silverlight及WF)可以被XAML命名空间所映射,是应为它们都标注了XmlnsDefinitionAttribute这个属性。如果要在XAML中使用其他没有标记这个属性的.NET程序集(如我们自己的编写的程序集),则我们需要按如下方式来导入.NET的命名空间到XAML中。下面的例子展示了通过xmlns标记来导入其它程序集的命名空间,当在XAML中使用另一个命名空间中的一个类时需要使用如下方式来导入这个命名空间:
xmlns:ns0="clr-namespace:OtherNS" |
(注:当导入的命名空间与这个XAML不位于一个程序集时,还需要指明其所在的程序集,在本文最后所附的小例子中有体现)
当导入一个命名空间后就可以使用如下方式定义节点,这个节点的类来自导入的命名空间中。
<ns0:SeClass1 name="" … /> |
上面的例子中,我们使用clr-namespace这个关键字指定要导入XAML的外部命名空间,接着介绍种更据XML风格的方法。
首先我们在程序集的AssemblyInfo.cs中定义如下声明:
[assembly: XmlnsDefinition( "http://lsxqw2004.cnblogs.com/Silverlight", "lsxqw2004.Silverlight")] |
这样就可以在XAML文件中使用如下声明来倒入上面的程序集
xmlns:ns0=http://lsxqw2004.cnblogs.com/Silverlight |
这样XAML中就引入了lsxqw2004.Silverlight这个程序集。
这里对XAML命名空间与程序集映射的内部工作原理做简单介绍,XAML分析器会在编译时通过查找程序集的描述说明建立一份XAML命名空间与.NET命名空间的对应关系,并正确的将 http://schemas.microsoft.com/winfx/2006/xaml/presentation映射到相应的程序集上。
这种XAML命名空间与.NET命名空间的映射使用如下XML的处理指令来完成:
<?Mapping XmlNamespace="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ClrNamespace="System.Windows"?> |
对于注册到GAC的程序集可以按如上写法不指定具体的Assembly。对于自己编写的程序集需要按如下写法:
<?Mapping XmlNamespace="http://http://lsxqw2004.cnblogs.com/Silverlight" ClrNamespace="MyDllNamespace" Assembly="MyCustomAssemblyLibrary"?> |
这样指明导入的命名空间所在的程序集供XAML编译器查找。
需要注意的是XML处理指令中,?与处理指令Mapping之间不能有空格。
可以直接在XAML通过这种方式导入外部程序集,而不使用上文提到的在项目程序集中使用"assembly: "这个Attribute的方式。
最后我们看一个将XAML映射到.NET程序集,并使用XAML表示C#代码的小示例:
首先是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> |
示例中使用的导入命名空间的方式在前文中已介绍,不再赘述,还有需要注意的就是前文提到的这种两个命名空间不在同一程序集的情况,需要在命名空间后面指明程序集:使用assembly关键字,且使用程序集的短名称即可。
另外说一个后文会介绍到的问题,XAML使用的类型转化器,上面代码中的这个语句:
<sys:Int32 x:Key="key1">7</sys:Int32> |
之所以可以实现就是因为类型转化器将字符串(其中那个7)转化为整型。
最后来说一个有趣的话题
在XAML代码中常会见到这两种Attribute的写法x:Name与Name。到底它们有什么区别呢?
通过前文分析,我们知道XAML默认命名空间下的Name Attribute映射到相应.NET类型对象的Name Property。通过设置这个Attribute我们就可以设置.NET的Property。
而x:Name是XAML的命名空间,设置这个属性XAML也会把其映射到.NET类型的Name Property(这是一个特殊的机制)。而且使用x:Name的一个好处是可以在XAML设计过程中使用这个Name来查看,区分各个XAML的对象。这样一个"Name"用于XAML与.NET两不误。
补充知识:
XAML中还支持以xml:关键字开头的特殊指令,其由W3C组织定义,默认映射到XML命名空间(http://www.w3.org/XML/1998/namespace)中,用于标准XML文档,其中XAML支持的xml:space用于控制空白字符的解析,xml:lang用于声明文档语言和文化。
本文到此结束,下一篇继续XAML的话题。
注1:所谓等效关键字,可以参加本系列第5篇文章,其中介绍了指定元素Name的方法(包括但不限于x:Name)。
参考:
《WPF揭秘》
《Silverlight2完美征程》
《Essential Silverlight 2中文版》