Xamarin.Forms 中的动态样式

样式不会响应属性更改,并在应用程序期间保持不变。 例如,将样式分配到视觉元素后,如果修改、删除其中一个 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 中定义的 Style。 不过,由于 SearchBar 实例的 Style 属性是使用 DynamicResource 设置的,因此缺少字典键不会导致引发异常。

相反,在代码隐藏文件中,构造函数会创建一个包含 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;
        }
    }
}

执行 OnButtonClicked 事件处理程序时,searchBarStyle 将在 blueSearchBarStylegreenSearchBarStyle 之间切换。 这会导致如以下屏幕截图中所示的外观:

蓝色动态样式示例绿色动态样式示例

以下代码示例展示了 C# 中的等效页面:

public class DynamicStylesPageCS : ContentPage
{
    bool originalStyle = true;

    public DynamicStylesPageCS ()
    {
        ...
        var baseStyle = new Style (typeof(View)) {
            ...
        };
        var blueSearchBarStyle = new Style (typeof(SearchBar)) {
            ...
        };
        var greenSearchBarStyle = new Style (typeof(SearchBar)) {
            ...
        };
        ...
        var searchBar1 = new SearchBar { Placeholder = "These SearchBar controls" };
        searchBar1.SetDynamicResource (VisualElement.StyleProperty, "searchBarStyle");
        ...
        Resources = new ResourceDictionary ();
        Resources.Add ("blueSearchBarStyle", blueSearchBarStyle);
        Resources.Add ("greenSearchBarStyle", greenSearchBarStyle);
        Resources ["searchBarStyle"] = Resources ["blueSearchBarStyle"];

        Content = new StackLayout {
            Children = { searchBar1, searchBar2, searchBar3, searchBar4,    button    }
        };
    }
    ...
}

在 C# 中,SearchBar 实例使用 SetDynamicResource 方法来引用 searchBarStyleOnButtonClicked 事件处理程序代码与 XAML 示例相同,执行时,searchBarStyle 将在 blueSearchBarStylegreenSearchBarStyle 之间切换。

动态样式继承

无法使用 Style.BasedOn 属性从动态样式派生样式。 相反,Style 类包括 BaseResourceKey 属性,该属性可以设置为可动态更改其值的字典键。

以下代码示例演示 XAML 页面中的动态样式继承

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Styles.DynamicStylesInheritancePage" Title="Dynamic Inheritance" 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>
            <Style x:Key="tealSearchBarStyle" TargetType="SearchBar" BaseResourceKey="searchBarStyle">
              ...
            </Style>
            ...
        </ResourceDictionary>
    </ContentPage.Resources>
    <ContentPage.Content>
        <StackLayout Padding="0,20,0,0">
            <SearchBar Text="These SearchBar controls" Style="{StaticResource tealSearchBarStyle}" />
            ...
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

SearchBar 实例使用 StaticResource 标记扩展来引用名为 tealSearchBarStyleStyle。 此 Style 设置一些附加属性,并使用 BaseResourceKey 属性引用 searchBarStyle。 不需要 DynamicResource 标记扩展,因为 tealSearchBarStyle 不会更改,但从其中派生的 Style 除外。 因此,tealSearchBarStyle 保留 searchBarStyle 的链接,并在基本样式更改时发生变化。

在代码隐藏文件中,构造函数创建一个包含 searchBarStyle 键的 ResourceDictionary 条目,如前面演示动态样式的示例所示。 执行 OnButtonClicked 事件处理程序时,searchBarStyle 将在 blueSearchBarStylegreenSearchBarStyle 之间切换。 这会导致如以下屏幕截图中所示的外观:

蓝色动态样式继承示例绿色动态样式继承示例

以下代码示例展示了 C# 中的等效页面:

public class DynamicStylesInheritancePageCS : ContentPage
{
    bool originalStyle = true;

    public DynamicStylesInheritancePageCS ()
    {
        ...
        var baseStyle = new Style (typeof(View)) {
            ...
        };
        var blueSearchBarStyle = new Style (typeof(SearchBar)) {
            ...
        };
        var greenSearchBarStyle = new Style (typeof(SearchBar)) {
            ...
        };
        var tealSearchBarStyle = new Style (typeof(SearchBar)) {
            BaseResourceKey = "searchBarStyle",
            ...
        };
        ...
        Resources = new ResourceDictionary ();
        Resources.Add ("blueSearchBarStyle", blueSearchBarStyle);
        Resources.Add ("greenSearchBarStyle", greenSearchBarStyle);
        Resources ["searchBarStyle"] = Resources ["blueSearchBarStyle"];

        Content = new StackLayout {
            Children = {
                new SearchBar { Text = "These SearchBar controls", Style = tealSearchBarStyle },
                ...
            }
        };
    }
    ...
}

tealSearchBarStyle 直接分配到 SearchBar 实例的 Style 属性。 此 Style 设置一些附加属性,并使用 BaseResourceKey 属性引用 searchBarStyle。 此处不需要 SetDynamicResource 方法,因为 tealSearchBarStyle 不会发生更改,但它派生自的 Style 除外。 因此,tealSearchBarStyle 保留 searchBarStyle 的链接,并在基本样式更改时发生变化。

第 9 频道YouTube 上查找更多 Xamarin 视频。