Definir estilo de aplicativos usando XAML

Os aplicativos .NET MAUI (interface do usuário do aplicativo multiplataforma) do .NET geralmente contêm vários controles que têm uma aparência idêntica. Por exemplo, um aplicativo pode ter várias instâncias Label que têm as mesmas opções de fonte e opções de layout:

<Label Text="These labels"
       HorizontalOptions="Center"
       VerticalOptions="Center"
       FontSize="18" />
<Label Text="are not"
       HorizontalOptions="Center"
       VerticalOptions="Center"
       FontSize="18" />
<Label Text="using styles"
       HorizontalOptions="Center"
       VerticalOptions="Center"
       FontSize="18" />

Neste exemplo, cada objeto Label tem valores de propriedade idênticos para controlar a aparência do texto exibido pelo Label. No entanto, definir a aparência de cada controle individual pode ser repetitivo e propenso a erros. Em vez disso, um estilo pode ser criado que define a aparência e, em seguida, aplicado aos controles necessários.

Introdução aos estilos

Um aplicativo pode ser estilizado usando a classe Style para agrupar uma coleção de valores de propriedade em um objeto que pode ser aplicado a vários elementos visuais. Isso ajuda a reduzir a marcação repetitiva e permite que a aparência de um aplicativo seja alterada com mais facilidade.

Embora os estilos sejam projetados principalmente para aplicativos baseados em XAML, eles também podem ser criados em C#:

  • Style objetos criados em XAML normalmente são definidos em um ResourceDictionary atribuído à coleção Resources de um controle, página ou à coleção Resources do aplicativo.
  • Style objetos criados em C# normalmente são definidos na classe da página ou em uma classe que pode ser acessada globalmente.

Escolher em que local definir um Style afeta o local em que ele pode ser usado:

  • Style instâncias definidas no nível do controle só podem ser aplicadas ao controle e a seus filhos.
  • Style instâncias definidas no nível da página só podem ser aplicadas à página e a seus filhos.
  • Style instâncias definidas no nível do aplicativo podem ser aplicadas em todo o aplicativo.

Cada objeto Style contém uma coleção de um ou mais objetos Setter, com cada Setter tendo um Property e um Value. O Property é o nome da propriedade vinculável do elemento ao qual o estilo é aplicado e o Value é o valor aplicado à propriedade.

Cada objeto Style pode ser explícito ou implícito:

  • Um objeto explícito Style é definido especificando um TargetType e um valor x:Key e definindo a propriedade Style do elemento de destino como a referência x:Key. Para obter mais informações, consulte Estilos explícitos.
  • Um objeto implícito Style é definido especificando apenas um TargetType. O objeto Style será aplicado automaticamente a todos os elementos desse tipo. No entanto, as subclasses do TargetType não têm automaticamente o Style aplicado. Para obter mais informações, consulte Estilos implícitos.

Ao criar um Style, a propriedade TargetType é sempre necessária. O exemplo a seguir mostra um estilo explícito:

<Style x:Key="labelStyle" TargetType="Label">
    <Setter Property="HorizontalOptions" Value="Center" />
    <Setter Property="VerticalOptions" Value="Center" />
    <Setter Property="FontSize" Value="18" />
</Style>

Para aplicar um Style, o objeto de destino deve ser um VisualElement que corresponda ao valor da propriedade TargetType do Style:

<Label Text="Demonstrating an explicit style" Style="{StaticResource labelStyle}" />

Os estilos mais baixos na hierarquia de exibição têm precedência sobre aqueles definidos mais acima. Por exemplo, definir um Style que define Label.TextColor como Red no nível do aplicativo será substituído por um estilo no nível da página que define Label.TextColor como Green. Da mesma forma, um estilo de nível de página será substituído por um estilo de nível de controle. Além disso, se Label.TextColor for definido diretamente em uma propriedade de controle, isso terá precedência sobre todos os estilos.

Os estilos não respondem a alterações de propriedade e permanecem inalterados durante a duração de um aplicativo. No entanto, os aplicativos podem responder a alterações de estilo dinamicamente em runtime usando recursos dinâmicos. Para obter mais informações, consulte Estilos dinâmicos.

Estilos explícitos

