前言 XAML是微软推出的一种宣告式标记语言采用XML的格式让开发人员设计应用程序编程接口在微软近期推出的各种开发平台例如WPFSilverlightWP甚至Win的Metro style app开发上都可以看到XAML的身影XAML可以这么的神奇的跨平台运作是因为XAML不涉足执行平台的运作机制…等等只单纯的依照开发人员的设计建立对应的对象让执行平台使用例如 XAML范例 <phone:PhoneApplicationPage x:Class=XamlSampleMainPage xmlns= xmlns:x= xmlns:phone=clrnamespace:MicrosoftPhoneControls;assembly=MicrosoftPhone xmlns:d= xmlns:mc=compatibility/ mc:Ignorable=d d:DesignWidth= d:DesignHeight=> <TextBlock x:Name=ShowTextBlock Text=Hello World FontSize=/> </phone:PhoneApplicationPage> namespace XamlSample { public partial class MainPage : PhoneApplicationPage { public MainPage() { // Base InitializeComponent() } } } Code范例 <phone:PhoneApplicationPage x:Class=XamlSampleMainPage xmlns= xmlns:x= xmlns:phone=clrnamespace:MicrosoftPhoneControls;assembly=MicrosoftPhone xmlns:d= xmlns:mc=compatibility/ mc:Ignorable=d d:DesignWidth= d:DesignHeight=> </phone:PhoneApplicationPage> namespace XamlSample { public partial class MainPage : PhoneApplicationPage { public MainPage() { // Base InitializeComponent() // Create TextBlock showTextBlock = new TextBlock() showTextBlockName = ShowTextBlock; showTextBlockText = Hello World; showTextBlockFontSize = ; thisContent = showTextBlock; } } } 执行结果 这两个WP的范例程序执行结果都是在画面上显示Hello World而我们在程序代码里加入断点来检视执行结果的对象(如下图)可以看出两个范例最终产生的对象结构是相同的也就是说不管是使用XAML或是使用程序代码的方式来建构画面对象都是相同的NET会依照开发人员设计的XAML内容建立对象就像是开发人员使用程序代码建立对象一样理解这个范例之后可以简单的说XAML是用来产生对象的配置文件执行平台使用XAML产生的对象而NET依照设定来产生对象是采用Reflection我们也可以更广义的说「XAML是用来产生对象的Reflection配置文件」 XAML范例中断 Code范例中断 本篇文章采用「XAML是用来产生对象的Reflection配置文件」这样的角度剖析XAML来辅助开发人员理解XAML并且知道是如何透过XAML来产生对象 ObjectElement 下面这段XAML代表一个TextBlock对象当程序执行的时候NET会剖析XAML Element来产生一个TextBlock对象像这样会产生一个对象的XAML Element称为「ObjectElement」 <sample:TextBlock x:Name=ShowTextBlock Text=Hello World FontSize= xmlns:sample=clrnamespace:SystemWindowsControls;assembly=SystemWindows /> 有用过Reflection的开发人员会知道组件名称命名空间类别名称有这三项字符串数据就可以反射生成一个对象出来在ObjectElement里这三项数据也有各自设定的规范依照XAML的规范来解读上面这个Object Element可以得到「组件名称」是SystemWindows「命名空间」是SystemWindowsControls「类别名称」则是TextBlockNET剖析Object Element之后就会依照这些字符串数据反射生成出一个TextBlock对象将这个XAML Element取代Hello World范例里的TextBlock依然可以正常的显示Hello World <phone:PhoneApplicationPage x:Class=XamlSampleMainPage xmlns= xmlns:x= xmlns:phone=clrnamespace:MicrosoftPhoneControls;assembly=MicrosoftPhone xmlns:d= xmlns:mc=compatibility/ mc:Ignorable=d d:DesignWidth= d:DesignHeight=> <sample:TextBlock x:Name=ShowTextBlock Text=Hello World FontSize= xmlns:sample=clrnamespace:SystemWindowsControls;assembly=SystemWindows /> </phone:PhoneApplicationPage> 当然这样的XAML内容看起来跟一般常见的XAML有所出入一整个复杂了许多因为XAML是由XML发展出来的很多的格式沿用XML的规范许多命名空间可以由上层的Element所提供这样的规范大量减少XAML需要设定的数据内容这部分有兴趣的开发人员可以参考XML的相关技术资料另外各种开发平台也定义了一些默认的关键词让XAML的设计可以变得更简洁这部分有兴趣的开发人员可以参考开发平台的相关技术资料以上面这个范例来说因为是要产生开发平台预设的TextBlock而这个开发平台预设的命名空间已经在PhoneApplicationPage做过宣告所以可以将组件名称命名空间都省略掉XAML经过这些规范的简化之后就可以产生出一般常见的XAML数据内容 PropertyAttribute 下面这段XAML代表一个TextBlock对象 TextBlock对象有一个Text属性当程序执行的时候NET会剖析XAML Element来产生一个TextBlock对象并且将这个TextBlock对象的Text属性设定为Hello WorldFontSize属性设定为像这样会设定一个对象属性的设定称为「PropertyAttribute」 <TextBlock x:Name=ShowTextBlock Text=Hello World FontSize=/> 查询MSDN可以发现TextBlock的FontSize属性是一个型别为SystemDouble的属性而XAML因为是XML的格式所以被限制了只能输入字符串形式的数据受于这样的限制NET剖析PropertyAttribute的时候会尝试将字符串数据转型为对象属性的型别以下面这个范例来说在执行的阶段会看到NET的错误通知告知无法将字符串数据转型为SystemDouble <TextBlock x:Name=ShowTextBlock Text=Hello World FontSize=Clark/> PropertyElement 在XAML的规范里PropertyAttribute章节里的TextBlock范例也可以写成下面范例的格式将TextBlock的FontSize属性改写成为一对独立的标签并且设定值写在标签的内容里像这样设定一个对象属性的设定称为「PropertyElement」 <TextBlock x:Name=ShowTextBlock Text=Hello World > <TextBlockFontSize> </TextBlockFontSize> </TextBlock> PropertyElement的写法看起来有点多余但其实这是为了XAML的延展性而设计一般对象的属性有些不单纯是intdouble这些实值型别也有可能是一个对象(Class)一个结构(Struct)而一个对象又会有属性整个对象就是以树状结构生长下去这时PropertyAttribute使用字符串来设定这个对象树状结构会显得力不从心PropertyElement定义了可以使用ObjectElement来当作内容来解决这个问题NET在剖析PropertyElement的时候会将ObjectElement内容反射生成出对应的对象设定为对象的对象属性而使用ObjectElement来当作PropertyElement的内容另一个原因是ObjectElement自己是描述一个对象它又可以拥有自己的PropertyAttributePropertyElement这样就可以一层一层设计出对象的树状结构 下面这段XAML采用PropertyElement来设定TextBlock对象的Foreground属性而TextBlock对象的Foreground属性是一个型别为Brush的对象属性所以在PropertyElement里采用ObjectElement来生成要设定给Foreground属性的一个Brush对象 <TextBlock x:Name=ShowTextBlock Text=Hello World FontSize=> <TextBlockForeground> <SolidColorBrush Color=#FF /> </TextBlockForeground> </TextBlock> 仔细看上面的范例会发现并不是生成一个Brush对象而是生成Brush的延伸对象SolidColorBrush这是因为Brush对象是一个抽象类并没有办法直接生成所以只能生成延伸自Brush的SolidColorBrush来当作Foreground属性的对象这也就是说我们可以生成延伸类别来设定对象属性这是一个面向对象开发很重要的功能提供了开发人员抽换对象的能力大幅增加系统对象的弹性 PropertyElementCollection 既然一般对象的属性不单纯是intdouble这些实值型别有可能是一个对象(Class)一个结构(Struct)就免不了的对象的属性也有可能是对象结构的集合(Collection)PropertyElement另外也定义了可以使用多个ObjectElement来当作内容来解决这个问题NET在剖析PropertyElement的时候会将多个ObjectElement内容反射生成出对应的对象并且加入对象的对象集合属性 下面这段XAML采用PropertyElement来设定LinearGradientBrush对象的GradientStops属性而LinearGradientBrush对象的GradientStops属性是一个GradientStop型别的对象集合属性所以在PropertyElement里采用多个ObjectElement来生成要设定给GradientStops属性的多个GradientStop对象 <TextBlock x:Name=ShowTextBlock Text=Hello World FontSize=> <TextBlockForeground> <LinearGradientBrush StartPoint= EndPoint= > <LinearGradientBrushGradientStops> <GradientStop Color=#FF Offset=/> <GradientStop Color=#FF Offset=/> <GradientStop Color=#FF Offset=/> </LinearGradientBrushGradientStops> </LinearGradientBrush> </TextBlockForeground> </TextBlock> 后记 了解XAML的对象生成在学习WPFSilverlightWP等等开发平台的时候就可以参考MSDN类别库资料来查询透过XAML生成的对象了解对象本身的职责及工作内容以及设定每个属性会让对象发生何种变化这样从对象本身开始学习的路线会比较正确而且快速并且不会被过多繁杂的变化所迷惑 |