Xamarin.Forms之样式

使用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>
View Code

设置每个单独控件的外观可能会重复且容易出错,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>
View Code

要应用样式,目标对象必须是与样式的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>
View Code

隐式样式

隐式样式是同一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>
View Code

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>
View Code

样式继承

样式可以继承其他样式,以减少重复并实现重用。

通过将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>
View Code

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>
View Code

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;
        }
    }
}
View Code

动态样式继承

使用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>
View Code

设备样式必须使用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>
View Code

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>
View Code

在此示例中,第一个BoxView的样式设置为行分隔符,而第三个BoxView的样式设置为圆形。 第二个BoxView应用了两个样式类,这些类给其圆角并将其旋转45度:

注:可以将多个样式类应用于控件,因为StyleClass属性的类型为IList <string>。 发生这种情况时,样式类将以升序排列。 因此,当多个样式类设置相同的属性时,位于列表最高位置的样式类中的属性将优先

原文地址:https://www.cnblogs.com/peterYong/p/11798243.html