Para criar uma Style no nível da página, um ResourceDictionary deve ser adicionado à página e, em seguida, uma ou mais declarações de Style podem ser incluídas no ResourceDictionary. Um Style é tornado explícito dando à sua declaração um atributo x:Key, o que lhe dá uma chave descritiva no ResourceDictionary. Os estilos explícitos devem ser aplicados a elementos visuais específicos, definindo suas propriedades Style.

O exemplo a seguir mostra estilos explícitos no ResourceDictionary de uma página e aplicados aos objetos Label da página:

<ContentPage ...>
    <ContentPage.Resources>
        <Style x:Key="labelRedStyle"
               TargetType="Label">
            <Setter Property="HorizontalOptions" Value="Center" />
            <Setter Property="VerticalOptions" Value="Center" />
            <Setter Property="FontSize" Value="18" />
            <Setter Property="TextColor" Value="Red" />
        </Style>
        <Style x:Key="labelGreenStyle"
               TargetType="Label">
            <Setter Property="HorizontalOptions" Value="Center" />
            <Setter Property="VerticalOptions" Value="Center" />
            <Setter Property="FontSize" Value="18" />
            <Setter Property="TextColor" Value="Green" />
        </Style>
        <Style x:Key="labelBlueStyle"
               TargetType="Label">
            <Setter Property="HorizontalOptions" Value="Center" />
            <Setter Property="VerticalOptions" Value="Center" />
            <Setter Property="FontSize" Value="18" />
            <Setter Property="TextColor" Value="Blue" />
        </Style>
    </ContentPage.Resources>
    <StackLayout>
        <Label Text="These labels"
               Style="{StaticResource labelRedStyle}" />
        <Label Text="are demonstrating"
               Style="{StaticResource labelGreenStyle}" />
        <Label Text="explicit styles,"
               Style="{StaticResource labelBlueStyle}" />
        <Label Text="and an explicit style override"
               Style="{StaticResource labelBlueStyle}"
               TextColor="Teal" />
    </StackLayout>
</ContentPage>

Neste exemplo, o ResourceDictionary define três estilos que são definidos explicitamente nos objetos Label da página. Cada Style é usado para exibir o texto em uma cor diferente, ao mesmo tempo em que define o tamanho da fonte e as opções de layout horizontal e vertical. Cada Style é aplicado a um Label diferente definindo suas propriedades Style usando a extensão de marcação StaticResource. Além disso, embora a Label final tenha um Style definido, ela também substitui a propriedade TextColor para um valor de Color diferente.

Estilos implícitos

Para criar uma Style no nível da página, um ResourceDictionary deve ser adicionado à página e, em seguida, uma ou mais declarações de Style podem ser incluídas no ResourceDictionary. Um Style é feito implícito não especificando um atributo x:Key. Em seguida, o estilo será aplicado a elementos visuais no escopo que correspondem exatamente ao TargetType, mas não a elementos derivados do valor TargetType.

O exemplo de código a seguir mostra um estilo implícito no ResourceDictionary de uma página e aplicado aos objetos Entry da página:

<ContentPage ...>
    <ContentPage.Resources>
        <Style TargetType="Entry">
            <Setter Property="HorizontalOptions" Value="Fill" />
            <Setter Property="VerticalOptions" Value="Center" />
            <Setter Property="BackgroundColor" Value="Yellow" />
            <Setter Property="FontAttributes" Value="Italic" />
            <Setter Property="TextColor" Value="Blue" />
        </Style>
    </ContentPage.Resources>
    <StackLayout>
        <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>

Neste exemplo, o ResourceDictionary define um único estilo implícito que é definido implicitamente nos objetos Entry da página. O Style é usado para exibir texto azul em um fundo amarelo, ao mesmo tempo em que define outras opções de aparência. O Style é adicionado ao ResourceDictionary da página sem especificar um atributo x:Key. Portanto, o Style é aplicado a todos os objetos Entry implicitamente, pois eles correspondem exatamente à propriedade TargetType do Style. No entanto, o Style não é aplicado ao objeto CustomEntry, que é uma Entry de subclasse. Além disso, a quarta Entry substitui as propriedades BackgroundColor e TextColor do estilo para valores Color diferentes.

Aplicar um estilo a tipos derivados

A propriedade Style.ApplyToDerivedTypes permite que um estilo seja aplicado a controles derivados do tipo base referenciado pela propriedade TargetType. Portanto, definir essa propriedade como true permite que um único estilo tenha como destino vários tipos, desde que os tipos derivem do tipo base especificado na propriedade TargetType.

