共用方式為


第 3 部分: XAML 標記延伸

XAML 標記延伸在 XAML 中構成重要功能,可讓屬性設定為從其他來源間接參考的物件或值。 XAML 標記延伸對於共用對象和參考整個應用程式所使用的常數特別重要,但它們會在數據系結中找到其最大的公用程式。

XAML 標記延伸

一般而言,您可以使用 XAML 將物件的屬性設定為明確的值,例如字串、數位、列舉成員,或轉換成幕後值的字串。

不過,有時候,屬性必須改為參考在其他位置定義的值,或者可能需要在運行時間透過程式代碼處理一點。 針對這些用途,可以使用 XAML 標記延伸

這些 XAML 標記延伸不是 XML 的延伸。 XAML 是完全合法的 XML。 它們稱為「延伸模組」,因為它們是由實作 的 IMarkupExtension類別中的程式代碼所支援。 您可以撰寫自己的自訂標記延伸。

在許多情況下,XAML 標記延伸會在 XAML 檔案中立即辨識,因為它們會顯示為以大括弧分隔的屬性設定:{ 和 },但有時標記延伸會出現在標記中做為傳統元素。

共用資源

某些 XAML 頁面包含數個檢視,其中屬性設定為相同的值。 例如,這些 Button 物件的許多屬性設定都相同:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <StackLayout>
        <Button Text="Do this!"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                BorderWidth="3"
                Rotation="-15"
                TextColor="Red"
                FontSize="24" />

        <Button Text="Do that!"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                BorderWidth="3"
                Rotation="-15"
                TextColor="Red"
                FontSize="24" />

        <Button Text="Do the other thing!"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                BorderWidth="3"
                Rotation="-15"
                TextColor="Red"
                FontSize="24" />

    </StackLayout>
</ContentPage>

如果需要變更其中一個屬性,您可能偏好只進行一次變更,而不是三次變更。 如果這是程式代碼,您可能會使用常數和靜態只讀對象,協助保持這類值一致且易於修改。

在 XAML 中,一個熱門的解決方案是將這類值或物件儲存在資源字典。 類別 VisualElement 會定義名為 Resources 類型的 ResourceDictionary屬性,這是具有 型 string 別索引鍵和 型 object別值的字典。 您可以將物件放入此字典中,然後從標記參考它們,全部都在 XAML 中。

若要在頁面上使用資源字典,請包含一組 Resources 屬性項目標記。 將這些專案放在頁面頂端最方便:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <ContentPage.Resources>

    </ContentPage.Resources>
    ...
</ContentPage>

也必須明確包含 ResourceDictionary 標籤:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <ContentPage.Resources>
        <ResourceDictionary>

        </ResourceDictionary>
    </ContentPage.Resources>
    ...
</ContentPage>

現在,各種類型的物件和值可以新增至資源字典。 這些類型必須具現化。 例如,它們不能是抽象類。 這些類型也必須有公用無參數建構函式。 每個專案都需要以 x:Key 屬性指定的字典索引鍵。 例如:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <ContentPage.Resources>
        <ResourceDictionary>
            <LayoutOptions x:Key="horzOptions"
                           Alignment="Center" />

            <LayoutOptions x:Key="vertOptions"
                           Alignment="Center"
                           Expands="True" />
        </ResourceDictionary>
    </ContentPage.Resources>
    ...
</ContentPage>

這兩個專案是結構類型的 LayoutOptions值,而且每個專案都有唯一索引鍵和一或兩個屬性集。 在程式代碼和標記中,使用的 LayoutOptions靜態字段比較常見,但在這裡,設定屬性比較方便。

現在,必須將這些按鈕的 和 VerticalOptions 屬性設定HorizontalOptions為這些資源,而且使用 XAML 標記延伸完成StaticResource

<Button Text="Do this!"
        HorizontalOptions="{StaticResource horzOptions}"
        VerticalOptions="{StaticResource vertOptions}"
        BorderWidth="3"
        Rotation="-15"
        TextColor="Red"
        FontSize="24" />

