第 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:Double
與 x: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
物件時,、 Android
和 UWP
屬性會轉換成 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 新手開發人員會想知道他們是否可以將視覺專案,例如 Label
或 Button
放在 中 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.PI
和 Math.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
大小與平台相關:
其他標準標記延伸
數個標記延伸是 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 部分涵蓋。數據系結基本概念。