O exemplo a seguir mostra um estilo implícito que define a cor de fundo de Button instâncias como vermelho:

<Style TargetType="Button"
       ApplyToDerivedTypes="True">
    <Setter Property="BackgroundColor"
            Value="Red" />
</Style>

Colocar esse estilo em um ResourceDictionary de nível de página resultará na aplicação dele a todos os objetos Button na página e também a todos os controles derivados de Button. No entanto, se a propriedade ApplyToDerivedTypes permanecesse não definida, o estilo só seria aplicado a objetos Button.

Estilos globais

Os estilos podem ser definidos globalmente adicionando-os ao dicionário de recursos do aplicativo. Esses estilos podem ser consumidos em todo um aplicativo e ajudam a evitar a duplicação de estilo entre páginas e controles.

O exemplo a seguir mostra uma Style definida no nível do aplicativo:


<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:Styles"
             x:Class="Styles.App">
    <Application.Resources>        
        <Style x:Key="buttonStyle" TargetType="Button">
            <Setter Property="HorizontalOptions"
                        Value="Center" />
            <Setter Property="VerticalOptions"
                        Value="CenterAndExpand" />
            <Setter Property="BorderColor"
                        Value="Lime" />
            <Setter Property="CornerRadius"
                        Value="5" />
            <Setter Property="BorderWidth"
                        Value="5" />
            <Setter Property="WidthRequest"
                        Value="200" />
            <Setter Property="TextColor"
                        Value="Teal" />
        </Style>
    </Application.Resources>
</Application>

Neste exemplo, o ResourceDictionary define um único estilo explícito, buttonStyle, que será usado para definir a aparência de Button objetos.

Observação

Os estilos globais podem ser explícitos ou implícitos.

O exemplo a seguir mostra uma página consumindo o buttonStyle nos objetos Button da página:

<ContentPage ...>
    <StackLayout>
        <Button Text="These buttons"
                Style="{StaticResource buttonStyle}" />
        <Button Text="are demonstrating"
                Style="{StaticResource buttonStyle}" />
        <Button Text="application styles"
                Style="{StaticResource buttonStyle}" />
    </StackLayout>
</ContentPage>

Herança de estilo

Os estilos podem herdar de outros estilos para reduzir a duplicação e permitir a reutilização. Isso é obtido definindo a propriedade Style.BasedOn como uma Style existente. Em XAML, isso pode ser feito definindo a propriedade BasedOn como uma extensão de marcação StaticResource que faz referência a um Style criado anteriormente.

Os estilos que herdam de um estilo base podem incluir instâncias Setter para novas propriedades ou usá-las para substituir setters do estilo base. Além disso, os estilos que herdam de um estilo base devem ter como destino o mesmo tipo ou um tipo que deriva do tipo direcionado pelo estilo base. Por exemplo, se um estilo base for direcionado a objetos View, os estilos baseados no estilo base poderão ser direcionados a objetos View ou tipos derivados da classe View, como objetos Label e Button.

Um estilo só pode herdar de estilos no mesmo nível ou acima na hierarquia de exibição. Isso significa que:

  • Um estilo no nível do aplicativo só pode herdar de outros estilos no nível do aplicativo.
  • Um estilo de nível de página pode herdar de estilos de nível de aplicativo e outros estilos de nível de página.
  • Um estilo de nível de controle pode herdar de estilos de nível de aplicativo, estilos de nível de página e outros estilos de nível de controle.

O exemplo a seguir mostra a herança de estilo explícito:

<ContentPage ...>
    <ContentPage.Resources>
        <Style x:Key="baseStyle"
               TargetType="View">
            <Setter Property="HorizontalOptions" Value="Center" />
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>
    </ContentPage.Resources>
    <StackLayout>
        <StackLayout.Resources>
            <Style x:Key="labelStyle"
                   TargetType="Label"
                   BasedOn="{StaticResource baseStyle}">
                <Setter Property="FontSize" Value="18" />
                <Setter Property="FontAttributes" Value="Italic" />
                <Setter Property="TextColor" Value="Teal" />
            </Style>
            <Style x:Key="buttonStyle"
                   TargetType="Button"
                   BasedOn="{StaticResource baseStyle}">
                <Setter Property="BorderColor" Value="Lime" />
                <Setter Property="CornerRadius" Value="5" />
                <Setter Property="BorderWidth" Value="5" />
                <Setter Property="WidthRequest" Value="200" />
                <Setter Property="TextColor" Value="Teal" />
            </Style>
        </StackLayout.Resources>
        <Label Text="This label uses style inheritance"
               Style="{StaticResource labelStyle}" />
        <Button Text="This button uses style inheritance"
                Style="{StaticResource buttonStyle}" />
    </StackLayout>