標記 StaticResource 延伸一律以大括號分隔,並包含字典索引鍵。

名稱 StaticResource 會將它 DynamicResource與 區隔開,這也 Xamarin.Forms 支援。 DynamicResource 是針對與運行時間期間可能會變更之值的字典索引鍵,而 StaticResource 只會在建構頁面上的專案時,從字典存取元素一次。

BorderWidth針對屬性,必須將雙精度浮點數儲存在字典中。 XAML 可方便定義常見資料類型的標籤,例如 x:Doublex:Int32

<ContentPage.Resources>
    <ResourceDictionary>
        <LayoutOptions x:Key="horzOptions"
                       Alignment="Center" />

        <LayoutOptions x:Key="vertOptions"
                       Alignment="Center"
                       Expands="True" />

        <x:Double x:Key="borderWidth">
            3
        </x:Double>
    </ResourceDictionary>
</ContentPage.Resources>

您不需要將它放在三行上。 此旋轉角度的這個字典專案只會佔用一行:

<ContentPage.Resources>
    <ResourceDictionary>
        <LayoutOptions x:Key="horzOptions"
                       Alignment="Center" />

        <LayoutOptions x:Key="vertOptions"
                       Alignment="Center"
                       Expands="True" />

         <x:Double x:Key="borderWidth">
            3
         </x:Double>

        <x:Double x:Key="rotationAngle">-15</x:Double>
    </ResourceDictionary>
</ContentPage.Resources>

這兩個資源可以使用與值相同的方式 LayoutOptions 來參考:

<Button Text="Do this!"
        HorizontalOptions="{StaticResource horzOptions}"
        VerticalOptions="{StaticResource vertOptions}"
        BorderWidth="{StaticResource borderWidth}"
        Rotation="{StaticResource rotationAngle}"
        TextColor="Red"
        FontSize="24" />

針對 類型的 Color資源,您可以使用直接指派這些型別屬性時所使用的相同字串表示法。 建立資源時會叫用類型轉換器。 以下是 類型的 Color資源:

<Color x:Key="textColor">Red</Color>

程式通常會將 FontSize 屬性設定為 列舉的成員, NamedSize 例如 Large。 類別 FontSizeConverter 會在幕後運作,以使用 Device.GetNamedSized 方法將其轉換成平臺相依值。 不過,定義字型大小資源時,使用數值會比較合理,如下所示為 x:Double 類型:

<x:Double x:Key="fontSize">24</x:Double>

現在資源設定會定義所有 Text 屬性:

<Button Text="Do this!"
        HorizontalOptions="{StaticResource horzOptions}"
        VerticalOptions="{StaticResource vertOptions}"
        BorderWidth="{StaticResource borderWidth}"
        Rotation="{StaticResource rotationAngle}"
        TextColor="{StaticResource textColor}"
        FontSize="{StaticResource fontSize}" />

您也可以在資源字典內使用 OnPlatform ,為平台定義不同的值。 以下是物件如何 OnPlatform 成為不同文字色彩之資源字典的一部分:

<OnPlatform x:Key="textColor"
            x:TypeArguments="Color">
    <On Platform="iOS" Value="Red" />
    <On Platform="Android" Value="Aqua" />
    <On Platform="UWP" Value="#80FF80" />
</OnPlatform>

請注意, OnPlatform 取得兩個 x:Key 屬性,因為它是字典中的物件,而 x:TypeArguments 屬性是因為它是泛型類別。 初始化 iOS物件時,、 AndroidUWP 屬性會轉換成 Color 值。

