
1.4 XAML解说
在Silverlight中有个技术不容忽视,那就是XAML技术,应该说这个技术是微软专门为.NET 3.0定做的一套技术。在.NET 3.0里随处可见这种XML文件的身影,作为预备知识,本章将要介绍这一部分内容。
首先,让我们来介绍一下什么是XML
1.4.1 XAML概说
XAML(发音是“Zamel”)是标准的扩展标记语言,它是微软用于定义新的用户界面而特意开发的。该语言开发的目的为了能像MVC结构一样,开发出一个界面与用户行为逻辑分离的应用程序。MVC结构的模型、控制器、视图结构很好地让开发人员、设计人员、数据库应用人员各行其是,彼此相得益彰地工作;而XAML很希望在视图这一层提供更好的用户体验。
XAML是微软为了定义用户界面而创建的一个单独的工具,这个工具的设计初衷是在Vista上基于.NET Framework而开发的新产品,应该说这一本来被命名为Avalon的产品呈现了基于.NET Runtime Framework的新用户表示层,这一表示层基于严格的XML语法,使用类似<Button></Button>的方法显示一个按钮。这一标记会直接等同于new一个System.Windows.Controls.Button类的新实例。这一运作机制由Common Language Runtime(CLR)来完成,CLR大家可以理解为类似Java的Java虚拟机,但不同于Java虚拟机的是,CLR不但可以运行C#,也可以运行其他多种语言。
由于XAML的运行本质是CLR把它翻译成CLR对象,所以基本上所有的XAML都可以用CLR支持的原有对象重写,这也就是为什么我们既可以用VS 2008开发应用,也可以用Blend这种设计工具进行开发的原因。同时由于XAML对象本身所具有的XML属性,所以依然可以使用传统的DOM对象(文档模型)来进行XAML的访问,这也为我们操作各个元素的属性以及添加新对象提供了方便。
目前,微软对XML的支持除了常见的DOM之外,还有新的数据访问方式LINQ,通过这一方式访问XML会有很多更便捷的方式,不过LINQ不是本书的重点,所以在这里我们暂不介绍。
事件处理模型也是XAML标记本身无法办到的事情,为此Silverlight使用了大量后置的逻辑代码,这方面很类似于微软的ASP.NET时代提供的前置ASPX加后置CS或者VB代码的风格,只是由于两个版本的Silverlight在处理的时候略有不同,所以需要稍微分别对待。
XAML类似于在互联网上使用的其他语言,可以使用CSS进行格式化输出操作。
XAML还具有面向对象的继承性,可以通过继承的方式实现对象的重载。
这种继承性使XAML方便地实现了对象重载,我们可以想象出对通过矩形对象的继承而衍生出各种更复杂的多边形对象,通过圆形对象继承出椭圆形对象的效果,所以说XAML的继承本身也大大增强了其可用性。下面我们来一一介绍一下XAML的各个方面。
1.4.2 XAML的元素与属性
XAML的特定规则定义基于XML定义,把数据对象、数据属性以及事件模型通通定义成一个XML标记。这意味着无论何时应用这种标记都必须按照严格的语法规范使用属性,例如,某些属性要求的数据类型是字符串,而此时开发者如果忘记写入双引号,则会导致程序报错。在Silverlight中这一应用尤为重要,因为无论是内嵌于页面的表示文件,还是单独的XAML演示文件都会对这些属性进行检查,一般在开发JavaScript的时候我们都对大小写敏感投入了足够的注意力,但是由于一些编译器对XAML没有语法点亮功能,所以很多时候在XAML中使用注意这些情况就显得尤为重要。在下面的介绍中我会特别提醒大家注意一些容易写错的地方。
下面我们写一段简单的XML代码说明一下情况。
<Ellipse xmlns="http://schemas.microsoft.com/client/2007" Fill="Black"Width="300" Height="100"/>
这是一段很标准的XAML代码,用于绘制一个黑色的椭圆。在XAML中使用这种语句定义一个图形类似于在面向对象语言中使用带参数的构造函数构建一个新的对象示例,如果说这两种方法有什么本质不同的话,面向对象语言在编译的时候不会马上申请一块内存空间,而XAML则会在第一时间render一个图形。
在这段代码中对象Ellipse的所有属性被赋值,这些赋值是基于严格的XML语法的。也就是说,首先这些赋值的数值是大小写敏感的,比如Fill的对应值是Black,如果写成black可能就有问题,因为对应的集合对象里可能没有这个数值。当然读者如果试验一下会发现情况还没那么糟糕,不过这只能说明在设置这个集合对象的时候开发者较为体贴地考虑到了各位的拼写习惯,不信的话,读者可以尝试设置为BlAck,这么做就有藐视开发者英语水平的嫌疑,所以保证不能被识别。除了大小写敏感之外,所有的属性还应该由" "或者' '包裹,否则将被视为无效属性,一般的语法点亮机制都可以显示这一错误。
除了设置属性外,还可以设置一些对象处理函数,这些函数负责处理对象的一些操作,比如下面这个例子。
<Ellipse xmlns="http://schemas.microsoft.com/client/2007" Fill="Black"Width="300" Height="100” MouseEnter="onMouseEnter"/>
这里指定了MouseEnter事件所对应的处理函数为onMouseEnter,这是一个JavaScript函数,关于JavaScript函数的使用情况,我们会在后面的事件及响应章节做专门的介绍。不过,需要注意的是,不要忘记在宿主页面中加入对含有这个JavaScript函数文件的引用,否则编译器会在响应的时候报告找不到应用。
顺便提示一件事情,如果用IE打开一个XAML文件,则该文件会被以WPF的方式打开,而不是用Silverlight打开。本书的大部分例子都是单独的XAML文件,读者可以根据自己的爱好加载这些内容。不过,需要读者注意的是,双击XAML文件的时候文件会被认为是WPF的单独页面文件,而且在很多情况下WPF文件和Silverlight文件在呈现方式上会有若干区别,所以这样打开的文件可能会报告错误,请读者一定要把宿主文件装配上再进行阅读。
1.4.3 XAML名字空间
XAML的根节点必然是一个XML的名字空间,因为XML文件需要根据不同的名字空间来进行判断,检验自己的格式是否符合要求以及子节点的情况是否符合要求,这是XML的基本描述情况,从这个角度上说,xmlns这一属性就显示出其特殊的意义。
xmlns属性是专门用于定义XML文档中元素所属的名字空间的,对名字空间没有概念的读者可以把名字空间理解为一个远程的语法检查器。XML的诞生源于广大用户对HTML的巨大失望,原因之一就是对HTML松散的语法结构表示出失望,而从XML诞生的第一天开始,其对各个元素和内容的严格性就成为了与生俱来的优势,这一严格性的统一标准就是通过名字空间来实现的。名字空间是一段放置于网上的语法检查策略,基于scheme策略(不了解也没太大关系),每个XML文件被加载的时候都会通过下载这一语法检查器来检查其语法内容,主要是各个属性对应的数据类型是否合法,子节点的定义是否合适,有没有闭合的标记等。微软提供的对于Silverlight的名字空间位于http://schemas.microsoft.com/client/2007,你可以使用针对于wpf/e的http://schemas. microsoft.com/winfx/2006/xaml/。原则上说,第一个名字空间是微软用于专门支持Silverlight的,而第二个名字空间是在.net 3.0下支持wpf的,不过微软的官方文档大多建议使用第一个名字空间。因为第一个名字空间支持一种叫做“Markup Compatibility”的技术。何谓“Markup Compatibility”呢?这个技术的中文翻译为“增强标记兼容性”。
写过XAML的朋友应该都知道:在XAML中可以通过<!--****-->标记来实现注释。但是,利用XAML标记兼容性,还提供了其他更加强大的注释功能。
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:mc=http://schemas.openxmlformats.org/markup-compatibility/2006 xmlns:c="Comment" mc:Ignorable="c"> <Canvas> <Button c:Width="100" Height="50">Hello</Button> </Canvas> </Window>
看见了Width前面的c前缀吗?它的作用就是注释掉Width属性。是不是感觉比标记注释的方法简单。而且这个c前面不但可以应用在属性上,也可以直接应用在实例上。例如:
<Window xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:mc=http://schemas.openxmlformats.org/markup-compatibility/2006 xmlns:c="Comment" mc:Ignorable="c"> <Canvas> <c:Button Width="100" Height="50">Hello</c:Button> </Canvas> </Window>
上面的代码就全部注释掉了Button实例。当然,这种方法不建议在最后的发布XAML文档中出现,只适合在XAML文档的开发过程中使用。
在某些情况下,Silverlight还使用x标记引入第二个名字空间:
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
xmlns:x的作用也是指定名字空间。这里为什么是x而不是其他呢?,我们可以简单地理解为其只是MS的一个命名而已,没有任何特殊的意义,当然,为了避免和它冲突,我们定义自己的名字空间的时候不能用x。
这就是XAML语言的名字空间,一个可以定义XAML语法解析器的专用位置。其实名字空间的更大用途在于Silverlight 2中和.NET的Assemblage进行整合,而在Silverlight 1.0中仅仅用于检查语法的正确性不需要定义第二个名字,也就是用x标记来定义一个元素,而在2里这一技术则应用比较频繁。
1.4.4 属性元素
通过上面的例子大家可能有一种想法,就是XAML的属性元素肯定是一些字符串,实际情况远比这个复杂得多。比如,Ellipse对象具有一个名为RenderTransform的属性(关于这一属性的应用,我们在介绍动画的时候再详细讲解),这个属性要求其值为一个RotateTransform或者一个ScaleTransform对象的实例,如何把一个对象
分配给元素的属性呢?这是一个比较复杂的问题。
RenderTransform="<RotateTransform Angle="45" CenterY="60"/>"
这样的写法从语法上说是没有问题的,但是很遗憾的是,编译器不能认可这些语句,因为RotateTransform对象并没有在被赋予给RenderTransform之前进行初始化。也就是说,这类似于在面向对象程序中仅仅声明了一个变量而没有"new"它,就直接将该变量作为参数传送给了一个对象,编译器肯定会在这个时候报错。所以正确的写法应该是:
<Ellipse.RenderTransform> <RotateTransform Angle="45"CenterY="60"/> </Ellipse.RenderTransform>
这种写法的语法是用“对象名.对象属性”标记作为属性设置的区域空间,在其中以对象的声明方式说明一下对象的内容,并把声明的对象作为属性的参数。这里要顺便说一下,像RotateTransform这样的元素,属于UI表示层属性值元素,类似于联合体对象中的内容,不能作为XAML根节点元素,一旦单独声明该元素将无法通过xmlns指定的解释器的语法校验。
属性元素中的类型转换也是一个常见的问题,在Silverlight中各个元素的属性不单单只是一般的数字或者字符串,而可能是各种各样的复杂数据类型,这一点在刚刚介绍的例子中也有明显的体现。除了像类的实例有非常特别的形式之外,还有一些如同刚才在椭圆对象属性中颜色属性所使用的“orange”值,该值是一个在System.Color名字空间下的属性,这种属性类似于普通编程语言中的联合体对象,取值只能在某个范围内。可是前面的例子中我们刚刚说过这种填充对象本身必须是一个“SolidColorBrush”(这是一个用于绘制颜色的类型),这里为什么使用一个字符串就可以实现呢?这其实就是XAML在内部对这个字符串进行了转义操作。XAML语法编译器自动判定在这种情况下使用的数据类型,并对String类型的“Orange”进行了转换,转义成为6位的RGB颜色,用于描述刷子的颜色。在HTML页面中可以用两种方式指定颜色──以颜色名称或者表示RGB颜色值的数字。一个RGB颜色值由3个两位十六进制数字组成,分别代表各自的颜色强度。
例如,颜色值#FF0000之所以被渲染为红色,是因为红色的值达到了最高值FF(等于十进制的255)。
当你使用!DOCTYPE声明指定为标准兼容模式时,Microsoft Internet Explorer 6和以后版本将忽略不遵从样式表(CSS)级别1的样式表声明。根据CSS 1,以十六进制RGB值指定的颜色必须带有前导“#”字符。像“FFFFFF”这样的值将被忽略,而不会被理解为“#FFFFFF”(即白色),但Internet Explorer的先前版本是这么理解的。这影响所有接受颜色值的属性。
注意 尽管这些颜色名称可能不被其他浏览器使用,但 RGB 颜色值应该可以在不同浏览器上正确显示。如果想要为跨越不同浏览器工作的 Web 页指定颜色值,那么应该使用 RGB 颜色值。在 Silverlight 的应用中不但有 6 位的RGB颜色,还有用于重叠效果的8位RGB颜色,8位RGB在6位RGB的基础上,在颜色开头说明上加入了2位Alpha值,用于计算颜色叠加时的效果。
(这一部分内容在后面介绍刷子对象的时候我们再深入探讨。)
<Ellipse xmlns=http://schemas.microsoft.com/client/2007 Width="400"Height="500"> <Ellipse.Fill> <SolidColorBrush Color="Orange"> </Ellipse.Fill> </Ellipse>
这段代码和上面使用“SolidColorBrush”对象设置椭圆颜色的效果是完全一样的。XAML的自动类型转换功能帮助我们对Orange进行了转义,这样不但缩短了XAML文件的长度,也增强了文件本身的可读性,应该说是一举两得的好事。当然实际情况中由于Silverlight 1.0使用JavaScript作为后台的支撑程序,所以难免不能把所有的数据类型都做好相应的转换。不过,在Silverlight的新版本中,所有内容的属性对象都将成为类的形式,也就是说,各种属性要么是类的示例,要么是类对象的属性,总之在使用上会比现在的情况方便很多。关于这些内容,我们在后面介绍的时候还会详细说明。另外需要说明的是,Silverlight的属性同样是大小写敏感的,这样增加了开发人员的灵活性。
子元素也是Silverlight中xaml的重要组成部分,在HTML时代对一个HTML文档的解析就可以做成DOM层的应用,而DOM对象(文档对象模型)更是Web应用中一个非常重要的技巧。在这里我们首先介绍一下基于HTML的DOM解析。
下面这段示例代码使用了DOM级别1的方法,从JavaScript动态创建了一个HTML表格。它创建了一个包含4个单元的小表格,并且在每个单元中含有文本。单元中文字内容是“这个单元是y行x列”,来展示单元格在表格中所处的位置。
<head> <title>样例代码 - 使用JavaScript和DOM接口创建一个HTML表格</title> <script> function start(){ // 获得body的引用 var mybody=document.getElementsByTagName("body").item(0); // 创建一个TABLE元素 mytable = document.createElement("TABLE"); // 创建一个TBODY元素 mytablebody = document.createElement("TBODY"); // 创建所有的单元格 for(j=0;j<2;j++){ // 创建一个TR元素 mycurrent_row=document.createElement("TR"); for(i=0;i<2;i++){ // 创建一个TD元素 mycurrent_cell=document.createElement("TD"); // 创建一个文本(text)节点 currenttext=document.createTextNode("cell is row "+j+", column"+i); // 将我们创建的这个文本节点添加在TD元素里 mycurrent_cell.appendChild(currenttext); // 将TD元素添加在TR里 mycurrent_row.appendChild(mycurrent_cell); } // 将TR元素添加在TBODY里 mytablebody.appendChild(mycurrent_row); } // 将TBODY元素添加在TABLE里 mytable.appendChild(mytablebody); // 将TABLE元素添加在BODY里 mybody.appendChild(mytable); // 设置mytable的边界属性border为2 mytable.setAttribute("border","2"); } </script> </head> <body onload="start()"> </body> </html>
注意我们创建元素和文本节点的顺序:
首先我们创建了TABLE元素,然后创建了TABLE的子元素TBODY。接下来,我们使用循环语句创建了TBODY的子元素TR。对于每一个TR元素,我们使用一个循环语句创建它的子元素TD。对于每一个TD元素,我们创建单元格内的文本节点。现在,我们创建了TABLE、TBODY、TR、TD等元素,然后创建了文本节点;下面,我们将每一个对象接在各自的父节点上,使用逆序:
首先,我们将每一个文本节点接在TD元素上:
mycurrent_cell.appendChild(currenttext);
然后,我们将每一个TD元素接在它们的父TR元素上:
mycurrent_row.appendChild(mycurrent_cell);
接下来,我们将每一个TR元素接在它们的父TBODY元素上:
mytablebody.appendChild(mycurrent_row);
下一步,我们将TBODY元素接在它的父TABLE元素上:
mytable.appendChild(mytablebody);
最后,我们将TABLE元素接在它的父元素BODY上。
mybody.appendChild(mytable);
请记住这个机制,你将会在W3C DOM编程中经常使用它。
首先,你从上到下地创建元素;然后,你从下向上地将子元素接在它们的父元素上。
下面是由JavaScript代码生成的HTML代码。
<TABLE border=5> <tr><td>cell is row 0 column 0</td><td>cell is row 0 column 1</td></tr> <tr><td>cell is row 1 column 0</td><td>cell is row 1 column 1</td></tr> </TABLE>
下面是由代码生成的TABLE及其子元素的DOM对象树,如图1-7所示。

图1-7 DOM对象解析图
你可以只用一些DOM方法来创建这个表格和它内部的子元素。请在脑海中时刻保留你想要创建的数据结构的树的模型,这样有利于更简便地写出必需的代码。在图1-7所示的DOM对象树中,TABLE有一个子元素TBODY;TBODY有两个子元素TR;每一个TR又含有一个子元素TD。最后,每一个TD有一个子元素——文本节点。
1.基本的DOM方法(如下面代码所示)
getElementsByTagName是文档接口(Document interface)和元素接口(Element interface)中的方法,所以不管是根文档对象还是所有的元素对象都含有getElementBy TagName方法,用来通过它们的标签名称(tagname)来获得某些元素的一系列子元素。可以使用的方法是:element.getElementsByTagName(tagname)。
getElementsByTagName返回一个有特定标签名称的子元素列表。从这个子元素列表中,可以通过调用item和你想得到的元素的下标,来获得单个元素。列表中第一个元素的下标是0。上面的方法很简单,但是当你操作一个巨大的数据结构时还是应该小心一些。OK,下面我们继续对表格例子进行修改。下面的示例更加简单,它展示了一些基础的方法。
<html> <head> <title>样例代码 - 使用JavaScript和DOM接口操作HTML表格</title> <script> function start(){ // 获得所有的body元素列表(在这里只有一个) myDocumentElements=document.getElementsByTagName("body"); // 我们所需要的body元素是这个列表的第一个元素 myBody=myDocumentElements.item(0); // 现在,让我们获得body子元素中所有的p元素 myBodyElements=myBody.getElementsByTagName("p"); // 我们所需要的是这个列表中的第二个单元元素 myP=myBodyElements.item(1); } </script> </head> <body onload="start()"> <p>hi</p> <p>hello</p> </body> </html>
在这个例子中,我们设置变量myP指向DOM对象body中的第二个p元素。
首先,我们使用下面的代码获得所有的body元素的列表,因为在任何合法的HTML文档中都只有一个body元素,所以这个列表是只包含一个单元的。
document.getElementsBy TagName("body")
然后,我们取得列表的第一个元素,它本身就是body元素对象。
myBody=myDocument Elements.item(0);
接下来,我们通过下面代码获得body子元素中所有的p元素。
myBodyElements= myBody.getElementsByTagName("p");
最后,我们从列表中取第二个单元元素。
myP=myBodyElements.item(1);
一旦取得了HTML元素的DOM对象,你就可以设置它的属性了。比如,如果希望设置背景色属性,则只需要添加:
myP.style.background="rgb(255,0,0)"; // 设置inline的背景色风格
(1)使用document.createTextNode(..)创建文本节点
使用文档对象来调用一个createTextNode方法并创建自己的文本节点。你只需要传递文字内容给这个函数,返回的值就是一个展示那个文本节点信息的对象。
myTextNode=document.createTextNode("world");
这表示你已经创建了一个TEXT_NODE(一个文字片断)类型的节点,并且它的内容是“world”,你对任何myTextNode的引用都指向这个节点对象。如果想将这个文本插入到HTML页面中,还需要将它作为其他节点元素的子元素。
(2)使用appendChild(..)插入元素
通过调用myP.appendChild([node_element]),你可以将这个元素设置成第二个元素的一个新的子元素。
myP.appendChild(myTextNode);
在测试了这个例子之后,我们注意到,hello和world单词被组合在一起:helloworld。事实上,当你看到HTML页面时,hello和world两个文本节点看起来更像是一个节点,但是请记住它们在文档模型中的形式是两个节点。第二个节点是一个TEXT_NODE类型的新节点,也是第二个p元素的第二个子元素。
createTextNode和appendChild是在单词hello和world之间设置空格的一个简单方法。另外一个重要的注意事项是:appendChild方法将把新的子节点接在最后一个子节点之后,正如world被加在了hello之后。所以如果想在hello和world中间添加一个文本节点的话,你应该使用insertBefore方法来替代appendChild。
(3)使用文档对象和createElement(..)方法创建新的元素
你可以使用createElement来创建新的HTML元素或者任何其他元素。比如,如果想要创建一个新的p作为BODY的子元素,你可以使用前面例子中的myBody并给它接上一个新的元素节点。使用document.createElement("tagname")可以方便地创建一个节点。如下:
myNewPTAGnode=document.createElement("p"); myBody.appendChild(myNewPTAGnode);
(4)使用removeChild(..)方法移除节点
每一个节点都可以被移除。下面的一行代码移除了包含在myP(第二个p元素)下面的文本节点world。
myP.removeChild(myTextNode);
最后,你可以将myTextNode(那个包含了world单词的节点)添加给我们最后创建的p元素:
myNewPTAGnode.appendChild(myTextNode);
被修改的对象树的最后状态如图1-8所示。

图1-8 DOM树修改后结果
2.动态创建一个表格
下面我们将继续修改上面的代码。
创建表格的基本步骤如下。
① 获得body对象(文档对象的第一个元素)。
② 创建所有元素。
③ 根据表格结构将每一个孩子节点拼接起来。
下面的一段源码是经过修改的Sample1.html。
在start函数的最后,有一行新代码,即使用另一个DOM方法(setAttribute)来设置表格的边界属性。setAttribute有两个参数:属性的名称和属性的值。你可以使用这个方法来设置任意元素的任意属性。
<head> <title>示例代码 - 使用JavaScript和DOM接口来处理HTML</title> <script> function start(){ // 获得body的引用 var mybody=document.getElementsByTagName("body").item(0); // 创建一个标签名称为TABLE的元素 mytable = document.createElement("TABLE"); // 创建一个标签名称为TBODY的元素 mytablebody = document.createElement("TBODY"); // 创建所有的单元格 for(j=0;j<2;j++){ // 创建一个标签名称为TR的元素 mycurrent_row=document.createElement("TR"); for(i=0;i<2;i++){ // 创建一个标签名称为TD的元素 mycurrent_cell=document.createElement("TD"); // 创建一个文本节点 currenttext=document.createTextNode("cell is row "+j+", column"+i); // 将文本节点添加到TD单元格内 mycurrent_cell.appendChild(currenttext); // 将TD单元格添加到TR行中 mycurrent_row.appendChild(mycurrent_cell); } // 将TR行添加到TBODY中 mytablebody.appendChild(mycurrent_row); } // 将TBODY添加到TABLE中 mytable.appendChild(mytablebody); // 将TABLE添加到BODY中 mybody.appendChild(mytable); // 设置边界属性为2 mytable.setAttribute("border","2"); } </script> </head> <body onload="start()"> </body> </html>
3.使用CSS和DOM来操作表格
(1)从表格中获得一个文本节点
示例中介绍了两个新的DOM属性。首先,使用childNodes属性来获得mycel的孩子节点列表。childNodes列表包括所有的孩子节点,无论它们的名称或类型是什么。像getElementsByTagName一样,它返回了一个节点列表。不同的是,getElementsByTagName只返回指定标签名称的元素。一旦获得了返回的列表,你可以使用item(x)方法来使用指定的元素。这个例子在表格的第2行第2个单元格中的myceltext中保存了一个文本节点。运行这个例子并观察结果,它创建了一个新的文本节点,这个文本节点的内容是myceltext的值,并且将这个文本节点作为了BODY元素的一个孩子。
如果对象是一个文本节点,你可以使用data属性来回收(retrieve)节点的文本内容。
mybody=document.getElementsByTagName("body").item(0); mytable=mybody.getElementsByTagName("table").item(0); mytablebody=mytable.getElementsByTagName("tbody").item(0); myrow=mytablebody.getElementsByTagName("tr").item(1); mycel=myrow.getElementsByTagName("td").item(1); // mycel的孩子节点列表的第一个元素 myceltext=mycel.childNodes.item(0); // currenttext的内容是myceltext的内容 currenttext=document.createTextNode(myceltext.data); mybody.appendChild(currenttext);
(2)获得一个属性的值
在Sample1.html的最后,我们在mytable对象上调用了setAttribute,这个调用是用来设置表格的边界属性的。然后使用了getAttribute方法来获得一个属性的值:
mytable.getAttribute("border");
(3)通过改变样式属性来隐藏一列
一旦在JavaScript变量中保存了一个对象,你就可以直接为它设置样式属性了。下面的代码是修改后的Sample1.html。在这里,第2列的每一个单元格都被隐藏了,而且第1列中的每一个单元格改为使用红色背景。注意,样式属性是被直接设置的。
<html> <body onload="start()"> </body> <script> function start(){ var mybody=document.getElementsByTagName("body").item(0); mytable = document.createElement("TABLE"); mytablebody = document.createElement("TBODY"); for(j=0;j<2;j++){ mycurrent_row=document.createElement("TR"); for(i=0;i<2;i++){ mycurrent_cell=document.createElement("TD"); currenttext=document.createTextNode("cell is:"+i+j); mycurrent_cell.appendChild(currenttext); mycurrent_row.appendChild(mycurrent_cell); // 当column为0时,设置单元格背景色;column为1时隐藏单元格 if(i==0){ mycurrent_cell.style.background="rgb(255,0,0)"; } else { mycurrent_cell.style.display="none"; } } mytablebody.appendChild(mycurrent_row); } mytable.appendChild(mytablebody); mybody.appendChild(mytable); } </script> </html>
在XAML中子元素不但可以通过DOM对象进行访问,还具有自己的属性。这种属性的写法不同于上面已经介绍过的内容,比如<textblock>对象的属性Text就可以有两种写法:
<TextBlock Text="Hello World"/>由于还支持内容属性,所以也可以写成<TextBlock> Hello World</TextBlock>,这两种写法完全等价。
在为元素属性添加子元素的时候同样要注意这样的问题,有些元素的属性支持直接添加子元素,这样用户可以直接把子元素写到里面。例如:
<LinearGradientBrush> <LinearGradientBrush.GradientStops> <GradientStopCollection> <GradientStop Offset="0" Color="Blue"/> <GradientStop Offset="1" Color="Red"/> </GradientStopCollection> </LinearGradientBrush.GradientStops> </LinearGradientBrush>
这里的GradientStopCollection是一个集合对象,这种写法实际上是首先建立一个集合对象,然后把元素插入对象中,再把集合对象作为属性指派给<LinearGradientBrush. GradientStops>。如果你增加的属性是只读属性,那么可以直接把元素加入到属性集合当中,这样,系统会自动为你指定一个空的集合对象,类似下面的写法:
<LinearGradientBrush> <LinearGradientBrush.GradientStops> <GradientStop Offset="0" Color="Blue"/> <GradientStop Offset="1" Color="Red"/> </LinearGradientBrush.GradientStops> </LinearGradientBrush>
对于这个属性,又由于LinearGradientBrush.GradientStops是内容属性,所以可以简写成<LinearGradientBrush>。
<GradientStop Offset="0" Color="Blue"/> <GradientStop Offset="1" Color="Red"/> </LinearGradientBrush>
基本上本书所有手写代码都遵循这个规范,但是希望大家在阅读的时候能够理解其包含的意义。这里需要提示一点的是,Canvas这个对象比较特殊,如下代码:
<Canvas> <Canvas.Children> <Ellipse Fill="Orange" Width="300" Height="100"> <Ellipse Fill="Blue" Width="100" Height="300"> </Canvas.Children> </Canvas>
在WPF中是完全可以使用的,但是在Silverlight中则会报错。