1. 程式人生 > >WPF XAML 從零開始認識XAML

WPF XAML 從零開始認識XAML

    剖析最簡單的XMAL程式碼:

<Window x:Class="MyFirstApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        
    </Grid>
</Window>

       花花綠綠一大片,還有兩個像主頁地址的東西....它們都是些什麼呢,讓我們一個個來分析。


    XAML是由XML派生出來的語言,所以有很多在XML中的概念在XAML中是通用的。比如使用標籤宣告一個元素(每一個元素對應記憶體中的一個物件)時,需要使用起始標籤<tab>和結束標籤</tag>,夾在起始標籤和終止標籤中的XAML程式碼表示隸屬於這個標籤的內容。如果沒有什麼內容隸屬於這個標籤,則這個標籤稱為空標籤,可以寫為<tag/>


    為了表示同類標籤中的某個標籤的與眾不同,可以給它的特徵(Attribute)賦值。為特徵賦值的語法如下:
    非空標籤:<tag Attribute1=value1 Attribute2=value2>content</tag>
    空標籤<tag Attribute1=value1 Attribute2=value2/>

    在這裡有必要把Attribute和Property仔細地辨別一下。

    這個詞的混淆由來已久,混淆的主要原因是大多數的中文譯文裡即把Attribute譯為“屬性”,也把Property譯為“屬性”,其實,這兩個詞表示的不是同一個層面上的東西。


    Property屬於面向物件的範疇。在使用面向物件程式設計的時候,常常需要對客觀事物進行抽象,在把抽象出來的結果封裝成類,類中用來表示事物狀態的成員就是Property。比如要編寫一個模擬賽車的遊戲,那麼必不可少的就是要對現實的賽車進行抽象,現實中汽車會帶很多資料,但是遊戲中可能只關心它的長度,寬度,高度,重量,速度等有限的幾個資料,同時,還會把汽車的加速,減速等行為提取出來用演算法進行模擬,這個過程就是屬於抽象(結果是Car這個類)。顯然,Car.Length,Car.Height,Car.Weight等表達的是當前汽車所處的一個狀態,而Car.Accelerate()、Car.Break()表達的是汽車能做什麼。因此,Car.Length,Car.Height就是Property的典型屬性,是針對物件而言的。將Property譯為“屬性”也很貼切,總之一句話:Property屬性是針對物件而言的。


    Attribute則是程式語言文法層面的東西。比如有兩個同類語法的元素A和B,為了表示A與B不完全相同或者A與B的用法上有些區別,這時候就要針對A和B加一些Attribute。也就是說Attribute只與語言層面上的東西有關,與抽象出來的物件沒有什麼關係。因為Attribute是為了表示“區分”的。所以它譯為“特徵”。C#中的Attribute就是這種應用的典型例子,我們可以為一個類新增Attribute,這個類裡面有很多Property(屬性)。顯然,Attribute只用來影響類中程式中的用法,而Property 則對應著抽象物件身上的性狀,根本不是一個層面上的東西。


    習慣上,英語中把標籤式語言中表示一個標籤特徵的“名----值”對稱做Attribute。如果恰好有在使用一種標籤式語言做面向物件程式設計,這兩個概念就有可能混在一起了。實際上,使用能夠使用面向物件程式設計的標籤式語言只是把標籤和物件做了一個對映-----針對標籤還是叫做Attribute,針對物件還是叫做Property,仍然不是一個層面上的東西。而且,標籤的Attribute不是和物件的Property一一對映的,往往一個標籤對於的Atrribute大於他所對映物件的Property。


    因為XAML是用來在UI上繪製控制元件的,而控制元件本身就是面向物件的抽象產物,所以XAML標籤中的Atrribute有一大部分和它對應他的物件的Property是一一對應的。當然,這還意味著標籤中的Atrribute並不對應它所代表物件的Property。


    明白了XAML的格式以及Atrribute和Property之間對應的關係,對上面的程式碼可謂是一目瞭然。它的總體結構是一個Window標籤內包含一個Grid標籤(或者說Grid標籤是Window標籤的內容),所代表的內容是一個Window物件裡面包含一個Grid物件。

    XAML是一種宣告式語言,當你看見一個標籤,就證明你聲明瞭一個物件,物件之間的關係要麼是並列,包含全都體現在標籤的關係上。
下面的程式碼就是<Window>標籤的Atrribute。

