您的位置:首页 > 其它

阅读WPF揭秘前两章探索Silverlight运行的基本原理和RIA工作流程的密码(-)

2011-10-11 12:29 363 查看
阅读WPF揭秘前两章探索Silverlight运行的基本原理和RIA工作流程的密码 Rob Relyea的blog上的一篇文章讲到了XAML的编译方式,以及XAML, BAML, .g.cs 的由来。 XAML
原来的意思是eXtensible Avalon Markup Language,现在改成了eXtensible Application Markup Language,毕竟现在XAML已经不只是运行在Avalon中,在XP上安装了WinFX的Runtime也已经可以运行了。 编译过程(foo.xaml)
1) 解析 foo.xaml (解析器:System.Xml.XmlTextReader)
2) markup 编译器调用BAMLWriter 在obj\release\.目录产生foo.baml
3) markup 编译器生成一个Partial类保存到foo.g.cs文件中 Foo.baml?
BAML中的B表示Binary, BAML中标识了XAML中对象的层次和属性,在程序运行时BAML的载入速度要快很多。 Foo.g.cs?
.g的意思是"Generated",XAML是一种描述,最终它还是将被解释成C#,每个XAML文件实际上都是定义了一个新的类,foo.g.cs包含了foo.xaml.cs以外的同一类的自动生成的代码。
.g.cs中定义了每个元素的ID属性,加载了foo.baml文件。

Rob Relyea

xaml:Blog XAML and IL Tim Anderson asked a question on the MSDN forum for WPF. I'm not 100% clear about the relationship between XAML and IL. As far as I can tell, you can compile XAML but it ends up as BAML rather than IL. Equally, my understanding is that anything you can do in XAML you can also do in C# (declarative vs procedural). If so, presumably it would be possible to compile XAML to IL, even if this is not what happens. Another interesting question: if C# compiles to IL, but XAML does not, then what are the pros and cons - eg. performance? In WPF, XAML can be used a few ways. A) Compiled into the assembly
Use <Page Include="Page1.xaml" /> in a .csproj/.vbproj file to get it compiled into the assembly. (It will be stored as a .baml file...and it will have a corresponding .g.cs/.g.vb file). B) Included in the assembly
Use <Resource Include="Page1.xaml" /> in a .csproj/.vbproj file to get it included in the assembly, but left as a XAML text file. C) Distributed with the app, but not in the assembly.
Use <Content Include="Page.xaml" /> in a .csproj/.vbproj file to make sure click-once knows about the file (it is listed in the .exe.manifest) and will be distributed with the exe. D) Loose in the world
Don't put it in your .csproj/.vbproj...put it up on some server or in the file system. In WPF, we only support event wiring/code with A. A few blog posts that go into more details about this include: A) 3 Coding Styles for Avalon Applications
B) MarkupCompilation: XAML, BAML, .g.cs Details Yes, XAML could be compiled into all code instead of half BAML and .g.cs (in fact we used to do it that way). XPS has chosen to distribute XAML in text form in an XPS file. XPS doesn't support having IL in the XPS file.
Most WPF applications will use compiled XAML.
WPF/E will likely use an approach similar to XPS (storing XAML in text form), but will have to solve the addition of IL. XPS chose to keep XAML as an XML file because it was easier for different devices/printers to consume or create.
WPF chose to compile XAML for the perf benefit.
WPF/E plans to keep XAML as an XML file. They will get smaller download sizes than WPF since they will be in a .zip file. Parsing performance can be faster if you do work ahead of time (like we do with BAML). Posted on Mar 28 2006, 06:42 AM by rrelyea Filed under: XAML XAML是一种相对简单、通用的声明式编程语言,它适合于构建和初始化.NET对象。.NET Framework 3.0包括了一个XAML编译器和运行时解析器,还有一个插件允许你在IE浏览器中查看独立的基于WPF的XAML文件(有时叫做松散XAML页面)。 因为XAML仅仅是一种使用.NET API的方式,想把它与HTML、可伸缩向量图形(Scalable Vector Graphics,SVG)或者其他特定于域的格式或语言作比较,是完全错误的。XAML由一些规则(告诉解析器和编译器如何处理XML)和一些关键字组成,但是它自己没有任何有意义的元素。因此,如果在没有WPF这样的框架的基础上讨论XAML,就如同在没有.NET Framework的基础上讨论C#一样。 XAML在WPF中扮演的角色通常是令人困惑的,因此第一件要搞清楚的事情是WPF和XAML可以独立使用,它们并不是互相依赖的。虽然XAML最初是为WPF而设计的,但它也可以应用于其他技术(如Windows Workflow Foundation,WF)。由于XAML的通用性,实际上可以把它应用于任何.NET技术。然而,是否在使用WPF时使用XAML是可选的,每一件XAML能做的事情完全可以由任何一种你喜欢的.NET语言来实现(但是反过来则不行)。但是,由于本章开始提到的那些使用XAML的好处,很少会看到在现实世界中使用WPF但却不使用XAML的情况。 2.2 元素和特性 XAML规范定义了一些规则,用于把.NET命名空间、类型、属性和事件映射为XML命名空间、元素和特性。可以看一下下面简单(但是完整)的XAML文件,它定义了一个WPF按钮,跟另一段与之功能一致的C#代码比较一下:




虽然这两段代码功能是相同的,但你可以很快地在IE浏览器中查看XAML,还会看到一个活生生的按钮放在浏览器窗口中,如图2-1所示,而C#代码则必须要用额外的代码编译方可使用。 在XAML中定义一个XML元素(叫作对象元素)与在.NET中实例化一个对应的对象(总是使用默认的构造函数)是等价的。设置对象元素的一个特性(attribute),与设置一个同名属性(property attribute,称为属性特性)或者为一个同名事件设置一个事件处理程序(也称为事件特性),也是等价的。例如,这里对那个Button进行了更新,不仅设置了Content属性,也为其Click事件添加了一个事件处理程序。




图2-1 在.xaml文件中声明一个简单的WPF按钮





这就需要使用适当的签名定义一个叫作button_Click的方法,这也意味着XAML文件无法像图2-1那样独立呈现。本章最后的2.8节将解释如何使用需要额外代码的XAML。注意,XAML和C#一样,也是区分大小写的。 属性和事件处理的顺序 在运行时(run-time)模式下,为任何一个XAML声明的对象设置属性之前,总要添加一些事件处理程序(除了本章最后所讲到的Name属性外,该属性在对象构建之后立即被设置),这样就可以让某个事件在属性被设置时被触发,而不用担心XAML使用特性的顺序。 至于多个属性集或添加多个事件处理程序,它们总会遵照一定的顺序,即属性特性和事件特性是在对象元素中指定的。值得庆幸的是,这一排序方式不会在实际应用中产生影响,因为.NET设计指南指出:类应该允许以任何顺序设置属性,添加事件处理程序也是如此。 2.3 命名空间 比较上述XAML代码示例和相应的C#代码示例,最神秘的地方在于XML命名空间(http:// schemas.microsoft.com/winfx/2006/xaml/presentation)是如何被映射到.NET命名空间(System. Windows.Controls)上的。该映射及其他WPF命名空间的映射是在WPF程序集中硬编码完成的,里面有好几个XmlnsDefinitionAttribute自定义特性的实例。(顺便说一句,在schemas.microsoft. com这个URL中不存在网页——这仅仅是一个人为设定的字符串,就像其他命名空间一样。) XAML文件的根对象元素必须指定至少一个XML命名空间,用于验证自己和子元素。你可以(在根元素或子元素上)声明额外的XML命名空间,但每一个命名空间下的标识符都必须有一个唯一的前缀,例如,WPF的XAML文件都会使用第二个命名空间加上前缀x(记作xmlns:x而不仅仅是xmlns):