以下是最後一個完整的 XAML 檔案,其中三個按鈕會存取六個共用值:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SharedResourcesPage"
             Title="Shared Resources Page">

    <ContentPage.Resources>
        <ResourceDictionary>
            <LayoutOptions x:Key="horzOptions"
                           Alignment="Center" />

            <LayoutOptions x:Key="vertOptions"
                           Alignment="Center"
                           Expands="True" />

            <x:Double x:Key="borderWidth">3</x:Double>

            <x:Double x:Key="rotationAngle">-15</x:Double>

            <OnPlatform x:Key="textColor"
                        x:TypeArguments="Color">
                <On Platform="iOS" Value="Red" />
                <On Platform="Android" Value="Aqua" />
                <On Platform="UWP" Value="#80FF80" />
            </OnPlatform>

            <x:Double x:Key="fontSize">24</x:Double>
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>
        <Button Text="Do this!"
                HorizontalOptions="{StaticResource horzOptions}"
                VerticalOptions="{StaticResource vertOptions}"
                BorderWidth="{StaticResource borderWidth}"
                Rotation="{StaticResource rotationAngle}"
                TextColor="{StaticResource textColor}"
                FontSize="{StaticResource fontSize}" />

        <Button Text="Do that!"
                HorizontalOptions="{StaticResource horzOptions}"
                VerticalOptions="{StaticResource vertOptions}"
                BorderWidth="{StaticResource borderWidth}"
                Rotation="{StaticResource rotationAngle}"
                TextColor="{StaticResource textColor}"
                FontSize="{StaticResource fontSize}" />

        <Button Text="Do the other thing!"
                HorizontalOptions="{StaticResource horzOptions}"
                VerticalOptions="{StaticResource vertOptions}"
                BorderWidth="{StaticResource borderWidth}"
                Rotation="{StaticResource rotationAngle}"
                TextColor="{StaticResource textColor}"
                FontSize="{StaticResource fontSize}" />

    </StackLayout>
</ContentPage>

螢幕快照會驗證一致的樣式,以及平臺相依樣式:

樣式控件

雖然在頁面頂端定義集合最常見 Resources ,但請記住 Resources ,屬性是由 VisualElement定義,而且您可以在頁面上的其他元素上擁有 Resources 集合。 例如,請嘗試將新增至 StackLayout 此範例中的 :

<StackLayout>
    <StackLayout.Resources>
        <ResourceDictionary>
            <Color x:Key="textColor">Blue</Color>
        </ResourceDictionary>
    </StackLayout.Resources>
    ...
</StackLayout>

您會發現按鈕的文字色彩現在是藍色。 基本上,每當 XAML 剖析器遇到 StaticResource 標記延伸時,就會搜尋可視化樹狀結構,並使用包含該索引鍵的第一個 ResourceDictionary

儲存在資源字典中最常見的物件類型之一是 Xamarin.FormsStyle,它會定義屬性設定的集合。 樣式會在樣式一文中討論。

有時候,XAML 新手開發人員會想知道他們是否可以將視覺專案,例如 LabelButton 放在 中 ResourceDictionary。 雖然這肯定是可能的,但它沒有太大的意義。 的 ResourceDictionary 用途是共享物件。 無法共用視覺化專案。 同一個實例無法在單一頁面上出現兩次。

x:Static 標記延伸

儘管他們的名字相似, x:Static 而且 StaticResource 非常不同。 StaticResource 從資源字典傳回 物件,同時 x:Static 存取下列其中一項:

  • 公用靜態欄位
  • 公用靜態屬性
  • 公用常數位段
  • 列舉成員。

定義 StaticResource 資源字典的 XAML 實作支持標記延伸,而 x:Static 它是 XAML 的內部部分,如 x 前置詞所示。

以下是一些示範如何 x:Static 明確參考靜態字段和列舉成員的範例:

<Label Text="Hello, XAML!"
       VerticalOptions="{x:Static LayoutOptions.Start}"
       HorizontalTextAlignment="{x:Static TextAlignment.Center}"
       TextColor="{x:Static Color.Aqua}" />

到目前為止,這並不令人印象深刻。 但是標記 x:Static 延伸也可以從您自己的程式代碼參考靜態欄位或屬性。 例如,以下是一個 AppConstants 類別,其中包含一些您可能想要在整個應用程式中於多個頁面上使用的靜態字段:

using System;
using Xamarin.Forms;