x:Class="MyFirstApplication.MainWindow
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml
Title="MainWindow" Height="350" Width="525"

    其中Title、Width、Height一看就知道和Window的Property物件對應的。中間兩行(即兩個xmlns)是在宣告名稱空間。最上面一行是在使用名為class的Attribute,這個Attribute來自於X:字首所對應的名稱空間。下面仔細解釋。


    前面已經說過,XMAL語言是從XML語言派生而來的。XML有一個功能就是可以在XML文件的標籤內使用xmlns特徵來定義名稱空間(NameSpace),XML也就是XML--NameSpace的縮寫。定義名稱空間的好處就是,當來源不同的類重名時,可以使用名稱空間加以區分。xmlns特徵的語法如下:

xmlns[:可選的對映字首]=“名稱空間”

     xmlns後可以跟一個可選的對映字首,之間用冒號隔開。如果沒有寫可選對映的字首,那就意味著所有來自這個名稱空間的所有標籤都不用加這個字首,這個沒有對映的名稱空間稱為“預設的名稱空間”,預設名稱空間只能有一個,而且應該選擇其中使用最頻繁的名稱空間來作為預設名稱空間。在上面的例子中,<Window>和<Grid>來自於第二行宣告的預設名稱空間,而第一樣的CLASS特徵則來來自於三行的中x:字首對應的名稱空間。這裡可以做一個有趣的小實驗:如果給第二行宣告的名稱空間加一個字首,例如n,那麼程式碼必須要改成一下形式編譯才能通過:
<n:Window x:Class="WpfApplication2.Window2"  
        xmlns:n="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
        Title="Window2" Height="300" Width="300">  
    <n:Grid>  
          
    </n:Grid>  
</n:Window>

     XAML中引用外來程式集和其中.NET名稱空間的語法和C#是不一樣的。在C#中,如果想使用System.Windows.Control名稱空間內的Button類,需要先把包含System.Windows.Control名稱空間的程式集PresentationFramework.dll新增引用到專案中,然後再用C#程式碼頂部寫上一句:using System.Windows.Control;。在XAML中做同樣的事情也需要新增對程式集的引用,然後再在根元素的起始標籤中寫上一句:xmlns:c="clr-namespace:System.Windows.Control;assembly=PresentationFramework"。c是對映的字首,換成其它字串也可以。因為button來自字首為c的名稱空間,所以在使用button的時候就要使用<c:Button>....</c:Button>。

    xmlns:c="clr-namespace:System.Windows.Control;assemble=PresentationFramework",這麼長的一串字串看上去的確有點恐怖,但不用擔心,VS中有自動提示功能。
在VS自動提示的頂部,你會看到幾個像網站地址的幾個名稱空間,其中就包含例子程式碼中的那兩行。為什麼名稱空間看上去像一個網站地址呢。其實把它copy到瀏覽器位址列嘗試跳轉也不會開啟網頁。這裡只是XAML直譯器的一個硬性編碼(hard-coding),只要見到這些固定的字串,就會把一系列的程式集和程式集中包含的名稱空間引入進來。

    預設引入的這兩個名稱空間格外的重要,它們對應的程式集和.net名稱空間如下:

    http://schemas.microsoft.com/winfx/2006/xaml/presentation對應:

System.Windows;
System.Windows.Automation;
System.Windows.Control;
System.Windows.Control.Primitives;
System.Windows.Data;
System.Windows.Document;
System.Windows.Forms.Intergration;
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.TextFormmatting;
System.Windows.Navigation;
System.Windows.Shapes;

    也就是說你可以在XAML中可以直接使用這些CLR名稱空間下的型別(因為預設XML名稱空間前沒有字首)。
    

    http://schemas.microsoft.com/winfx/2006/xaml則對應一些與XAML語法和編譯相關的CLR名稱空間, 使用這些名稱空間中的型別需要加上字首x,因為它們被對映到x的XML名稱空間中。


    從這兩個名稱空間的名字和它所對應的.NET程式集上,這個不難看出,第一個空間名稱對應的是繪製UI相關的程式集,是表示(Presentation)層面上的東西;第二個名稱空間則對應著XAML解析處理相關的程式集,是語言層面上的東西。

最後讓讓我回到最初的程式碼。你可能會問:在XAML裡面有x:Class="MyFirstApplication.MainWindow",在MainWindow.xaml.cs裡面也聲明瞭MainWindow這個類,難道他們不會衝突嗎?仔細看看MainWindow.xaml.cs中MainWindow的宣告就知道了----在宣告的時候使用的是partial關鍵字,這樣,這樣由XAML中解析成的類和C#檔案裡面定義的部分就合二為一了,正是由於這種partial機制,我們可以把邏輯程式碼留在.cs檔案裡,用C#語言來實現,而把那些宣告及佈局UI元素的程式碼分離出去,實現UI和邏輯分離,並且,用於繪製UI的程式碼(如宣告控制元件型別的欄位,設定它們的外觀和佈局等)也不必再使用C#語言,使用XAML和XAML編輯工具就可以輕鬆搞定。

參考《深入淺出WPF》