แชร์ผ่าน


Dynamic Styles in Xamarin.Forms

Styles do not respond to property changes, and remain unchanged for the duration of an application. For example, after assigning a Style to a visual element, if one of the Setter instances is modified, removed, or a new Setter instance added, the changes won't be applied to the visual element. However, applications can respond to style changes dynamically at runtime by using dynamic resources.

The DynamicResource markup extension is similar to the StaticResource markup extension in that both use a dictionary key to fetch a value from a ResourceDictionary. However, while the StaticResource performs a single dictionary lookup, the DynamicResource maintains a link to the dictionary key. Therefore, if the dictionary entry associated with the key is replaced, the change is applied to the visual element. This enables runtime style changes to be made in an application.

The following code example demonstrates dynamic styles in a XAML page:

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

The SearchBar instances use the DynamicResource markup extension to reference a Style named searchBarStyle, which is not defined in the XAML. However, because the Style properties of the SearchBar instances are set using a DynamicResource, the missing dictionary key doesn't result in an exception being thrown.

Instead, in the code-behind file, the constructor creates a ResourceDictionary entry with the key searchBarStyle, as shown in the following code example:

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

When the OnButtonClicked event handler is executed, searchBarStyle will switch between blueSearchBarStyle and greenSearchBarStyle. This results in the appearance shown in the following screenshots:

Blue Dynamic Style Example Green Dynamic Style Example

The following code example demonstrates the equivalent page in 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    }
        };
    }
    ...
}

In C#, the SearchBar instances use the SetDynamicResource method to reference searchBarStyle. The OnButtonClicked event handler code is identical to the XAML example, and when executed, searchBarStyle will switch between blueSearchBarStyle and greenSearchBarStyle.

Dynamic style inheritance

Deriving a style from a dynamic style can't be achieved using the Style.BasedOn property. Instead, the Style class includes the BaseResourceKey property, which can be set to a dictionary key whose value might dynamically change.

The following code example demonstrates dynamic style inheritance in a XAML page:

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

The SearchBar instances use the StaticResource markup extension to reference a Style named tealSearchBarStyle. This Style sets some additional properties and uses the BaseResourceKey property to reference searchBarStyle. The DynamicResource markup extension is not required because tealSearchBarStyle will not change, except for the Style it derives from. Therefore, tealSearchBarStyle maintains a link to searchBarStyle and is altered when the base style changes.

In the code-behind file, the constructor creates a ResourceDictionary entry with the key searchBarStyle, as per the previous example that demonstrated dynamic styles. When the OnButtonClicked event handler is executed, searchBarStyle will switch between blueSearchBarStyle and greenSearchBarStyle. This results in the appearance shown in the following screenshots:

Blue Dynamic Style Inheritance Example Green Dynamic Style Inheritance Example

The following code example demonstrates the equivalent page in 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 },
                ...
            }
        };
    }
    ...
}

The tealSearchBarStyle is assigned directly to the Style property of the SearchBar instances. This Style sets some additional properties, and uses the BaseResourceKey property to reference searchBarStyle. The SetDynamicResource method isn't required here because tealSearchBarStyle will not change, except for the Style it derives from. Therefore, tealSearchBarStyle maintains a link to searchBarStyle and is altered when the base style changes.

Find more Xamarin videos on Channel 9 and YouTube.