namespace XamlSamples
{
    static class AppConstants
    {
        public static readonly Thickness PagePadding;

        public static readonly Font TitleFont;

        public static readonly Color BackgroundColor = Color.Aqua;

        public static readonly Color ForegroundColor = Color.Brown;

        static AppConstants()
        {
            switch (Device.RuntimePlatform)
            {
                case Device.iOS:
                    PagePadding = new Thickness(5, 20, 5, 0);
                    TitleFont = Font.SystemFontOfSize(35, FontAttributes.Bold);
                    break;

                case Device.Android:
                    PagePadding = new Thickness(5, 0, 5, 0);
                    TitleFont = Font.SystemFontOfSize(40, FontAttributes.Bold);
                    break;

                case Device.UWP:
                    PagePadding = new Thickness(5, 0, 5, 0);
                    TitleFont = Font.SystemFontOfSize(50, FontAttributes.Bold);
                    break;
            }
        }
    }
}

若要在 XAML 檔案中參考這個類別的靜態欄位,您需要某種方式來指出此檔案所在的 XAML 檔案內。 您可以使用 XML 命名空間宣告來執行此動作。

回想一下,作為標準 Xamarin.Forms XAML 範本一部分建立的 XAML 檔案包含兩個 XML 命名空間宣告:一個用於存取 Xamarin.Forms 類別,另一個用於參考 XAML 內建的標記和屬性:

xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

您需要其他 XML 命名空間宣告才能存取其他類別。 每個額外的 XML 命名空間宣告都會定義新的前置詞。 若要存取共享應用程式 .NET Standard 連結庫的本機類別,例如 AppConstants,XAML 程式設計人員通常會使用前置詞 local。 命名空間宣告必須指出 CLR (Common Language Runtime) 命名空間名稱,也稱為 .NET 命名空間名稱,也就是出現在 C# namespace 定義或指示詞中 using 的名稱:

xmlns:local="clr-namespace:XamlSamples"

您也可以在任何 .NET Standard 連結庫參考的元件中定義 .NET 命名空間的 XML 命名空間宣告。 例如,以下是sys標準 .NET System 命名空間的前置詞,其位於 netstandard 元件中。 因為這是另一個元件,因此您也必須指定元件名稱,在此案例 中為 netstandard

xmlns:sys="clr-namespace:System;assembly=netstandard"

請注意,關鍵詞 clr-namespace 後面接著冒號,然後是 .NET 命名空間名稱,後面接著分號、關鍵詞 assembly、等號和元件名稱。

是,冒號會跟著 clr-namespace ,但等號後面接著 assembly。 語法是以這種方式刻意定義:大部分的 XML 命名空間宣告會參考開頭 URI 配置名稱的 URI,例如 http,其一律後面接著冒號。 此 clr-namespace 字串的一部分是用來模擬該慣例。

這兩個命名空間宣告都包含在 StaticConstantsPage 範例中。 請注意,維度 BoxView 會設定為 Math.PIMath.E,但縮放比例為 100:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XamlSamples"
             xmlns:sys="clr-namespace:System;assembly=netstandard"
             x:Class="XamlSamples.StaticConstantsPage"
             Title="Static Constants Page"
             Padding="{x:Static local:AppConstants.PagePadding}">

    <StackLayout>
       <Label Text="Hello, XAML!"
              TextColor="{x:Static local:AppConstants.BackgroundColor}"
              BackgroundColor="{x:Static local:AppConstants.ForegroundColor}"
              Font="{x:Static local:AppConstants.TitleFont}"
              HorizontalOptions="Center" />

      <BoxView WidthRequest="{x:Static sys:Math.PI}"
               HeightRequest="{x:Static sys:Math.E}"
               Color="{x:Static local:AppConstants.ForegroundColor}"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand"
               Scale="100" />
    </StackLayout>
</ContentPage>

相對於畫面的結果 BoxView 大小與平台相關:

使用 x:Static 標記延伸的控件

其他標準標記延伸