</ContentPage>

Neste exemplo, o baseStyle tem como destino View objetos e define as propriedades HorizontalOptions e VerticalOptions. O baseStyle não é definido diretamente em nenhum controle. Em vez disso, labelStyle e buttonStyle herdam dele, definindo valores de propriedade associáveis adicionais. Os objetos labelStyle e buttonStyle são definidos em um Label e Button.

Importante

Um estilo implícito pode ser derivado de um estilo explícito, mas um estilo explícito não pode ser derivado de um estilo implícito.

Estilos dinâmicos

Os estilos não respondem a alterações de propriedade e permanecem inalterados durante a duração de um aplicativo. Por exemplo, depois de atribuir um Style a um elemento visual, se um dos objetos Setter for modificado, removido ou um novo Setter adicionado, as alterações não serão aplicadas ao elemento visual. No entanto, os aplicativos podem responder a alterações de estilo dinamicamente em runtime usando recursos dinâmicos.

A extensão de marcação DynamicResource é semelhante à extensão de marcação StaticResource, pois ambas usam uma chave de dicionário para buscar um valor de um ResourceDictionary. No entanto, enquanto o StaticResource executa uma única pesquisa de dicionário, o DynamicResource mantém um link para a chave do dicionário. Portanto, se a entrada do dicionário associada à chave for substituída, a alteração será aplicada ao elemento visual. Isso permite que alterações de estilo de tempo de execução sejam feitas em um aplicativo.

O exemplo a seguir mostra estilos dinâmicos:

<ContentPage ...>
    <ContentPage.Resources>
        <Style x:Key="baseStyle"
               TargetType="View">
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>
        <Style x:Key="blueSearchBarStyle"
               TargetType="SearchBar"
               BasedOn="{StaticResource baseStyle}">
            <Setter Property="FontAttributes" Value="Italic" />
            <Setter Property="PlaceholderColor" Value="Blue" />
        </Style>
        <Style x:Key="greenSearchBarStyle"
               TargetType="SearchBar">
            <Setter Property="FontAttributes" Value="None" />
            <Setter Property="PlaceholderColor" Value="Green" />
        </Style>
    </ContentPage.Resources>
    <StackLayout>
        <SearchBar Placeholder="SearchBar demonstrating dynamic styles"
                   Style="{DynamicResource blueSearchBarStyle}" />
    </StackLayout>
</ContentPage>

Neste exemplo, o objeto SearchBar usa a extensão de marcação DynamicResource para definir um Style chamado blueSearchBarStyle. O SearchBar pode então ter sua definição de Style atualizada no código:

Resources["blueSearchBarStyle"] = Resources["greenSearchBarStyle"];

Neste exemplo, a definição de blueSearchBarStyle é atualizada para usar os valores da definição greenSearchBarStyle. Quando esse código for executado, o SearchBar será atualizado para usar os objetos Setter definidos em greenSearchBarStyle.

Herança de estilo dinâmico

A derivação de um estilo de um estilo dinâmico não pode ser obtida usando a propriedade Style.BasedOn. Em vez disso, a classe Style inclui a propriedade BaseResourceKey, que pode ser definida como uma chave de dicionário cujo valor pode ser alterado dinamicamente.

O exemplo a seguir mostra a herança de estilo dinâmico:

<ContentPage ...>
    <ContentPage.Resources>
        <Style x:Key="baseStyle"
               TargetType="View">
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>
        <Style x:Key="blueSearchBarStyle"
               TargetType="SearchBar"
               BasedOn="{StaticResource baseStyle}">
            <Setter Property="FontAttributes" Value="Italic" />
            <Setter Property="TextColor" Value="Blue" />
        </Style>
        <Style x:Key="greenSearchBarStyle"
               TargetType="SearchBar">
            <Setter Property="FontAttributes" Value="None" />
            <Setter Property="TextColor" Value="Green" />
        </Style>
        <Style x:Key="tealSearchBarStyle"
               TargetType="SearchBar"
               BaseResourceKey="blueSearchBarStyle">
            <Setter Property="BackgroundColor" Value="Teal" />
            <Setter Property="CancelButtonColor" Value="White" />
        </Style>
    </ContentPage.Resources>
    <StackLayout>
        <SearchBar Text="SearchBar demonstrating dynamic style inheritance"
                   Style="{StaticResource tealSearchBarStyle}" />
    </StackLayout>
</ContentPage>

Neste exemplo, o objeto SearchBar usa a extensão de marcação StaticResource para fazer referência a um Style chamado tealSearchBarStyle. Este Style define algumas propriedades adicionais e usa a propriedade BaseResourceKey para fazer referência a blueSearchBarStyle. A extensão de marcação DynamicResource não é necessária porque tealSearchBarStyle não será alterada, exceto para a Style da qual deriva. Portanto, tealSearchBarStyle mantém um link para blueSearchBarStyle e é atualizado quando o estilo base é alterado.

A definição blueSearchBarStyle pode ser atualizada no código:

Resources["blueSearchBarStyle"] = Resources["greenSearchBarStyle"];

Neste exemplo, a definição de blueSearchBarStyle é atualizada para usar os valores da definição greenSearchBarStyle. Quando esse código for executado, o SearchBar será atualizado para usar os objetos Setter definidos em greenSearchBarStyle.

Classes de estilo

As classes de estilo permitem que vários estilos sejam aplicados a um controle, sem recorrer à herança de estilo.

Uma classe de estilo pode ser criada definindo a propriedade Class em um Style para um string que representa o nome da classe. A vantagem que isso oferece, em relação à definição de um estilo explícito usando o atributo x:Key, é que várias classes de estilo podem ser aplicadas a um VisualElement.

Importante

Vários estilos podem compartilhar o mesmo nome de classe, desde que sejam direcionados a tipos diferentes. Isso permite que várias classes de estilo, com nomes idênticos, sejam direcionadas a tipos diferentes.

O exemplo a seguir mostra três classes de estilo BoxView e uma classe de estilo 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>

Neste exemplo, as classes de estilo Separator, Rounded e Circle definem propriedades BoxView para valores específicos. A classe de estilo Rotated tem um TargetType de VisualElement, o que significa que ela só pode ser aplicada a instâncias VisualElement. No entanto, sua propriedade ApplyToDerivedTypes é definida como true, o que garante que ela possa ser aplicada a todos os controles derivados de VisualElement, como BoxView. Para obter mais informações sobre como aplicar um estilo a um tipo derivado, consulte Aplicar um estilo a tipos derivados.

As classes de estilo podem ser consumidas definindo a propriedade StyleClass do controle, que é do tipo IList<string>, como uma lista de nomes de classe de estilo. As classes de estilo serão aplicadas, desde que o tipo de controle corresponda ao TargetType das classes de estilo.

O exemplo a seguir mostra três instâncias BoxView, cada uma definida com classes de estilo diferentes:

<ContentPage ...>
    <ContentPage.Resources>
        ...
    </ContentPage.Resources>
    <StackLayout>
        <BoxView StyleClass="Separator" />       
        <BoxView WidthRequest="100"
                 HeightRequest="100"
                 HorizontalOptions="Center"
                 StyleClass="Rounded, Rotated" />
        <BoxView HorizontalOptions="Center"
                 StyleClass="Circle" />
    </StackLayout>
</ContentPage>    

Neste exemplo, o primeiro BoxView é estilizado para ser um separador de linha, enquanto o terceiro BoxView é circular. O segundo BoxView tem duas classes de estilo aplicadas a ele, que lhe dão cantos arredondados e o giram 45 graus:

Captura de tela de BoxViews estilizado com classes de estilo.

Importante

Várias classes de estilo podem ser aplicadas a um controle porque a propriedade StyleClass é do tipo IList<string>. Quando isso ocorre, as classes de estilo são aplicadas em ordem crescente de lista. Portanto, quando várias classes de estilo definem propriedades idênticas, a propriedade na classe de estilo que está na posição de lista mais alta terá precedência.