菜鸟学Windows Phone 8开发(2)——了解XAML
本系列文章来源MSDN的 面向完全新手的 Windows Phone 8 开发 主要是想通过翻译本系列文章来巩固下基础知识顺带学习下英语和练习下自己的毅力
这节课,我想讨论XAML语法(syntax),希望你有对前面写的代码有印象,通过看那些代码是很容易理解XAML基本功能的,但是本节我想指出我们不能第一眼就看出的特性和功能。
往高点看,我们今天这节课的计划如下:
1、我们今天将通过将XAML和C#比较来讨论XAML的本质和目标;
2、一些特别的功能和我们不能第一眼就看出来的隐藏功能;
我今天的目标是通过本堂课能让大家对XAML有一个足够的了解,甚至能在我还没解释的时候猜出他的意思。
一、什么是XAML
在前面的课中,我们对XAML做了个简单的讲解,他看起来和HTML有点相同,这绝不是偶然,因为XAML本质就是XML,叫扩展标记语言。我会花点时间解释他们的关系,但是更进一步说XML看起来像HTML以至于他们都继承自同一个祖先,HTML是专门用户构建web页面的文章,xml是更通用的,我的意思是你可以用于你设计的任何目的,并且还可以按你需要定义他的名称和属性,在过去开发者常用他来保存应用程序的设置信息或者用它来在两个不同的系统之间传递数据。为了使用xml你需要定义一个架构他包括特定属性和元素的名称,架构就像合约,xml的使用者和创建者都要遵守(abide by)这个合约为了更好的进行通信,一个架构对于xml是非常重要的,记住它我们等下会用到它。
咋看一下,XAML是有特殊用途的xml,通过看我们最近的例子中的XAML,它在手机界面定义了我们的用户界面。在这方面他看起来像HTML,但是他们有很大的不同,XAML实际上是用于创建类的实例和设置属性的值得,例如在前面的课程中我们用XAML定义了一个button控件。
上面的代码和以下在c#中的代码是等效的。
我已经在MainPage类的构造函数中添加了C#代码,我将花点时间讨论MainPage.xaml和MainPage.xaml.cs文件的关系,我们看到我们能写c#代码来定义行为,我仅仅是在MainPage类的构造函数里面写的代码。
这时候我们有两个button一个用xaml定义一个用c#代码定义。
但是我们只能看到一个button,因为我们在构造函数里面创建的button是呆在我们用XAML代码创建的button的位置,为了证明这个我们需要添加一个Margin属性,让他离左边210像素。
Margin属性的类型是Thickness,一个代表4个方向(dimentsion)的通用类。在这个例子中我们创建了一个新的Thickness类并传递第一个参数210.我们运行后看到:
现在我们能看到两个button
现在最大的疑问是我们创建了两个几乎相同的button,一个使用xaml一个使用c#。
xaml的像这样:
<Button></Button>
c#我们创建一个button实例。
我们能在button元素上面设置属性也可以在button实例上设置属性。
重要的是XAML是一种更简单和简洁的语法来创建类的实例和设置类的属性。我们需要花10行c#代码而xaml只需要一行,
更大的好处是,使用XAML能及时在设计器中看到我的改变,使用c#代码创建的东西我需要运行这个程序才能看到我的微调(tweak)。
2、类型转换器介绍
如果你有一双敏锐的眼睛,你可能发xaml和c#代码在设置HorizontalAlignment属性的时候是不一样的,如果你尝试设置:
myButton.HorizontalAlignment="left";
你将会产生一个编译错误,xaml解析器将扮演一个转换器将string类型的left转换成System.Windows.HorizontalAliginment.Left的枚举类型,转换器是一个将string类型转换成一种强类型的来,在windows 8的api中有很多这样的类,我们在整个系列中都会用到他们。HorizontalAlignment属性是微软开发者用一个特殊的属性标记在源代码中,他是一种信号会通知xaml解析器尝试将string转换成枚举的值,为了有趣,我们可以看一下如果我们有拼写错误的情况:
你将看到一个编译错误,因为xmal解析器不能将这个string类型的匹配到System.Windows.HorizontalAlignment.Left.枚举值。
因此,XAML的第一个特性是使用简洁(succinct)的代码创建类的实例,在windows8应用程序中它通常用于构建用户界面元素,但是XAML不仅仅是用于创建界面元素,我们还可以使用其他技术让他用于其他目的。
3、理解XAML命名空间定义
接下来,我们将讨论在ManiPage.xaml文件顶部的代码。
看到这里的时候你肯定会想起我前面说的xaml架构(schema),它是xaml的一部分,对于这个列子它们是在哪里遵守了这个架构呢。
从第3行到第8行,有6个架构,每一个都是用xmlns属性定义的,第3行的第一个架构是默认的命名空间,换句话说它是没有冒号(colon)在冒号后面也没有单词,像第4行到第8行那样。
接下来从第4行到第8行的命名空间将用名字和冒号组合,这很清楚,:x和:phone是命名空间,和架构(之前说的合约)关联,在MainPage.xaml中的其余代码中的元素或属性必须遵守至少一个架构。换句话说如果我们定义了不在上述命名空间中的属性或元素,我们就不能确保我们的编译器能正常的解析我们的代码将程序运行起来。
例如下面的例子:
<Grid x:Name="LayoutRoot" Background="Transparent">
我们将希望grid元素和属性是默认命名空间(第3行)中的架构。然而x:是第4行中的机构。
我有一个想法,我们将地址复制到浏览器打开,我们了解更多的标记语言。
http://schemas.microsoft.com/winfx/2006/xaml/presentation
什么?我们不能打开它,那是因为这个架构地址是没有发表的。因此我们不能通过url访问它。相反架构是一个简单的唯一名字。像我们在c#中的命名空间用于区分在不同命名空间下面可能相同的类名,架构用于区分类名,他就像一个姓或者别名。这个用于命名空间区分的url我们应该认为他是一个uri(通用资源标识符)而不是一个定位符,xml命名空间将用于不同的应用程序,windows 运行时xaml解析器或将他变为可执行的代码,然而visual studio和blend或将他们变为一种设置展现。
因此这第二个定义的命名空间定义的是一个匹配,x:是属于 http://schemas.microsoft.com/winfx/2006/xaml架构,因此任何一个以x:前缀开始的属性或元素都是遵守这个架构的。
但是有什么不同呢?他是很微妙的,第二个架构定义了XAML的内在规则,第一个架构定义了Windows 8特定用途的规则或合约,换句话说我们能直接使用Grid、Button、MediaElement等空间元素而不是使用一个前缀那是因为这些都定义在默认的命名空间中。
你可以看到第5行和6行定义的命名空间和架构Phone和Shell是使用了和其他不同的URI(通用资源标识符),其中一个是使用了我们安装Windows Phone API的时候安装在我们电脑上的Microsoft.Phone CLR程序命名空间程序集。在最上面的一行能看到:
<phone:PhoneApplicationPage
表明PhoneApplicationPage是他自身定义的一部分。它继承自Windows.System.Controls.Page类这正是WPF页面类和Windows Store应用程序页面类的父类,有很多基本的功能在三种程序中共享,所以有利的一面是你可以通过学习本系列课程你也可以创建WPF和Windows Stor程序。虽然他们有差异但是他们有很多相同的地方。
第7行和第8行定义的命名空间和架构是用来允许Visual Studio在左边正确显示预览窗口的。这些功能是在运行的时候被忽略的。第9行就提示编译器忽略任何带d:前缀的xaml代码。
我们还有很多问题没有解决,我们能花很多时间来谈论它的特性,但是我们主要的目的是要知道文件的上面的每一行代码添加进来都是有用的。他们定义了我们下面代码必须遵守的规则,你不需要修改他们,如果你修改了他们那么可能会终止你的程序,我希望你不要去玩弄这些代码除非你有更好的理由,第10到14行的代码是一些其他的属性,我们接下来会讨论。
4、理解.xaml和.xaml.cs文件之间的关系
在vs的解决方案管理窗口中,你会发现XAML文件展开都能看到一个同名的c#文件,唯一不同的是他以.cs结尾,如果你点开这个文件,你会发现他里面定义了一个MainPage类,并且使用了Partial关键字。
另外的一半是定义在MainPage.xaml文件的第1和2行:
<phone:PhoneApplicationPage
x:Class="SoundBoard.MainPage"
x:Class="SoundBoard.MainPage"
但是他没有像cs里面的代码一样使用partial来表明他们之间的关系。
为什么这很重要,因为他们是相关的所以编译器会将他们编译成单个的类。意思是他们是一个整体的两个部分,这是一个很重要的概念,xaml代码也能像C#代码一样编译成中间语言然后一起组成一个类。意思是说我们能创建一个类的实例在一个文件中然后访问它在另外一个文件中,也即我们在xaml创建一个叫audioPlayer的MediaElement实例我们能在c#代码中访问并设置他的属性。我们将看到更多的例子在其他的课程中。
5、理解默认属性
因为XAML是XML的一个扩展,因此我们能在元素里面嵌入其他元素,像下面的例子:
<PhoneApplicationPage>
<Grid ...>
<Grid ... >
<MediaElement ... />
<Button ... />
</Grid>
</Grid>
</PhoneApplicationPage>
<Grid ...>
<Grid ... >
<MediaElement ... />
<Button ... />
</Grid>
</Grid>
</PhoneApplicationPage>
这里PhoneApplicationPage是包含一个Grid,Grid保航一个MediaElement和一个Button或者更多的遵循正确XAML语法的元素,用户空间的内容属性使用在grid中,使他的孩子包括MediaElement和Button,对于我们使用的控件类型,默认属性也可以使用嵌入语法样式。你能这样做:
<Button Content="Hello World" ... />
... or this ...
<Button ... >
Hello World
</Button>
因为Content是Button的默认属性。
6、理解复杂属性和属性元素语法
在一些例子中我们只需要设置属性的值,而隐藏了在幕后的复杂的创建过程,例如我们设置Background=“Red”就是一个很好的例子,我们在c#中需要这样设置:
myButton.Background = newSolidColorBrush(Colors.Red);
我们必须创建一个SolidColorBrush实例并给他传递一个Colors的枚举值,这是一个很好的数据转换例子,但是一些属性是太复杂我们不能用属性表是他。
当一个属性不是很容易使用一个XAML属性表示的时候,我们称它为复杂属性,为了证明这个,我们先移除Background="Red",使用Content="hellword“样式设置默认属性,
接下来我们在属性窗口中设置一个线性渐变的画刷给Background属性:
1、选择Brush属性展开Brush编辑器,
2、选择Background属性
3、选择线性画刷按钮
4、一直移动筛选器到颜色编辑器的右上角。
我们应该能在手机界面预览窗口看到下面的界面:
重要的是我们来看下画刷编辑器生成的代码。
现在我们不能像以前只设置一个string值就可以设置属性的值并且你会发现Background属性里面还有自己的元素:
<Button ... >
<Button.Background>
...
</Button.Background>
</Button>
<Button.Background>
...
</Button.Background>
</Button>
这叫作属性元素语法来自于<Control.Property>
LinearGradientBrush就是一个很好的例子,我们使用一个对象来表示一个color或多个color,想象下他是一个线性的画刷来创建一个线性渐变使眼色从上到下渐变。确切的说你肯定不想写这些代码。因为他是违背WP8应用程序美感的。但是现在让我们假设我们在使用一个渐变颜色作为Background属性的值来表达我们的个性(individuality)。
正如你下面看到的,如果我们想定义一个LinearGradientBrush,我们必须提供很多信息来渲染,在某个点怎么进入下一个点的颜色改变等,LinearGradientBrush是一个由许多定义了颜色和位置的GradientStop对象的集合。
用于呈现LinearGradientBrush的代码visual Studio会自动让他缩短的,本来我们应该这样写:
<link href="http://static.ch9.ms/styles/noscript.css?v=xpdUwVK5NYRU81b7JbIL1HL85w01" rel="stylesheet"/>
<Button.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="Red" Offset="1" />
<GradientStop Color="Black" Offset="0" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Button.Background>
你会发现<LinearGradientBrush.GradientStops>和<GradientStopCollection>是被忽略(omit)的,这样简单紧凑的代码是由一个智能的XAML解析器完成的,首先因为GradientStops是LinearGradientBrush的默认属性,GradientStops是GradientStopCollection类型并且实现了IList<T>接口,这里的T就是GradientStop类型。因为这个XAML解析器能推断(deduce)出嵌套在<LinearGradientBrush>中的代码就是一个或多个GradientBrush实例,他们会在幕后被添加到GradientStopCollection中。这个故事的寓意是我们能创建一个类的实例,并且我们有一个清晰的控件设计器来设计我们的界面元素。尽管如此,XAML解析器是智能的,他不需要我们添加多余的代码,只需要我们有足够的信息能正确表达我们的意图就可以了。
回顾
我们学习了XAML的语法,发现大多数的XAML是很简单的,但是还是有很多我们不能第一眼就发现的东西:
1、XAML不仅仅是XML的一个扩展,他必须严格依赖架构和命名空间来遵守合约,以便我们能在不同的应用程序之间创建,解释。展示或者编译这些XAML代码。
2、XAML能允许我们用更简单紧凑的代码来申明累的实例并设置属性,这里我们是通过使用XAML和c#代码创建一个Button来得出结论的。
3、因为XAML是需要更少的代码,这就导致我们需要它隐藏的功能转换器来完成一些将string转换成类实例的功能。
4、对于一些复杂的XAML代码他是允许我们使用属性语法来完成,属性语法是通过一个依赖默认属性和推断的智能xaml感知器来减少我们大量代码的。
5、我们学习了嵌入语法风格和嵌入元素和可视化元素之间的关系,例如PhoneAppliactionPage中包含一个用于布局的Grid空间,但是相反他包含很多其他类型的控件,
6、我们学习了默认属性,每一个控件都有一个默认属性并且我们能使用嵌入语法来设置他的值。
接下来我们将学习更多的关于Grid布局,我们将学习XAML的附加属性和事件是怎么触发的。