摘要:[.NET] XAML(1)--物件生成
前言
XAML是微軟推出的一種宣告式標記語言,採用XML的格式讓開發人員設計應用程式介面。在微軟近期推出的各種開發平台,例如WPF、Silverlight、WP7、甚至Win8的Metro style app開發上都可以看到XAML的身影。XAML可以這麼的神奇的跨平台運作,是因為XAML不涉足執行平台的運作、機制...等等,只單純的依照開發人員的設計,建立對應的物件讓執行平台使用。例如:
XAML範例
<phone:PhoneApplicationPage
x:Class="XamlSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800">
<TextBlock x:Name="ShowTextBlock" Text="Hello World" FontSize="72"/>
</phone:PhoneApplicationPage>
namespace XamlSample
{
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
// Base
InitializeComponent();
}
}
}
Code範例
<phone:PhoneApplicationPage
x:Class="XamlSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800">
</phone:PhoneApplicationPage>
namespace XamlSample
{
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
// Base
InitializeComponent();
// Create
TextBlock showTextBlock = new TextBlock();
showTextBlock.Name = "ShowTextBlock";
showTextBlock.Text = "Hello World";
showTextBlock.FontSize = 72;
this.Content = showTextBlock;
}
}
}
執行結果
這兩個WP7的範例程式,執行結果都是在畫面上顯示Hello World。而我們在程式碼裡加入中斷點,來檢視執行結果的物件(如下圖),可以看出兩個範例最終產生的物件結構是相同的。也就是說,不管是使用XAML或是使用程式碼的方式來建構畫面物件都是相同的。.NET會依照開發人員設計的XAML內容建立物件,就像是開發人員使用程式碼建立物件一樣。理解這個範例之後,可以簡單的說,XAML是用來產生物件的設定檔、執行平台使用XAML產生的物件。而.NET依照設定來產生物件是採用Reflection,我們也可以更廣義的說「XAML是用來產生物件的Reflection設定檔」。
XAML範例中斷
Code範例中斷
本篇文章採用「XAML是用來產生物件的Reflection設定檔」,這樣的角度剖析XAML。來輔助開發人員理解XAML,並且知道是如何透過XAML來產生物件。
Object-Element
下面這段XAML,代表一個TextBlock物件。當程式執行的時候,.NET會剖析XAML Element來產生一個TextBlock物件。像這樣會產生一個物件的XAML Element稱為「Object-Element」。
<sample:TextBlock x:Name="ShowTextBlock" Text="Hello World" FontSize="72" xmlns:sample="clr-namespace:System.Windows.Controls;assembly=System.Windows" />
有用過Reflection的開發人員,會知道組件名稱、命名空間、類別名稱,有這三項字串資料就可以反射生成一個物件出來。在Object-Element裡,這三項資料也有各自設定的規範。依照XAML的規範來解讀上面這個Object Element,可以得到:「組件名稱」是System.Windows、「命名空間」是System.Windows.Controls、「類別名稱」則是TextBlock。.NET剖析Object Element之後,就會依照這些字串資料,反射生成出一個TextBlock物件。將這個XAML Element取代Hello World範例裡的TextBlock依然可以正常的顯示Hello World。
<phone:PhoneApplicationPage
x:Class="XamlSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800">
<sample:TextBlock x:Name="ShowTextBlock" Text="Hello World" FontSize="72" xmlns:sample="clr-namespace:System.Windows.Controls;assembly=System.Windows" />
</phone:PhoneApplicationPage>
當然這樣的XAML內容,看起來跟一般常見的XAML有所出入,一整個複雜了許多。因為XAML是由XML發展出來的,很多的格式沿用XML的規範。許多命名空間可以由上層的Element所提供,這樣的規範大量減少XAML需要設定的資料內容。這部分有興趣的開發人員可以參考XML的相關技術資料。另外各種開發平台也定義了一些預設的關鍵字,讓XAML的設計可以變得更簡潔,這部分有興趣的開發人員可以參考開發平台的相關技術資料。以上面這個範例來說,因為是要產生開發平台預設的TextBlock,而這個開發平台預設的命名空間已經在PhoneApplicationPage做過宣告,所以可以將組件名稱、命名空間都省略掉。XAML經過這些規範的簡化之後,就可以產生出一般常見的XAML資料內容。
Property-Attribute
下面這段XAML,代表一個TextBlock物件, TextBlock物件有一個Text屬性。當程式執行的時候,.NET會剖析XAML Element來產生一個TextBlock物件,並且將這個TextBlock物件的Text屬性設定為Hello World、FontSize屬性設定為72。像這樣會設定一個物件屬性的設定,稱為「Property-Attribute」。
<TextBlock x:Name="ShowTextBlock" Text="Hello World" FontSize="72"/>
查詢MSDN可以發現TextBlock的FontSize屬性,是一個型別為System.Double的屬性。而XAML因為是XML的格式,所以被限制了只能輸入字串形式的資料。受於這樣的限制,.NET剖析Property-Attribute的時候,會嘗試將字串資料轉型為物件屬性的型別。以下面這個範例來說,在執行的階段會看到.NET的錯誤通知,告知無法將字串資料轉型為System.Double。
<TextBlock x:Name="ShowTextBlock" Text="Hello World" FontSize="72Clark"/>
Property-Element
在XAML的規範裡,Property-Attribute章節裡的TextBlock範例,也可以寫成下面範例的格式。將TextBlock的FontSize屬性改寫成為一對獨立的標籤,並且設定值寫在標籤的內容裡。像這樣設定一個物件屬性的設定,稱為「Property-Element」。
<TextBlock x:Name="ShowTextBlock" Text="Hello World" >
<TextBlock.FontSize>
72
</TextBlock.FontSize>
</TextBlock>
Property-Element的寫法看起來有點多餘,但其實這是為了XAML的延展性而設計。一般物件的屬性有些不單純是int、double這些實值型別,也有可能是一個物件(Class)、一個結構(Struct)。而一個物件又會有屬性,整個物件就是以樹狀結構生長下去。這時Property-Attribute使用字串來設定這個物件樹狀結構會顯得力不從心。Property-Element定義了,可以使用Object-Element來當作內容來解決這個問題。.NET在剖析Property-Element的時候,會將Object-Element內容反射生成出對應的物件,設定為物件的物件屬性。而使用Object-Element來當作Property-Element的內容另一個原因是,Object-Element自己是描述一個物件,它又可以擁有自己的Property-Attribute、Property-Element,這樣就可以一層一層設計出物件的樹狀結構。
下面這段XAML,採用Property-Element來設定TextBlock物件的Foreground屬性。而TextBlock物件的Foreground屬性,是一個型別為Brush的物件屬性。所以在Property-Element裡採用Object-Element來生成要設定給Foreground屬性的一個Brush物件。
<TextBlock x:Name="ShowTextBlock" Text="Hello World" FontSize="72">
<TextBlock.Foreground>
<SolidColorBrush Color="#FF0000" />
</TextBlock.Foreground>
</TextBlock>
仔細看上面的範例,會發現並不是生成一個Brush物件,而是生成Brush的延伸物件SolidColorBrush。這是因為Brush物件是一個抽象類別,並沒有辦法直接生成。所以只能生成延伸自Brush的SolidColorBrush來當作Foreground屬性的物件。這也就是說,我們可以生成延伸類別來設定物件屬性。這是一個物件導向開發很重要的功能,提供了開發人員抽換物件的能力,大幅增加系統物件的彈性。
Property-Element-Collection
既然一般物件的屬性不單純是int、double這些實值型別,有可能是一個物件(Class)、一個結構(Struct)。就免不了的物件的屬性,也有可能是物件、結構的集合(Collection)。Property-Element另外也定義了,可以使用多個Object-Element來當作內容來解決這個問題。.NET在剖析Property-Element的時候,會將多個Object-Element內容反射生成出對應的物件,並且加入物件的物件集合屬性。
下面這段XAML,採用Property-Element來設定LinearGradientBrush物件的GradientStops屬性。而LinearGradientBrush物件的GradientStops屬性,是一個GradientStop型別的物件集合屬性。所以在Property-Element裡採用多個Object-Element,來生成要設定給GradientStops屬性的多個GradientStop物件。
<TextBlock x:Name="ShowTextBlock" Text="Hello World" FontSize="72">
<TextBlock.Foreground>
<LinearGradientBrush StartPoint="0,1" EndPoint="1,0" >
<LinearGradientBrush.GradientStops>
<GradientStop Color="#FF0000" Offset="0.0"/>
<GradientStop Color="#00FF00" Offset="0.5"/>
<GradientStop Color="#0000FF" Offset="1.0"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</TextBlock.Foreground>
</TextBlock>
後記
了解XAML的物件生成,在學習WPF、Silverlight、WP7等等開發平台的時候,就可以參考MSDN類別庫資料來查詢透過XAML生成的物件。了解物件本身的職責及工作內容,以及設定每個屬性會讓物件發生何種變化。這樣從物件本身開始學習的路線,會比較正確而且快速、並且不會被過多繁雜的變化所迷惑。
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。