使用XAML樣式設置Xamarin.Forms應用的樣式
Xamarin.Forms應用程序的樣式傳統上是通過使用Style類將一組屬性值分組到一個對象中來完成的,然后可以將其應用於多個視覺元素實例。 這有助於減少重復標記,並使應用外觀更容易更改。
使用級聯樣式表樣式化Xamarin.Forms應用程序
Xamarin.Forms支持使用級聯樣式表(CSS)設置視覺元素的樣式。 樣式表由規則列表組成,每個規則由一個或多個選擇器以及一個聲明塊組成。
但是在Xamarin.Forms中,CSS樣式表在運行時而不是在編譯時進行分析和評估,並且樣式表在使用時進行重新分析。更多參考
以下只介紹通過XAML Styles來設置
介紹
樣式允許自定義視覺元素的外觀。 樣式是為特定類型定義的,並包含該類型上可用屬性的值。
Xamarin.Forms應用程序通常包含外觀相同的多個控件。 例如,一個應用程序可能具有多個具有相同字體選項和布局選項的Label實例,如以下XAML代碼示例所示:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.NoStylesPage" Title="No Styles" IconImageSource="xaml.png"> <ContentPage.Content> <StackLayout Padding="0,20,0,0"> <Label Text="These labels" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" FontSize="Large" /> <Label Text="are not" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" FontSize="Large" /> <Label Text="using styles" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" FontSize="Large" /> </StackLayout> </ContentPage.Content> </ContentPage>
設置每個單獨控件的外觀可能會重復且容易出錯,instead,可以創建定義外觀的樣式,然后將其應用於所需的控件。
Create a style
Style類將屬性值的集合分組為一個對象,然后可以將其應用於多個視覺元素實例。這有助於減少重復標記,並使應用程序外觀更容易更改。
盡管樣式主要是為基於XAML的應用程序設計的,但是它們也可以在C#中創建:
- 在XAML中創建的樣式實例通常在ResourceDictionary中定義,該ResourceDictionary分配給控件、頁面的Resources集合或應用程序的Resources集合。
- 用C#創建的樣式實例通常在頁面的類中定義,或者在可以全局訪問的類中定義。
選擇在哪里定義樣式會影響可以在哪里使用:
- 在控件級別定義的樣式實例只能應用於控件及其子級。
- 在頁面級別定義的樣式實例只能應用於頁面及其子元素。
- 在應用程序級別定義的樣式實例可以應用於整個應用程序。
注:視圖層次結構中較低的樣式優先於較高的樣式。 例如,在應用程序級別設置將Label.TextColor設置為Red的樣式將被將Label.TextColor設置為Green的頁面級別樣式覆蓋。 同樣,頁面級別樣式將被控件級別樣式覆蓋。 另外,如果直接在控件屬性上設置Label.TextColor,則它優先於所有樣式。
每個Style實例都包含一個或多個Setter對象的集合,每個Setter都具有一個Property和一個Value。 “屬性”是樣式將應用到的元素的可綁定屬性的名稱,“值”是應用於屬性的值。
每個Style實例可以是顯式的,也可以是隱式的:
- 通過指定TargetType和x:Key值,以及將目標元素的Style屬性設置為x:Key引用,可以定義一個顯式的Style實例。 有關顯式樣式的更多信息,請參見顯式樣式。
- 通過僅指定TargetType來定義隱式Style實例。 然后,Style實例將自動應用於該類型的所有元素。 請注意,TargetType的子類不會自動應用樣式。 有關隱式樣式的更多信息,請參見隱式樣式。
創建樣式時,始終需要TargetType屬性。 以下代碼示例顯示了在XAML中創建的顯式樣式(請注意x:Key):

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.ExplicitStylesPage" Title="Explicit" IconImageSource="xaml.png"> <ContentPage.Resources> <ResourceDictionary> <Style x:Key="labelStyle" TargetType="Label"> <Setter Property="HorizontalOptions" Value="Center" /> <Setter Property="VerticalOptions" Value="CenterAndExpand" /> <Setter Property="FontSize" Value="Large" /> <Setter Property="TextColor" Value="Red" /> </Style> <Style x:Key="labelGreenStyle" TargetType="Label"> ... <Setter Property="TextColor" Value="Green" /> </Style> <Style x:Key="labelBlueStyle" TargetType="Label"> ... <Setter Property="TextColor" Value="Blue" /> </Style> </ResourceDictionary> </ContentPage.Resources>
要應用樣式,目標對象必須是與樣式的TargetType屬性值匹配的VisualElement,如以下XAML代碼示例所示:
<Label Text="Demonstrating an explicit style" Style="{StaticResource labelStyle}" />
顯式樣式
顯式樣式是通過設置控件的樣式屬性而有選擇地應用於控件的樣式。
要在頁面級別聲明樣式,必須將ResourceDictionary添加到頁面,然后可以在ResourceDictionary中包含一個或多個樣式聲明。 通過為其聲明提供x:Key屬性來使Style顯式化,該屬性在ResourceDictionary中為其提供了描述性鍵。 然后必須通過設置顯式樣式的“樣式”屬性將其應用於特定的視覺元素。
上面的例子就是 定義的顯示樣式(在頁面級別)。
還可以在控件級別定義樣式:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.ExplicitStylesPage" Title="Explicit" IconImageSource="xaml.png"> <ContentPage.Content> <StackLayout Padding="0,20,0,0"> <StackLayout.Resources> <ResourceDictionary> <Style x:Key="labelRedStyle" TargetType="Label"> ... </Style> ... </ResourceDictionary> </StackLayout.Resources> <Label Text="These labels" Style="{StaticResource labelRedStyle}" /> ... </StackLayout> </ContentPage.Content> </ContentPage>
隱式樣式
隱式樣式是同一TargetType的所有控件使用的隱式樣式,不需要每個控件都引用該樣式。
要在頁面級別聲明樣式,必須將ResourceDictionary添加到頁面,然后可以在ResourceDictionary中包含一個或多個樣式聲明。 通過不指定x:Key屬性來使樣式隱式化。 然后,該樣式將應用於與TargetType完全匹配的視覺元素,但不適用於從TargetType值派生的元素。
以下代碼示例顯示了XAML在頁面的ResourceDictionary中聲明的隱式樣式,並將其應用於頁面的Entry實例:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:Styles;assembly=Styles" x:Class="Styles.ImplicitStylesPage" Title="Implicit" IconImageSource="xaml.png"> <ContentPage.Resources> <ResourceDictionary> <Style TargetType="Entry"> <Setter Property="HorizontalOptions" Value="Fill" /> <Setter Property="VerticalOptions" Value="CenterAndExpand" /> <Setter Property="BackgroundColor" Value="Yellow" /> <Setter Property="FontAttributes" Value="Italic" /> <Setter Property="TextColor" Value="Blue" /> </Style> </ResourceDictionary> </ContentPage.Resources> <ContentPage.Content> <StackLayout Padding="0,20,0,0"> <Entry Text="These entries" /> <Entry Text="are demonstrating" /> <Entry Text="implicit styles," /> <Entry Text="and an implicit style override" BackgroundColor="Lime" TextColor="Red" /> <local:CustomEntry Text="Subclassed Entry is not receiving the style" /> </StackLayout> </ContentPage.Content> </ContentPage>
ResourceDictionary定義了一種應用於頁面的Entry實例的隱式樣式。 樣式用於在黃色背景上顯示藍色文本,同時還設置其他外觀選項。 樣式將添加到頁面的ResourceDictionary中,而無需指定x:Key屬性。 因此,將樣式隱式地應用於所有Entry實例,因為它們與Style的TargetType屬性完全匹配。 但是,該樣式不適用於CustomEntry實例,它是一個Entry的子類。
將樣式用於派生類型
Style.ApplyToDerivedTypes屬性使樣式可以應用於從TargetType屬性引用的基本類型派生的控件。 因此,將此屬性設置為true可使單個樣式定位多種類型,前提是這些類型派生自TargetType屬性中指定的基本類型。
下面的示例顯示一個隱式樣式,該樣式將Button實例的背景色設置為紅色:
全局樣式
通過將樣式添加到應用程序的資源字典中,可以使樣式全局可用。 這有助於避免在頁面或控件之間重復樣式。
默認情況下,從模板創建的所有Xamarin.Forms應用程序都使用App類來實現Application子類。 若要在應用程序級別聲明樣式,必須在使用XAML的應用程序的ResourceDictionary中,將默認App類替換為XAML App類並在其后添加相關代碼。 有關更多信息,請參見Working with the App Class.
以下代碼示例顯示了在應用程序級別聲明的Style:

<Application xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.App"> <Application.Resources> <ResourceDictionary> <Style x:Key="buttonStyle" TargetType="Button"> <Setter Property="HorizontalOptions" Value="Center" /> <Setter Property="VerticalOptions" Value="CenterAndExpand" /> <Setter Property="BorderColor" Value="Lime" /> <Setter Property="BorderRadius" Value="5" /> <Setter Property="BorderWidth" Value="5" /> <Setter Property="WidthRequest" Value="200" /> <Setter Property="TextColor" Value="Teal" /> </Style> </ResourceDictionary> </Application.Resources> </Application>
樣式繼承
樣式可以繼承其他樣式,以減少重復並實現重用。
通過將Style.BasedOn屬性設置為現有Style,可以執行樣式繼承。 在XAML中,這是通過將BasedOn屬性設置為引用以前創建的Style的StaticResource標記擴展來實現的。 在C#中,這是通過將BasedOn屬性設置為Style實例來實現的。
從基本樣式繼承的樣式可以包括新屬性的Setter實例,或使用它們從基本樣式中覆蓋樣式。
此外,從基本樣式繼承的樣式必須以相同的類型為目標,或者從基本樣式為目標的類型派生的類型為目標。 例如,如果基本樣式針對View實例,則基於基本樣式的樣式可以針對View實例或從View類派生的類型,例如Label和Button實例。
以下代碼演示了XAML頁面中的顯式樣式繼承:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.StyleInheritancePage" Title="Inheritance" IconImageSource="xaml.png"> <ContentPage.Resources> <ResourceDictionary> <Style x:Key="baseStyle" TargetType="View"> <Setter Property="HorizontalOptions" Value="Center" /> <Setter Property="VerticalOptions" Value="CenterAndExpand" /> </Style> <Style x:Key="labelStyle" TargetType="Label" BasedOn="{StaticResource baseStyle}"> ... <Setter Property="TextColor" Value="Teal" /> </Style> <Style x:Key="buttonStyle" TargetType="Button" BasedOn="{StaticResource baseStyle}"> <Setter Property="BorderColor" Value="Lime" /> ... </Style> </ResourceDictionary> </ContentPage.Resources> <ContentPage.Content> <StackLayout Padding="0,20,0,0"> <Label Text="These labels" Style="{StaticResource labelStyle}" /> ... <Button Text="So is the button" Style="{StaticResource buttonStyle}" /> </StackLayout> </ContentPage.Content> </ContentPage>
baseStyle以View實例為目標,並設置HorizontalOptions和VerticalOptions屬性,baseStyle不能直接在任何控件上設置。 而是,labelStyle和buttonStyle從其繼承,設置其他可綁定屬性值,然后,通過設置其Style屬性,將labelStyle和buttonStyle應用於Label實例和Button實例。
注:隱式樣式可以從顯式樣式派生,但是顯式樣式不能從隱式樣式派生。
尊重繼承鏈
樣式只能繼承視圖層次結構中相同級別或更高級別的樣式。 這意味着:
- 應用程序級資源只能繼承其他應用程序級資源。
- 頁面級資源可以從應用程序級資源和其他頁面級資源繼承。
- 控制級資源可以從應用程序級資源,頁面級資源和其他控制級資源繼承。
動態樣式
樣式不響應屬性更改,並且在應用程序持續時間內保持不變。 例如,在將樣式分配給視覺元素后,如果修改,刪除了一個Setter實例或添加了一個新的Setter實例,則更改將不會應用於視覺元素。 但是,應用程序可以使用動態資源在運行時動態響應樣式更改。
DynamicResource標記擴展與StaticResource標記擴展類似,兩者均使用字典鍵從ResourceDictionary獲取值。 但是,StaticResource執行單個字典查找,DynamicResource會維護指向字典鍵的鏈接。 因此,如果替換了與鍵關聯的字典條目,則更改將應用於可視元素,這使得可以在應用程序中進行運行時樣式更改。
下面的代碼示例演示XAML頁面中的動態樣式:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.DynamicStylesPage" Title="Dynamic" IconImageSource="xaml.png"> <ContentPage.Resources> <ResourceDictionary> <Style x:Key="baseStyle" TargetType="View"> ... </Style> <Style x:Key="blueSearchBarStyle" TargetType="SearchBar" BasedOn="{StaticResource baseStyle}"> ... </Style> <Style x:Key="greenSearchBarStyle" TargetType="SearchBar"> ... </Style> ... </ResourceDictionary> </ContentPage.Resources> <ContentPage.Content> <StackLayout Padding="0,20,0,0"> <SearchBar Placeholder="These SearchBar controls" Style="{DynamicResource searchBarStyle}" /> ... </StackLayout> </ContentPage.Content> </ContentPage>
SearchBar實例使用DynamicResource標記擴展來引用名為searchBarStyle的樣式,該樣式未在XAML中定義。 但是,由於SearchBar實例的Style屬性是使用DynamicResource設置的,因此缺少的字典鍵不會導致引發異常。
需要在代碼隱藏文件中,構造函數 使用key=searchBarStyle創建一個ResourceDictionary條目,如以下代碼示例所示:

public partial class DynamicStylesPage : ContentPage { bool originalStyle = true; public DynamicStylesPage () { InitializeComponent (); Resources ["searchBarStyle"] = Resources ["blueSearchBarStyle"]; } void OnButtonClicked (object sender, EventArgs e) { if (originalStyle) { Resources ["searchBarStyle"] = Resources ["greenSearchBarStyle"]; originalStyle = false; } else { Resources ["searchBarStyle"] = Resources ["blueSearchBarStyle"]; originalStyle = true; } } }
動態樣式繼承
使用Style.BasedOn屬性無法實現從動態樣式派生樣式。 而是,Style類包括BaseResourceKey屬性,可以將其設置為字典鍵,其值可能會動態更改。
下面的代碼示例演示了XAML頁面中的動態樣式繼承:
【見示例】
SearchBar實例使用StaticResource標記擴展來引用名為tealSearchBarStyle的樣式。 此樣式設置一些其他屬性,並使用BaseResourceKey屬性引用searchBarStyle。 不需要DynamicResource標記擴展,因為tealSearchBarStyle不會改變,除了它派生的Style之外【BaseResourceKey】。 因此,tealSearchBarStyle維護指向searchBarStyle的鏈接,並且在基本樣式更改時更改。
在代碼隱藏文件中,構造函數使用鍵searchBarStyle創建一個ResourceDictionary條目,如上一個演示動態樣式的示例所示。 執行OnButtonClicked事件處理程序時,searchBarStyle將在blueSearchBarStyle和greenSearchBarStyle之間切換。 這導致以下屏幕快照中顯示的外觀:
設備樣式
Xamarin.Forms在Device.Styles類中包含六種動態樣式,稱為設備樣式。
這些設備樣式為:
所有六個樣式只能應用於Label實例。 例如,顯示段落正文的Label可以將其Style屬性設置為BodyStyle。
以下代碼示例演示了如何在XAML頁面中使用設備樣式:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.DeviceStylesPage" Title="Device" IconImageSource="xaml.png"> <ContentPage.Resources> <ResourceDictionary> <Style x:Key="myBodyStyle" TargetType="Label" BaseResourceKey="BodyStyle"> <Setter Property="TextColor" Value="Accent" /> </Style> </ResourceDictionary> </ContentPage.Resources> <ContentPage.Content> <StackLayout Padding="0,20,0,0"> <Label Text="Title style" Style="{DynamicResource TitleStyle}" /> <Label Text="Subtitle text style" Style="{DynamicResource SubtitleStyle}" /> <Label Text="Body style" Style="{DynamicResource BodyStyle}" /> <Label Text="Caption style" Style="{DynamicResource CaptionStyle}" /> <Label Text="List item detail text style" Style="{DynamicResource ListItemDetailTextStyle}" /> <Label Text="List item text style" Style="{DynamicResource ListItemTextStyle}" /> <Label Text="No style" /> <Label Text="My body style" Style="{StaticResource myBodyStyle}" /> </StackLayout> </ContentPage.Content> </ContentPage>
設備樣式必須使用DynamicResource標記擴展名進行綁定。 通過更改文本大小的輔助功能設置,可以在iOS中看到樣式的動態性質。 每個平台上設備樣式的外觀都不同,如以下屏幕截圖所示:
設備樣式也可以通過將BaseResourceKey屬性設置為設備樣式的鍵名稱來派生。 在上面的代碼示例中,myBodyStyle繼承自BodyStyle並設置了帶重音的文本顏色。 有關動態樣式繼承的更多信息,請參見動態樣式繼承。
可訪問性
設備樣式尊重可訪問性首選項,因此字體大小將隨每個平台上可訪問性首選項的改變而變化。 因此,要支持可訪問的文本,請確保將設備樣式用作應用程序中任何文本樣式的基礎。
樣式類
Xamarin.Forms樣式類使多種樣式可以應用於控件,而無需求助於樣式繼承。
創建樣式類
可以通過將Style的Class屬性設置為表示類名的字符串來創建樣式類。 與使用x:Key屬性定義顯式樣式相比,此方法提供的優點是可以將多個樣式類應用於VisualElement。
注:多種樣式可以共享相同的類名,只要它們針對不同的類型。 這使多個名稱相同的樣式類可以針對不同的類型。
下面的示例顯示三個BoxView樣式類和一個VisualElement樣式類:

<ContentPage ...> <ContentPage.Resources> <Style TargetType="BoxView" Class="Separator"> <Setter Property="BackgroundColor" Value="#CCCCCC" /> <Setter Property="HeightRequest" Value="1" /> </Style> <Style TargetType="BoxView" Class="Rounded"> <Setter Property="BackgroundColor" Value="#1FAECE" /> <Setter Property="HorizontalOptions" Value="Start" /> <Setter Property="CornerRadius" Value="10" /> </Style> <Style TargetType="BoxView" Class="Circle"> <Setter Property="BackgroundColor" Value="#1FAECE" /> <Setter Property="WidthRequest" Value="100" /> <Setter Property="HeightRequest" Value="100" /> <Setter Property="HorizontalOptions" Value="Start" /> <Setter Property="CornerRadius" Value="50" /> </Style> <Style TargetType="VisualElement" Class="Rotated" ApplyToDerivedTypes="true"> <Setter Property="Rotation" Value="45" /> </Style> </ContentPage.Resources> </ContentPage>
Separator,Rounded和Circle樣式類分別將BoxView屬性設置為特定值。
Rotated樣式類具有VisualElement的TargetType,這意味着它只能應用於VisualElement實例。 但是,其ApplyToDerivedTypes屬性設置為true,以確保可以將其應用於從VisualElement派生的任何控件,例如BoxView。
使用樣式類
可以通過將控件的StyleClass屬性(類型為IList <string>)設置為樣式類名稱列表來使用樣式類。 如果控件的類型與樣式類的TargetType相匹配,則將應用樣式類。
下面的示例顯示三個BoxView實例,每個實例設置為不同的樣式類:

<ContentPage ...> <ContentPage.Resources> ... </ContentPage.Resources> <StackLayout Margin="20"> <BoxView StyleClass="Separator" /> <BoxView WidthRequest="100" HeightRequest="100" HorizontalOptions="Center" StyleClass="Rounded, Rotated" /> <BoxView HorizontalOptions="Center" StyleClass="Circle" /> </StackLayout> </ContentPage>
在此示例中,第一個BoxView的樣式設置為行分隔符,而第三個BoxView的樣式設置為圓形。 第二個BoxView應用了兩個樣式類,這些類給其圓角並將其旋轉45度:
注:可以將多個樣式類應用於控件,因為StyleClass屬性的類型為IList <string>。 發生這種情況時,樣式類將以升序排列。 因此,當多個樣式類設置相同的屬性時,位於列表最高位置的樣式類中的屬性將優先。