[.NET] XAML(2)--標記延伸

摘要:[.NET] XAML(2)--標記延伸

前言

在前一個章節[.NET] XAML(1)--物件生成,介紹了「XAML物件生成」這個簡單卻強大的物件生成模式。透過這個XAML物件生成配合物件導向的物件設計,可以讓開發人員依照XAML內容生成近乎無限組合的物件。但是光只有前一個章節介紹的內容,在實際的開發應用上,很快就會遇到不足的地方。


假設現在要使用Property-Element的設定,將TextBlock 的Text設定為.NET裡的Null,開發人員可能會寫出下面範例的XAML。



<TextBlock x:Name="ShowTextBlock" FontSize="72">
    <TextBlock.Text>
        Null
    </TextBlock.Text>
</TextBlock>

這段XAML乍看之下很合理,但是照前一章的運作邏輯去做分析。.NET剖析XAML Element來產生物件的時候,會將"Null"轉換為字串,而不會是開發人員預期的.NET裡面的Null。由這一個簡單的範例來理解,就可以發現Property-Attribute、Property-Element…等等,的確是有語意不足的地方。



Markup Extensions

為了補足Property-Attribute、Property-Element這些設定語意不足的地方,XAML另外提供了「標記延伸」(Markup Extensions)這個設定,來滿足開發人員的需求。Markup Extensions的運作邏輯,主要是圍繞在MarkupExtension這個Class身上。


當程式使用.NET剖析XAML Object-Element建立物件的時候,會特別去檢查建立出來的物件,是不是繼承自MarkupExtension的子物件型別。當發現建立的物件是繼承自MarkupExtension的子物件型別,.NET不會用這個物件當作Object-Element建立的結果,而是改呼叫這個物件繼承與覆寫的MarkupExtension的ProvideValue方法。並且使用ProvideValue方法的回傳值,當作Object-Element建立的結果物件。


我們可以建立範例,來驗證Markup Extensions的運作邏輯。首先建立一個繼承自MarkupExtension的子物件類別ClarkExtension,並且覆寫繼承自MarkupExtension的ProvideValue方法。在這個範例裡,ClarkExtension的ProvideValue方法只是單純的回傳一個"Clark"字串。



using System;
using System.Windows.Markup;
using System.Windows.Media;

namespace WpfApplication2
{
    public class ClarkExtension : MarkupExtension
    {
        public ClarkExtension()
        {

        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return "Clark";
        }
    }
}

接著建立XAML範例,來使用ClarkExtension這個Class。下面這段XAML,採用Property-Element來將ClarkExtension,設定為TextBlock物件的Text屬性。



<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sample="clr-namespace:WpfApplication2"
        Title="MainWindow" Height="400" Width="240">
    
    <TextBlock x:Name="ShowTextBlock" FontSize="72">
        <TextBlock.Text>
            <sample:ClarkExtension />
        </TextBlock.Text>
    </TextBlock>
    
</Window>

將這個範例編譯執行,並且在程式碼裡加入中斷點來檢視物件執行的順序。可以發現的確是執行了ClarkExtension的建構子之後,就呼叫ProvideValue、回傳Clark字串。並且在執行之後,將Clark字串設定為TextBlock 的Text內容,這樣最終程式呈現的執行結果就會如下圖。透過這個範例,開發人員可以簡單驗證與理解XAML的 Markup Extensions運作邏輯。



相關資料可以參考:
http://msdn.microsoft.com/zh-tw/library/ee855815.aspx
http://msdn.microsoft.com/zh-tw/library/system.windows.markup.markupextension.aspx


簡化語法

上一個章節XAML範例裡的Markup Extensions設定,看起來跟一般常見的XAML上的Markup Extensions有所出入,一整個複雜了許多,這對於開發人員來說並不是很友善的設計。為了降低複雜度及提高可讀性,XAML另外加入了簡化的語法設定,讓XAML的設計可以變得更簡潔。


首先XAML定義了MarkupExtension物件,可以省略撰寫Extension後置字元。以上一個章節的範例來說,可以將範例XAML裡的TextBlock簡化設定為:



<TextBlock x:Name="ShowTextBlock" FontSize="72">
    <TextBlock.Text>
        <sample:Clark />
    </TextBlock.Text>
</TextBlock>

另外使用Property-Attribute設定的時候,XAML也定義使用大括號 ({...}),來識別MarkupExtension物件。以上一個章節的範例來說,可以將範例XAML裡的TextBlock簡化設定為:



<TextBlock x:Name="ShowTextBlock" FontSize="72" Text="{sample:ClarkExtension}"/>

當然啦,這兩個簡化的規則是可以互相組合的。以上一個章節的範例來說,可以將範例XAML裡的TextBlock簡化設定為如下的結果。這樣的簡化結果,就是一般常見的XAML設定。



<TextBlock x:Name="ShowTextBlock" FontSize="72" Text="{sample:Clark}"/>

相關資料可以參考:
http://msdn.microsoft.com/zh-tw/library/ee855815.aspx#naming_the_support_type
http://msdn.microsoft.com/zh-tw/library/ms788723.aspx#markup_extensions


XAML 定義標記延伸

在XAML內對於一些常見的的使用情景,預先實做了幾個MarkupExtension子物件。這些預設的MarkupExtension子物件,通常放在System.Xaml組件的System.Windows.Markup命名空間內。在使用Visual Studio建立XAML相關頁面的時候,要引用這些MarkupExtension子物件,預設是以「x: 前置字元」來加以引用。幾個比較常見的XAML MarkupExtension如下:


x:Type (TypeExtension)
TypeExtension會剖析設定的參數資料,回傳對應的 Type物件。


x:Static(StaticExtension)
StaticExtension會剖析設定的參數資料,回傳對應的物件的靜態屬性。


x:Null(NullExtension)
NullExtension沒有需要設定的參數資料,就只是單純的回傳.NET的Null物件。


更多的資料可以參考:
http://msdn.microsoft.com/zh-tw/library/ms747254.aspx#XAML_Defined_Markup_Extensions


後記

XAML的Markup Extensions這個設定,是一些WPF、Silverlight、WP7功能(資料繫結、資源參考)的運作核心。先理解Markup Extensions這個運作核心的職責及工作內容,再去學習資料繫結、資源參考功能。這樣從運作核心本身開始學習的路線,會比較正確而且快速、並且不會被過多繁雜的變化所迷惑。


補充

XAML在WPF、Silverlight、WP7都可以使用,但不同目標平台所開放、支援的函式庫卻不一定相同。以本章說明的StaticExtension來說,這個MarkupExtension類別在WPF上有支援,但在WP7上卻是不支援的(撰寫本文時,開發環境安裝的是Windows Phone SDK 7.1)。開發人員在撰寫XAML的時候,先確認目標平台支援的函式庫相關內容,可以減少一些開發上不必要的困擾。

期許自己
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。