數個標記延伸是 XAML 的內建,且在 XAML 檔案中 Xamarin.Forms 受到支援。 其中一些不是經常使用,但當您需要它們時非常重要:

  • 如果屬性預設有非 null 值,但您想要將它設定為 null,請將它設定為 {x:Null} 標記延伸。
  • 如果屬性的類型Type為 ,您可以使用標記延伸{x:Type someClass}將它指派給 Type 物件。
  • 您可以使用標記延伸在 XAML x:Array 中定義陣列。 此標記延伸具有名為 Type 的必要屬性,表示陣列中的項目類型。
  • 標記 Binding 延伸會在第 4 部分討論 。數據系結基本概念
  • 標記RelativeSource延伸會在相對系結討論。

ConstraintExpression 標記延伸

標記延伸可以有屬性,但是它們不會像 XML 屬性一樣設定。 在標記延伸中,屬性設定會以逗號分隔,而且大括弧內不會顯示引號。

這可以透過名為ConstraintExpression的Xamarin.Forms標記延伸來說明,其與 類別搭配RelativeLayout使用。 您可以將子檢視的位置或大小指定為常數,或相對於父檢視或其他具名檢視。 的ConstraintExpression語法可讓您使用另一Factor個檢視的屬性加上 ,來設定檢視的位置或大小。Constant 比需要程式代碼更複雜的任何專案。

以下是範例:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.RelativeLayoutPage"
             Title="RelativeLayout Page">

    <RelativeLayout>

        <!-- Upper left -->
        <BoxView Color="Red"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=Constant,
                                            Constant=0}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=Constant,
                                            Constant=0}" />
        <!-- Upper right -->
        <BoxView Color="Green"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Width,
                                            Factor=1,
                                            Constant=-40}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=Constant,
                                            Constant=0}" />
        <!-- Lower left -->
        <BoxView Color="Blue"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=Constant,
                                            Constant=0}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Height,
                                            Factor=1,
                                            Constant=-40}" />
        <!-- Lower right -->
        <BoxView Color="Yellow"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Width,
                                            Factor=1,
                                            Constant=-40}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Height,
                                            Factor=1,
                                            Constant=-40}" />

        <!-- Centered and 1/3 width and height of parent -->
        <BoxView x:Name="oneThird"
                 Color="Red"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Width,
                                            Factor=0.33}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Height,
                                            Factor=0.33}"
                 RelativeLayout.WidthConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Width,
                                            Factor=0.33}"
                 RelativeLayout.HeightConstraint=
                     "{ConstraintExpression Type=RelativeToParent,
                                            Property=Height,
                                            Factor=0.33}"  />

        <!-- 1/3 width and height of previous -->
        <BoxView Color="Blue"
                 RelativeLayout.XConstraint=
                     "{ConstraintExpression Type=RelativeToView,
                                            ElementName=oneThird,
                                            Property=X}"
                 RelativeLayout.YConstraint=
                     "{ConstraintExpression Type=RelativeToView,
                                            ElementName=oneThird,
                                            Property=Y}"
                 RelativeLayout.WidthConstraint=
                     "{ConstraintExpression Type=RelativeToView,
                                            ElementName=oneThird,
                                            Property=Width,
                                            Factor=0.33}"
                 RelativeLayout.HeightConstraint=
                     "{ConstraintExpression Type=RelativeToView,
                                            ElementName=oneThird,
                                            Property=Height,
                                            Factor=0.33}"  />
    </RelativeLayout>
</ContentPage>

也許您應該從這個範例中吸取的最重要課程是標記延伸的語法:標記延伸的大括號內不得出現引號。 在 XAML 檔案中輸入標記延伸時,自然會想要以引號括住屬性的值。 抵制誘惑!

以下是正在執行的程式:

使用條件約束的相對版面配置

摘要

這裏顯示的 XAML 標記延伸提供 XAML 檔案的重要支援。 但也許最有價值的 XAML 標記延伸是 Binding,此系列 的第 4 部分涵蓋。數據系結基本概念