本系列文章來源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的附加屬性和事件是怎么觸發的。