这是XAML语言命名空间,用于映射System.Windows.Markup命名空间中的类型,而且它也定义了XAML编译器或解析器中的一些特殊的指令。这些指令通常是作为XML元素的特性出现的,因此它们看上去像宿主元素的属性,但实际上并不是如此。如果想了解XAML关键字,请见2.8.3节。 我们把http://schemas.microsoft.com/winfx/2006/xaml/presentatio作为默认(主要)命名空间,把http://schemas.microsoft.com/winfx/2006/xaml 作为次要命名空间,次要命名空间的前缀是x,这仅仅是一个规则,就像C#文件要以using System;指令开始一样。你可以如下改写原来那个XAML文件,意思是相同的。





当然,从可读性来讲,在使用这些常见的命名空间的时候不需要前缀(即原始的XML命名空间),其他一些命名空间则使用一个短前缀,这样处理是有道理的。 隐式.NET命名空间 WPF把下面所有的.NET命名空间映射到XML命名空间http://schemas.microsoft.com/ winfx/2006/xaml/presentation ,而这个命名空间将在整本书中使用。 ·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 因为这是一个多对一的映射,WPF的设计者需要保证不要引入两个同样名称的类,虽然这些类是放在不同的.NET命名空间中的。 提示 本章中独立的XAML示例显式地指定了它们的命名空间,但是在后面的章节,大多数示例是假设http://schemas.microsoft.com/winfx/2006/xaml/presentation命名空间被声明为主要命名空间,而http://schemas.microsoft.com/winfx/2006/xaml则被声明为次要命名空间,前缀是x。如果你想在IE浏览器中查看这些内容,或者将它们复制到像XamlPad这样的应用程序中,请确保这两个命名空间已经显式添加。 2.4 属性元素 在第1章中提到过,富创作是WPF的亮点之一。这一点可以用图2-1中的简单Button来演示,因为你可以把任意内容放在里面,不仅限于文本!为了演示这一点,下面的代码嵌入了一个简单的方形来做一个VCR的停止按钮。




Button的Content属性是System.Object类型的,因此它很容易被设置到40×40的Rectangle对象。结果显示在图2-2中。




图2-2 用复杂内容更新WPF按钮

那非常简洁,但是你如何才能在XAML中用属性特性语法做相同的事呢?你该为Content属性设置哪种字符串才可以完成之前在C#中声明的Rectangle功能呢?没有这样的字符串,但是XAML提供了一种替代的(且更加详细的)语法来设置复杂的属性值,即属性元素。如下所示:




Content属性现在被设置为一个XML元素而不是XML特性,这与之前的C#代码是一样的。Button.Content中的句点可用于区分对象元素(object element)与属性元素(property element)。它们总是会以“类型名,属性名TypeName.PropertyName”的形式出现,总会包含在“类型名”对象元素中,但它们没有属于自己的特性。 属性元素语法也可以用于简单的属性值。下面的Button使用特性设置了两个属性,它们是Content和Background:




这等同于使用元素设置该Button的两个相同的属性:




当然,在任何可以使用特性的地方使用特性是手工输入XAML时很好的捷径。 2.5 类型转换器 让我们看一看与之前的Button声明(设置了Content和Background属性)等价的C#代码:




等一下!之前XAML文件中的“White”是如何与C#中的System.Windows.Media.Brushes. White静态值White(类型为System.Windows.Media.SolidColorBrush)等价的呢?确实,这个示例提供了一些如何使用字符串设置在XAML属性的细节,这些属性的类型既不是System.String,也不是System.Object。在这样的情况下,XAML解析器或编译器必须寻找一个类型转换器,该转换器知道如何将一个字符串表达式转换为一种想要的数据类型。WPF提供了许多常用数据类型的类型转换器,如Brush、Color、FontWeight、Point等,它们都是派生自TypeConverter的类(如BrushConverter、ColorConverter等),你也可以为自定义的数据类型写类型转换器。与XAML语言不同,类型转换器通常支持不区分大小写的字符串。 如果没有Brush类型转换器,你就必须使用属性元素语法来设置XAML中的Background属性,如下所示:




之所以这样可行,是因为有Color类型转换器来转换“White”字符串。如果没有Color类型转换器,你仍然可以写成:




之所以这也行得通,是因为有一个类型转换器可以把每个字符串“255”转换为一个Byte值,它正是Color类型的A、R、G和B属性所期望的值,没有这个类型转换器的话,你就无能为力了。因此,类型转换器并不仅仅增强了XAML的可读性,也允许一些本来不能被表达的概念得以表达。 在过程式代码中使用类型转换器 虽然设置Background为System.Windows.Media.Brushes.White的C#代码产生的结果与XAML声明中设置“White”字符串是一样的,但C#中实际上并没有用到XAML解析器或者编译器所使用的相同的类型转换机制。下面的代码更精确地表达了运行时获取和执行适合Brush的类型转换器的过程:




与之前的C#代码不同,使用XAML时,拼错White不会导致编译错误,但是会在运行时抛出一个异常。(虽然Visual Studio的确提供了XAML的编译时的错误警告。) 2.6 标记扩展 标记扩展,就像类型转换器一样,可以用于扩展XAML的表达能力。它们都可以在运行时计算字符串特性的值(除了一些内建的、为提高性能而在编译时计算的标记扩展),并生成一个合适的基于字符串的对象。就像类型转换器一样,WPF有好几个内建的标记扩展,你会发现它们都派生自本书最前面的内封中的MarkupExtension。 但与类型转换器不同的是,标记扩展是通过XAML的显式的、一致的语法调用的,因此,标记扩展是最好的扩展XAML的方法。另外,标记扩展还可以用于克服现存类型转换器的一些限制,对于这些限制本来你是无能为力的。例如,如果你想用一个简单的字符串将控件的背景色设置为漂亮的渐变笔刷(gradient brush),虽然内建的BrushConverter无法完成,但你可以写一个自定义的标记扩展来实现。 只要特性值由花括号({})括起来,XAML编译器或解析器就会把它认作一个标记扩展值而不是一个普通的字符串(或其他一些需要进行类型转换的东西)。下面的按钮使用了3个不同的标记扩展类型,其中分别用到了3个不同的特性:




每个花括号中的第一个识别符是标记扩展类的名称。按照惯例,这样的类都以Extension后缀结尾,但是当你在XAML中使用它时,可以不用该后缀。在这个例子中,NullExtension(我们看到的是x:Null)和StaticExtension(我们看到的是x:Static)是System.Windows.Markup命名空间的类,因此必须使用前缀x来定位它们。Binding(没有Extension后缀)是在System.Windows.Data命名空间下的,因此在默认的XML命名空间下就可以找到它。 如果标记扩展支持,可以使用逗号分隔的参数来指定它的值。 定位参数(例如本例中的SystemParameters.IconHeight)是被作为字符串参数传入扩展类的相应构造函数中的。命名参数(如本例中的Path和RelativeSource)可以用来在已构造好的扩展对象上设置相应名字的属性。这些属性的值可以是标记扩展值自己(使用嵌套的花括号,如RelativeSource的值),也可以是文本值,它们可以通过普通的类型转换过程。如果你熟悉.NET自定义特性(.NET Framework比较流行的扩展机制),你很有可能注意到设计和使用标记扩展与设计和使用自定义特性很相似,是有意这么设计的。 在前面的Button声明中,NullExtension允许设置Background笔刷为null,BrushConvertor和许多其他类型转换器都不支持这种情况。这里仅仅是出于演示的目的,设置Background为null没有什么意义。StaticExtension允许使用静态属性、字段、常量和枚举值,而不使用XAML写的硬编码字面值。在这个例子中,Button的高度是遵循操作系统当前的图标高度设置的,这一设置可以通过System.Windows.SystemParameters类的IconHeight静态字段获得。Binding(在第9章中将深入讲解)可以把Content设置为与它的Height属性相同的值。 摆脱花括号 如果你曾经需要设置一个属性特性值为字面值字符串(以左花括号开始),就必须摆脱它以免把它作为标记扩展。我们可以通过在其之前增加一对空花括号来实现。例如:




若不这样做,你也可以使用属性元素语法而不用任何摆脱,这是因为花括号在上下文中不会有特殊的意义。之前的Button可以用下面的代码重写(因为Content是内容属性,这里使用了隐式属性元素语法):




因为标记扩展是有默认构造函数的类,它们可以与属性元素语法一起使用,下面的Button声明与之前的声明是一样的:




该转换之所以可以执行是因为这些标记扩展都有与形参化的构造函数的实参(使用属性特性语法的定位实参)对应的属性。例如,StaticExtension有一个Member属性与之前传入到形参化构造函数中的实参意思是一样的,RelativeSource有一个对应于构造函数实参的Mode属性。 标记扩展和过程式代码 由标记扩展完成的实际工作对于每个扩展都是不同的。例如,下面的C#代码与使用NullExtension、StaticExtension和Binding的基于XAML表示的按钮是一个意思:




尽管如此,这里的代码与XAML解析器或编译器使用的机制是不同的,解析器和编译器是依靠每个标记扩展在运行时设置合适的值(本质上是通过调用每个类的ProvideValue方法实现的)。与这一机制完全对应的过程式代码通常很复杂,有时还需要只有解析器才有的上下文(例如,如何解析一个XML命名空间前缀供StaticExtension的Member属性使用)。值得庆幸的是,我们没有理由在过程式代码中这样使用标记扩展! 2.7 对象元素的子元素 XAML文件就像所有的XML文件一样,必须有一个单独的根对象元素。因此,不必感到惊讶,对象元素是可以支持子对象元素的(不只是支持属性元素,考虑XAML时,它们并不是子元素)。一个对象元素可以有3种类型的子元素:一个内容属性值,集合项,或者一个能够通过类型转换到它的父元素的值。 2.7.1内容属性 大多数WPF类(通过定制特性)指定了一个属性,该属性可以被设置为XML元素中的任何内容。这个属性叫作内容属性,它确实是一个让XAML呈现变得更轻便简单的捷径。从某种意义上讲,这些内容属性有点像(通常会被误解为)Visual Basic中的默认属性。 Button的Content属性就是这样指定的,因此下面的Button:




并不一定要把内容属性叫作Content,像ComboBox、ListBox和TabControl(也在System. Windows.Controls命名空间中)这样的类就使用Items属性作为内容属性。 1.List List是实现了System.Collections.IList接口的集合,如System.Collections.ArrayList和许多WPF定义的集合类都是List。例如,下面的XAML向ListBox添加了两个项,它的Items属性是实现了IList的ItemCollection类型:




在以上这些示例中,ListBox的Items属性会自动被初始化为一个空的集合对象,因此代码可以正常工作。如果集合属性一开始就是空的(且是可读写的,不像ListBox的只读Items属性),你应该把这些项包装进一个显式的元素中,来实例化这个集合。 2.Dictionary System.Windows.ResourceDictionary是WPF中的一个常用的集合类型,在第8章中将详细讲解。它实现了System.Collections.IDictionary接口,因此能够支持在过程式代码中添加、移除和枚举键/值对,这与创建一个典型的散列表是一样的。在XAML中,你可以向一个实现了IDictionary的集合中添加键/值对。例如,下面的XAML添加了两个Color对象到一个Resource- Dictionary中。




这利用了经过特殊处理的XAML Key关键字(在次级XML命名空间中定义),从而允许为每个Color值添加一个键。(Color类型没有定义Key属性。)因此,这里的XAML与下面的C#代码是一样的:




注意,在带有x:Key的XAML中指定的值总是被作为字符串处理的,除非使用标记扩展,但不会尝试使用类型转换。 2.7.3更多类型转换 普通文本经常被用作对象元素的子元素,例如下面的基于XAML的SolidColorBrush声明:




这与下面的代码等价:




尽管Color没有被指定为内容属性。在本例中,第一个XAML代码段之所以能工作,是因为有类型转换器存在,它会把字符串'White'(或者'white',或'#FFFFFF')转换为SolidColorBrush对象。 虽然类型转换器在实现XAML的可读性方面起了重大作用,但不足的是,它们让XAML变得更魔幻,从而更难理解XAML如何映射到.NET对象的实例。使用目前的知识,应该说你还无法声明一个抽象的XAML类元素,因为没有办法实例化它。由于System.Windows.Media.Brush是SolidColorBrush、GradientBrush和其他一些具体笔刷的抽象基类,故可以如下表示之前的XAML代码段:




这是因为Brush的类型转换器仍然将把它理解成一个SolidColorBrush。这可能是一个不寻常的特性,但是在XAML中支持原始类型表示能力很重要,详见提示栏。 XAML扩展部分 因为XAML是设计用来与.NET类型系统一起工作的,你可以在其中使用任何类型的.NET对象(甚至是COM对象,这还多亏了COM的互操作性),也可以使用自己定义的对象,无论这些对象是不是与用户界面有关。但是,对象必须以“友好声明”(declarative-friendly)的方式进行设计。例如,如果一个类没有默认的构造函数,也没有提供有用的实例属性,那么它在XAML中是无法直接使用的。设计WPF API有很多细节来适应XAML的声明模型,它超越了通常.NET的设计规范。 XAML扩展部分 因为XAML是设计用来与.NET类型系统一起工作的,你可以在其中使用任何类型的.NET对象(甚至是COM对象,这还多亏了COM的互操作性),也可以使用自己定义的对象,无论这些对象是不是与用户界面有关。但是,对象必须以“友好声明”(declarative-friendly)的方式进行设计。例如,如果一个类没有默认的构造函数,也没有提供有用的实例属性,那么它在XAML中是无法直接使用的。设计WPF API有很多细节来适应XAML的声明模型,它超越了通常.NET的设计规范。 clr-namespace标记允许直接在XAML中放入一个.NET命名空间。仅当需要的类型不在相同的程序集(XAML编译后生成的)中时,最后的程序集规范才是必须的。一般使用程序集的简单名称(如mscorlib)。但你可以使用规范的呈现方式,它是由System.Reflection.Assembly.Load提供支持的(虽然不允许空格),该函数包含了额外的信息,如版本或公共密钥令牌。 这个例子有两个关键点强调了不仅与.NET类型系统进行整合,也要与.NET Framework基类库中的特定类型进行整合。 ·子元素可以使用标准的XAML x:Key语法添加到父Hashtable中,因为Hashtable和.NET Framework中的其他集合类从1.0版本开始就实现了IDictionary接口。 ·之所以可以这么直接使用System.Int32,是因为类型转换器已经存在,且类型转换器是 支持将字符串转换为整型的。因为XAML支持的类型转换器都是派生自System.Component- Model.TypeConverter的类,这个类从.NET Framework 1.0开始就有了。这其实也是 Windows Forms使用的类型转换机制(例如,允许在Visual Studio的属性格中输入字符串, 并将它们转换为适当的类型)。 对象元素的子元素的XAML处理规则 你现在已经看过3种类型的对象元素的子元素了。为了避免混淆,当转换子元素时,任何一个有效的XAML解析器或者编译器必须遵循下面的规则: (1) 如果该类型实现了IList接口,就为每个子元素调用IList.Add。 (2) 否则,如果该类型实现了IDictionary,就为每个子元素调用IDictionary.Add,在该值的键和元素中使用x:Key特性值。 (3) 否则,如果父元素支持内容属性(由System.Windows.Markup.ContentPropertyAttri- bute表示),而且子元素的类型与该内容属性是兼容的,就把子元素作为它的值。 (4) 否则,如果子对象是普通文本,且有类型转换器将子对象转换为父类型(没有在父元素上设置属性),则把子元素作为类型转换器的输入,将输出作为父对象的实例。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: