Estilizar aplicativos usando XAML

Os aplicativos .NET Multi-platform App UI (.NET MAUI) geralmente contêm vários controles que têm uma aparência idêntica. Por exemplo, um aplicativo pode ter várias Label instâncias 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 Label objeto 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 para agrupar uma coleção de valores de propriedade em um objeto que pode ser aplicado a Style vários elementos visuais. Isso ajuda a reduzir a marcação repetitiva e permite que a aparência de um aplicativo seja alterada mais facilmente.

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

  • Style objetos criados em XAML são normalmente definidos em um que é atribuído à coleção de um ResourceDictionary controle, página ou à ResourcesResources coleção do aplicativo.
  • Style objetos criados em C# são normalmente 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 As instâncias definidas no nível de controle só podem ser aplicadas ao controle e a seus filhos.
  • Style As instâncias definidas no nível da página só podem ser aplicadas à página e a seus filhos.
  • Style As instâncias definidas no nível do aplicativo podem ser aplicadas em todo o aplicativo.

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

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

  • Um objeto explícitoStyle é definido especificando um e um TargetTypex:Key valor e definindo a propriedade do elemento de Style destino para a x:Key referência. Para obter mais informações, consulte Estilos explícitos.
  • Um objeto implícitoStyle é definido especificando apenas um TargetTypearquivo . O Style objeto será então 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 , o objeto de destino deve ser um VisualElementStyleque corresponda ao TargetType valor da propriedade do Style:

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

Os estilos inferiores na hierarquia de exibição têm precedência sobre os definidos acima. Por exemplo, a definição de um que define como no nível do aplicativo será substituída por um estilo de nível de Style página que define Label.TextColorLabel.TextColor como GreenRed . 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 quaisquer 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 tempo de execução usando recursos dinâmicos. Para obter mais informações, consulte Estilos dinâmicos.

Estilos explícitos

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

O exemplo a seguir mostra estilos explícitosResourceDictionaryno , de uma página e aplicados aos objetos da Label 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 explicitamente definidos nos objetos da Label página. Cada um Style é usado para exibir texto em uma cor diferente, além de definir o tamanho da fonte e as opções de layout horizontal e vertical. Cada Style um é aplicado a um diferente Label definindo suas Style propriedades usando a StaticResource extensão de marcação. Além disso, embora o final Label tenha um conjunto, ele também substitui a TextColor propriedade por um Style valor diferenteColor.

Estilos implícitos

Para criar um no nível da página, um StyleResourceDictionary deve ser adicionado à página e, em seguida, uma ou mais Style declarações podem ser incluídas no ResourceDictionary. A Style torna-se implícito por não especificar um x:Key atributo. O estilo será então aplicado a elementos visuais no escopo que correspondem exatamente TargetType , mas não a elementos derivados do TargetType valor.

O exemplo de código a seguir mostra um estilo implícito no , de ResourceDictionaryuma página e aplicado aos objetos da Entry 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 são definidos implicitamente nos objetos da Entry página. O Style é usado para exibir texto azul em um plano de fundo amarelo, enquanto também define outras opções de aparência. O Style é adicionado à página sem ResourceDictionary especificar um x:Key atributo. Portanto, o Style é aplicado a todos os Entry objetos implicitamente, pois eles correspondem à TargetType propriedade do Style exatamente. No entanto, o Style não é aplicado ao CustomEntry objeto, que é uma subclasse Entry. Além disso, o quarto Entry substitui as BackgroundColor propriedades e TextColor o estilo para valores diferentes Color .

Aplicar um estilo a tipos derivados

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

O exemplo a seguir mostra um estilo implícito que define a cor do plano de fundo das Button ocorrências como vermelho:

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

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

Estilos globais

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

O exemplo a seguir mostra um Style definido 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 , , buttonStyleque será usado para definir a aparência dos Button objetos.

Observação

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

O exemplo a seguir mostra uma página consumindo os buttonStyle objetos da Button 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 é feito definindo a Style.BasedOn propriedade como um Stylearquivo . Em XAML, isso pode ser alcançado definindo a propriedade como uma StaticResource extensão de marcação que faz referência a BasedOn um Stylearquivo .

Os estilos que herdam de um estilo base podem incluir Setter instâncias para novas propriedades ou usá-los 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 derivado do tipo direcionado pelo estilo base. Por exemplo, se um estilo base tem como alvo View objetos, os estilos baseados no estilo base podem direcionar View objetos ou tipos derivados da View classe, como Label e Button objetos.

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

  • Um estilo de nível de aplicativo só pode herdar de outros estilos de nível de 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ícita :

<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, os baseStyle objetos targets View e define as HorizontalOptions propriedades e VerticalOptions . O baseStyle não é definido diretamente em nenhum controle. Em vez disso, e buttonStyle herdar dele, labelStyle definindo valores de propriedade vinculáveis adicionais. Os labelStyle objetos e são então definidos em um Label e ButtonbuttonStyle .

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 a um elemento visual, se um dos Setter objetos for modificado, removido ou um Style 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 tempo de execução usando recursos dinâmicos.

A DynamicResource extensão de marcação é semelhante à StaticResource extensão de marcação em que ambos usam uma chave de dicionário para buscar um valor de um ResourceDictionaryarquivo . No entanto, enquanto o executa uma única pesquisa de dicionário, o StaticResourceDynamicResource mantém um link para a chave de dicionário. Portanto, se a entrada de 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 SearchBar objeto usa a DynamicResource extensão de marcação para definir um Style nome blueSearchBarStyle. O SearchBar pode então ter sua Style definição atualizada em código:

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

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

Herança de estilo dinâmico

Derivar um estilo de um estilo dinâmico não pode ser obtido usando a Style.BasedOn propriedade. Em vez disso, a classe inclui a StyleBaseResourceKey propriedade, 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 SearchBar objeto usa a extensão de marcação para fazer referência a StaticResource um Style nome tealSearchBarStyle. Isso Style define algumas propriedades adicionais e usa a BaseResourceKey propriedade para fazer referência blueSearchBarStylea . A DynamicResource extensão de marcação não é necessária porque tealSearchBarStyle não será alterada, exceto para o Style que deriva. Portanto, tealSearchBarStyle mantém um link para blueSearchBarStyle e é atualizado quando o estilo base é alterado.

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

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

Neste exemplo, a blueSearchBarStyle definição é atualizada para usar os valores da greenSearchBarStyle definição. Quando esse código é executado, o SearchBar será atualizado para usar os Setter objetos definidos no 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 Class propriedade em um para um stringStyle que representa o nome da classe. A vantagem que isso oferece, em relação à definição de um estilo explícito usando o x:Key atributo, é que várias classes de estilo podem ser aplicadas a um VisualElementarquivo .

Importante

Vários estilos podem compartilhar o mesmo nome de classe, desde que tenham como alvo tipos diferentes. Isso permite que várias classes de estilo, que são nomeadas de forma idêntica, direcionem tipos diferentes.

O exemplo a seguir mostra três BoxView classes de estilo e uma VisualElement classe de estilo:

<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 Separatorclasses , Roundede Circle style definem BoxView propriedades para valores específicos. A Rotated classe style tem um TargetType de VisualElement, o que significa que só pode ser aplicada a VisualElement instâncias. No entanto, sua ApplyToDerivedTypes propriedade é definida como , o que garante que ela possa ser aplicada a quaisquer controles derivados do VisualElement, como trueBoxView. 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 StyleClass propriedade do controle, que é do tipo IList<string>, para uma lista de nomes de classe de estilo. As classes de estilo serão aplicadas, desde que o tipo do controle corresponda ao TargetType das classes de estilo.

O exemplo a seguir mostra três BoxView instâncias, cada uma definida para 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 como 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:

Screenshot of BoxViews styled with style classes.

Importante

Várias classes de estilo podem ser aplicadas a um controle porque a StyleClass propriedade é do tipo IList<string>. Quando isso ocorre, as classes de estilo são aplicadas em ordem de lista crescente. 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.