使用樣式來建立一致的 UI
資源適合用來避免在 XAML 標記中使用硬式編碼的重複值,但套用起來可能很單調。 您可以個別指派每個屬性值,這可能會導致產生雜亂且冗長的 XAML。 此單元會示範如何將多個設定群組為樣式,有助於清理程式碼並使其更容易維護。
資源可能如何讓您的 XAML 變得雜亂
資源會為單一屬性提供值。 但使用大量資源可能會導致冗長的 XAML。 假設您想要自訂按鈕的外觀。 首先,您會為所需的值建立資源。 接著,您會將每個資源套用至所有按鈕。 下列程式碼顯示兩個按鈕的 XAML 標記可能的樣子。
<Button
Text = "OK"
BackgroundColor = "{StaticResource highlightColor}"
BorderColor = "{StaticResource borderColor}"
BorderWidth = "{StaticResource borderWidth}"
TextColor = "{StaticResource textColor}" />
<Button
Text = "Cancel"
BackgroundColor = "{StaticResource highlightColor}"
BorderColor = "{StaticResource borderColor}"
BorderWidth = "{StaticResource borderWidth}"
TextColor = "{StaticResource textColor}" />
請注意相同的五個屬性在每個按鈕上的設定方式。 使用資源就不需其中四個項中重複的硬式編碼值。 不過,這類型的 XAML 標記很快就難以閱讀。 此外,如果您為每個控制項設定大量屬性,很容易不小心省略其中一個屬性,導致控制項的外觀不一致。 解決方案是建立一個樣式,一次指派所有四個屬性。
什麼是 Setter?
Setter 是用來建立樣式的重要元件。
Setter 是屬性/值組的容器。 您可以將 setter 想像成代表指派陳述式。 指定要指派哪些屬性,以及要套用的值。 您通常會在 XAML 標記中建立 Setter 物件。 以下範例會為 TextColor 屬性建立 Setter 物件。
<Setter Property="TextColor" Value="White" />
您可以使用資源做為 setter 中的值,如以下程式碼所示。 當您想在多個 setter 中使用相同的值時,這個技術非常有用。
<Setter Property="TextColor" Value="{StaticResource textColor}" />
注意
您在 setter 中指定的屬性值必須實作成可繫結屬性。 .NET MAUI 中以尾碼 Property 結尾的控制項上的所有屬性,都是可繫結的屬性。 如果您嘗試在 Setter 中使用 TextColor 等屬性,請確定該控制項有名為 TextColorProperty 的相應可綁定屬性。 實際上,幾乎所有要在 setter 中使用的屬性都是以這種方式實作。
什麼是樣式?
樣式是以特定型別控制項為目標的 setter 集合。 .NET MAUI 需要目標型別,以便確定 setter 中的屬性存在於該型別上。
下列程式碼示範一個樣式,此樣式合併了來自先前範例的四個值。 請注意,TargetType 設定為 Button,setter 中的所有屬性都是 Button 類別的成員。 您無法將此樣式用於標籤,因為 Label 類別不包含 BorderColor 或 BorderWidth 屬性。
<Style TargetType="Button">
<Setter Property="BackgroundColor" Value="#2A84D3" />
<Setter Property="BorderColor" Value="#1C5F9B" />
<Setter Property="BorderWidth" Value="3" />
<Setter Property="TextColor" Value="White" />
</Style>
定義樣式
您通常會將樣式定義為 ResourceDictionary 物件內的資源。 資源字典可讓將您輕鬆地將樣式用於同一個頁面上的多個控制項,或甚至用於整個應用程式。 下列程式碼示範如何將樣式定義為字典內的資源。 請注意,該樣式是使用 x:Key 屬性命名。 命名樣式可讓您在 XAML 頁面中參照樣式。
<ContentPage.Resources>
<Style x:Key="MyButtonStyle" TargetType="Button">
...
</Style>
</ContentPage.Resources>
套用樣式
您可以藉由將名稱指派給 Style 屬性,將樣式附加至控制項。 指派會促使將樣式中的每個 Setter 物件套用至目標控制項。 下列程式碼示範如何將按鈕樣式套用至兩個按鈕。
<Button Text="OK" Style="{StaticResource MyButtonStyle}" />
<Button Text="Cancel" Style="{StaticResource MyButtonStyle}" />
在上一個範例中,您使用 StaticResource 標記延伸模組,將樣式附加至控制項。 當您不需要在執行階段變更樣式時,此技術是絕佳選擇。 但如果您想要實作動態佈景主題這類 UI 需要變更的項目,該怎麼辦? 在此情況下,您可以使用 DynamicResource 標記延伸模組來載入樣式。
<Button Text="Cancel" Style="{DynamicResource MyButtonStyle}" />
DynamicResource 留意取代資源字典中的 x:Key 屬性。 如果您撰寫的程式碼會將新樣式以該相同 x:Key 值載入到 ResourceDictionary 中,則新樣式會自動套用至您的 UI。
將隱含樣式用於多個控制項
假設您的 UI 有 50 個按鈕,而您想要將相同樣式套用至這全部的按鈕。 根據我們目前所知,您必須手動指派給每個按鈕上的 Style 屬性。 這不是那麼難,但它仍然很單調乏味。
「隱含樣式」是您新增至資源字典的樣式,但沒有提供 x:Key 識別碼。 隱含樣式會自動套用至指定 TargetType 物件的所有控制項。
下列程式碼顯示上一個宣告為隱含樣式的範例。 此樣式會套用至頁面上的每個按鈕。
<ContentPage.Resources>
<Style TargetType="Button">
<Setter Property="BackgroundColor" Value="Blue" />
<Setter Property="BorderColor" Value="Navy" />
...
</Style>
</ContentPage.Resources>
重要
隱含樣式與控制項的對應必須與指定的 TargetType 完全符合。 繼承自目標型別的控制項將不會收到樣式。 若要影響繼承的控制項,您可以在定義樣式時,將 Style.ApplyToDerivedTypes 屬性設為 True。 例如,若要將樣式套用至 Button 型別,並讓該樣式影響從 Button 繼承的任何按鈕 (例如 ImageButton、RadioButton,或您建立的自訂型別),您可以使用這類樣式。
<ContentPage.Resources>
<Style TargetType="Button"
ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor" Value="Black" />
</Style>
</ContentPage.Resources>
覆寫樣式
您可以將樣式想成提供控制項的一組預設值。 現有的樣式可能接近您的需求,但包含一兩個您不想要的 setter。 在該情況下,您可以套用樣式,然後透過直接設定屬性來覆寫該值。 明確的設定會在樣式之後套用,因此其會覆寫來自該樣式的值。
假設您想要將下列樣式用於您頁面上的數個按鈕。
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="BackgroundColor" Value="Blue" />
<Setter Property="BorderRadius" Value="10" />
<Setter Property="BorderWidth" Value="3" />
</Style>
此樣式適用於所有按鈕,但需要紅色背景的 [取消] 按鈕除外。 只要您同時直接設定 BackgroundColor 屬性,就可以對 [取消] 按鈕使用相同樣式。 下列程式碼示範如何覆寫色彩設定。
<Button
Text="Cancel"
Style="{StaticResource MyButtonStyle}"
BackgroundColor="Red"
... />
以上階類型為目標
假設您想要自訂按鈕和標籤的背景色彩。 您可以為每種型別建立另外的樣式,或者可以建立將 TargetType 設定為 VisualElement 的樣式。 由於 VisualElement 是 Button 和 Label 的基底類別,因此這項技巧可行。
下列程式碼示範一個以基底類別為目標的樣式,該基底類別會套用至兩個不同的衍生類型。
<Style x:Key="MyVisualElementStyle" TargetType="VisualElement">
<Setter Property="BackgroundColor" Value="#2A84D3" />
</Style>
...
<Button Style="{StaticResource MyVisualElementStyle}" ... />
<Label Style="{StaticResource MyVisualElementStyle}" ... />
此範例使用 x:Key 識別樣式,且控制項會明確套用。 隱含樣式在此處沒有作用,因為隱含樣式的 TargetType 必須與控制項型別完全相符。
使用 BasedOn 從樣式繼承
假設您想要為您的 UI 建立一致的外觀。 您決定所有的控制項都應該都使用一致的背景色彩。 背景色彩設定可能會出現在您的多個樣式中。 下列程式碼示範兩種具有重複 setter 的樣式。
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="BackgroundColor" Value="Blue" />
<Setter Property="BorderColor" Value="Navy" />
<Setter Property="BorderWidth" Value="5" />
</Style>
<Style x:Key="MyEntryStyle" TargetType="Entry">
<Setter Property="BackgroundColor" Value="Blue" />
<Setter Property="TextColor" Value="White" />
</Style>
您可以使用樣式繼承,將重複的 setter 分解為基底樣式。 若要建立衍生樣式,請將其 BasedOn 屬性設為參照基底樣式。 新的樣式會從其基底樣式繼承所有 setter。 衍生的樣式也可以新增新的 setter,或以包含不同值的 setter 取代繼承的 setter。
下列程式碼示範重構成階層的先前範例樣式。 通用 setter 只會出現在基底樣式中,而不會重複出現。 請注意,您會使用 StaticResource 標記延伸模組,查詢基底樣式。 您無法在此情況下使用 DynamicResource。
<Style x:Key="MyVisualElementStyle" TargetType="VisualElement">
<Setter Property="BackgroundColor" Value="Blue" />
</Style>
<Style x:Key="MyButtonStyle" TargetType="Button" BasedOn="{StaticResource MyVisualElementStyle}">
<Setter Property="BorderColor" Value="Navy" />
<Setter Property="BorderWidth" Value="5" />
</Style>
<Style x:Key="MyEntryStyle" TargetType="Entry" BasedOn="{StaticResource MyVisualElementStyle}">
<Setter Property="TextColor" Value="White" />
</Style>
基底樣式與衍生樣式的 TargetType 值必須相容。 若要讓樣式相容,樣式必須具有相同 TargetType 屬性,或衍生樣式的 TargetType 是基底樣式 TargetType 的子代。