Recursos de temas XAML

Recursos de temas no XAML são um conjunto de recursos que aplicam valores diferentes de acordo com o tema do sistema que estiver ativo. Há 3 temas para os quais a estrutura XAML oferece suporte: "Light", "Dark" e "HighContrast".

Pré-requisitos: este tópico pressupõe que você leu Referências de recursos ResourceDictionary e XAML.

Recursos de tema versus recursos estáticos

Existem duas extensões de marcação XAML que podem referenciar um recurso XAML de um dicionário de recursos XAML existente: a extensão de marcação {StaticResource} e a extensão de marcação {ThemeResource}.

A avaliação de uma extensão de marcação {ThemeResource} ocorre quando o aplicativo é carregado e, posteriormente, toda vez que o tema é alterado em runtime. Normalmente é o resultado da alteração que o usuário faz nas configurações do dispositivo ou de uma alteração programática dentro do aplicativo que altera o tema atual.

Por outro lado, uma extensão de marcação {StaticResource} é avaliada somente quando o XAML é carregado pela primeira vez pelo aplicativo. Ele não é atualizado. Isso é semelhante a localizar e substituir no XAML pelo valor do runtime real na inicialização do aplicativo.

Recursos de tema na estrutura de dicionário de recursos

Cada recurso de tema é parte do arquivo XAML themeresources.xaml. Para fins de design, o arquivo themeresources.xaml está disponível na pasta \(Program Files)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\<versão SDK>\Generic de uma instalação do SDK (Kit de Desenvolvimento de Software) do Windows. Os dicionários de recursos em themeresources.xaml também são reproduzidos em generic.xaml no mesmo diretório.

O Windows Runtime não usa esses arquivos físicos para pesquisa de tempo de execução. É por isso que eles estão especificamente em uma pasta DesignTime e não são copiados para aplicativos por padrão. Em vez disso, esses dicionários de recursos existem na memória como parte do Windows Runtime propriamente dito, e as referências de recurso XAML do aplicativo a recursos de tema (ou recursos do sistema) são resolvidas no runtime.

Diretrizes para recursos de temas personalizados

Siga estas diretrizes ao definir e consumir os seus próprios recursos de tema personalizados:

Cuidado

Caso não siga essas diretrizes, você pode notar um comportamento inesperado relacionado aos temas no aplicativo. Para obter mais informações, consulte a seção Soluçionar problemas de recursos de tema.

A rampa de cores XAML e os pincéis dependentes de temas

O conjunto combinado de cores para temas "Claro", "Escuro" e "HighContrast" formam a rampa de cores do Windows no XAML. Se você deseja modificar os temas do sistema ou aplicar um tema aos seus próprios elementos XAML, é importante entender como os recursos de cores são estruturados.

Para obter informações adicionais sobre como aplicar cor em seu aplicativo do Windows, confira Cor em aplicativos do Windows.

Cores do tema Claro e Escuro

A estrutura XAML fornece um conjunto de recursos nomeado Color com valores personalizados para os temas "Light" e "Dark". Para o WinUI 2, os recursos de tema são definidos no Arquivo Xaml de recursos de tema comuns. Os nomes de cores são muito descritivos de seu uso pretendido e há um recurso SolidColorBrush correspondente para cada Recurso de cores.

Dica

Para obter uma visão geral visual dessas cores, consulte o aplicativo Galeria WinUI 3: Cores

O aplicativo Galeria da WinUI 3 inclui exemplos interativos da maioria dos controles, recursos e funcionalidades da WinUI 3. Obtenha o aplicativo na Microsoft Store ou o código-fonte no GitHub

Cores do tema de contraste do sistema Windows

Além do conjunto de recursos fornecido pela estrutura XAML, há um conjunto de valores de cor derivado da paleta do sistema Windows. Essas cores não são específicas dos aplicativos do Windows ou Windows Runtime. Porém, muitos dos recursos XAML Brush consomem essas cores quando o sistema está em operação (e o aplicativo está em execução) usando o tema "HighContrast". A estrutura XAML fornece essas cores em todo o sistema como recursos chaveados. As chaves seguem o formato de nomenclatura: SystemColor[name]Color.

Para obter mais informações sobre como dar suporte a temas de contraste, consulte Temas de contraste.

Cor de destaque do sistema

Além das cores do tema de alto contraste do sistema, a cor de destaque do sistema é fornecida como um recurso de cores especial usando-se a chave SystemAccentColor. Em runtime, esse recurso obtém a cor que o usuário especificou como a cor de destaque nas configurações de personalização do Windows.

Observação

Embora seja possível substituir os recursos de cores do sistema, é uma melhor prática respeitar as opções de cor do usuário, especialmente para configurações de alto contraste.

Pincéis dependentes de temas

Os recursos de cor mostrados nas seções anteriores são usados para definir a propriedade Color dos recursos SolidColorBrush nos dicionários de recursos de temas do sistema. Use os recursos de pincel para aplicar a cor a elementos XAML.

Vejamos como o valor de cor para esse pincel é determinado em runtime. Nos dicionários de recursos "Claro" e "Escuro", esse pincel é definido desta forma:

<SolidColorBrush x:Key="TextFillColorPrimaryBrush" Color="{StaticResource TextFillColorPrimary}"/>

No dicionário de recursos "HighContrast", esse pincel é definido desta forma:

<SolidColorBrush x:Key="TextFillColorPrimaryBrush" Color="{ThemeResource SystemColorWindowTextColor}"/>

Quando esse pincel é aplicado a um elemento XAML, a cor é determinada em runtime pelo tema atual, conforme mostrado nesta tabela.

Tema Recurso de cor Valor do runtime
Leve TextFillColorPrimary #E4000000
Escuro TextFillColorPrimary #FFFFFFFF
HighContrast SystemColorWindowTextColor A cor especificada nas configurações de Texto.

A rampa de tipos XAML

O arquivo themeresources.xaml define vários recursos que determinam um Style que você pode aplicar a contêineres de texto na interface do usuário, especificamente para TextBlock ou RichTextBlock. Esses não são os estilos implícitos padrão. Eles são fornecidos para facilitar a criação de definições da interface do usuário XAML que correspondam à rampa de tipos do Windows documentada em Diretrizes para fontes.

Esses estilos se destinam a atributos de texto que você deseja aplicar ao contêiner de texto inteiro. Se desejar que o estilos sejam aplicados somente a seções do texto, defina atributos nos elementos de texto dentro do contêiner, como em um Run em TextBlock.Inlines ou em um Paragraph em RichTextBlock.Blocks.

Os estilos têm esta aparência quando aplicados a um TextBlock:

text block styles

Estilo Peso Tamanho
Legenda Regular 12
Corpo Regular 14
Corpo Forte Seminegrito 14
Corpo Grande Regular 18
Subtítulo Seminegrito 20
Título Seminegrito 28
Título Grande Seminegrito 40
Tela Seminegrito 68
<TextBlock Text="Caption" Style="{StaticResource CaptionTextBlockStyle}"/>
<TextBlock Text="Body" Style="{StaticResource BodyTextBlockStyle}"/>
<TextBlock Text="Body Strong" Style="{StaticResource BodyStrongTextBlockStyle}"/>
<TextBlock Text="Body Large" Style="{StaticResource BodyLargeTextBlockStyle}"/>
<TextBlock Text="Subtitle" Style="{StaticResource SubtitleTextBlockStyle}"/>
<TextBlock Text="Title" Style="{StaticResource TitleTextBlockStyle}"/>
<TextBlock Text="Title Large" Style="{StaticResource TitleLargeTextBlockStyle}"/>
<TextBlock Text="Display" Style="{StaticResource DisplayTextBlockStyle}"/>

Para obter diretrizes sobre como usar a rampa de tipos do Windows em seu aplicativo, confira Tipografia em aplicativos do Windows.

Para obter detalhes sobre os estilos XAML, consulte WinUI no GitHub:

Dica

Para obter uma visão geral visual desses estilos, consulte o aplicativo Galeria WinUI 3: Tipografia

BaseRichTextBlockStyle

TargetType: RichTextBlock

Fornece as propriedades comuns para todos os outros estilos de contêiner RichTextBlock.

<!-- Usage -->
<RichTextBlock Style="{StaticResource BaseRichTextBlockStyle}">
    <Paragraph>Rich text.</Paragraph>
</RichTextBlock>

<!-- Style definition -->
<Style x:Key="BaseRichTextBlockStyle" TargetType="RichTextBlock">
    <Setter Property="FontFamily" Value="Segoe UI"/>
    <Setter Property="FontWeight" Value="SemiBold"/>
    <Setter Property="FontSize" Value="14"/>
    <Setter Property="TextTrimming" Value="None"/>
    <Setter Property="TextWrapping" Value="Wrap"/>
    <Setter Property="LineStackingStrategy" Value="MaxHeight"/>
    <Setter Property="TextLineBounds" Value="Full"/>
    <Setter Property="OpticalMarginAlignment" Value="TrimSideBearings"/>
</Style>

BodyRichTextBlockStyle

<!-- Usage -->
<RichTextBlock Style="{StaticResource BodyRichTextBlockStyle}">
    <Paragraph>Rich text.</Paragraph>
</RichTextBlock>

<!-- Style definition -->
<Style x:Key="BodyRichTextBlockStyle" TargetType="RichTextBlock" BasedOn="{StaticResource BaseRichTextBlockStyle}">
    <Setter Property="FontWeight" Value="Normal"/>
</Style>

Observação:  os estilos RichTextBlock não têm todos os estilos de rampa de texto de TextBlock, principalmente porque o modelo de objeto de documento baseado em bloco de RichTextBlock facilita a definição de atributos nos elementos de texto individuais. Além disso, definir TextBlock.Text usando a propriedade de conteúdo XAML provoca uma situação em que não há elementos de texto para os quais definir um estilo e, portanto, você teria que definir um estilo para o contêiner. Isso não é um problema para RichTextBlock, pois o conteúdo de texto sempre precisa estar em elementos de texto específicos, como Paragraph, onde você pode aplicar estilos XAML do cabeçalho de página, do subcabeçalho de página e das definições de rampa de texto semelhantes.

Estilos nomeados diversos

Há um conjunto adicional de definições Style inseridas que você pode aplicar para criar um Button diferente do estilo implícito padrão.

TargetType: Botão

Esse Style oferece um modelo completo para um Button que pode ser o botão voltar de navegação de um aplicativo de navegação. As dimensões padrão são 40 x 40 pixels. Para personalizar o estilo, você pode definir explicitamente Height, Width, FontSize e outras propriedades no Button ou criar um estilo derivado usando BasedOn.

Veja a seguir um Button com o recurso NavigationBackButtonNormalStyle aplicado.

<Button Style="{StaticResource NavigationBackButtonNormalStyle}" />

Ela tem esta aparência:

A button styled as a back button

TargetType: Botão

Esse Style oferece um modelo completo para um Button que pode ser o botão voltar de navegação de um aplicativo de navegação. Ele é semelhante a NavigationBackButtonNormalStyle, mas as dimensões são 30 x 30 pixels.

Veja a seguir um Button com o recurso NavigationBackButtonSmallStyle aplicado.

<Button Style="{StaticResource NavigationBackButtonSmallStyle}" />

Solucionar problemas de recursos de temas

Caso não siga as diretrizes para uso de recursos de tema, você poderá notar um comportamento inesperado relacionado aos temas no aplicativo.

Por exemplo, quando você abrir um submenu com tema claro, partes do aplicativo com tema escuro também mudarão como se estivessem no tema claro. Ou, caso você navegue até uma página com tema claro e, em seguida, volte à navegação original, a página com tema escuro original (ou partes dela) agora parecerá ser do tema claro.

Normalmente, esses tipos de problemas ocorrem quando você fornece um tema "Padrão" e um tema "HighContrast" para dar suporte a cenários de alto contraste e, em seguida, usa ambos os temas "Claro" e "Escuro" em partes diferentes do aplicativo.

Por exemplo, considere esta definição do dicionário de temas:

<!-- DO NOT USE. THIS XAML DEMONSTRATES AN ERROR. -->
<ResourceDictionary>
  <ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Default">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="HighContrast">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
    </ResourceDictionary>
  </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

Intuitivamente, ela parece correta. Você deseja alterar a cor apontada por myBrush quando estiver em alto contraste, mas, quando não estiver em alto contraste, você depende da extensão de marcação {ThemeResource} para verificar se myBrush aponta para a cor certa do tema. Se o aplicativo jamais tiver FrameworkElement.RequestedTheme definido em elementos dentro da árvore visual, isso normalmente funcionará conforme esperado. No entanto, você terá problemas em seu aplicativo assim que começar a remodelar diferentes partes da sua árvore visual.

O problema ocorre porque pincéis são recursos compartilhados, diferentemente da maioria dos outros tipos XAML. Se você tiver dois elementos em subárvores XAML com temas diferentes que fazem referência ao mesmo recurso de pincel, quando a estrutura percorrer cada subárvore para atualizar suas expressões de extensão de marcação {ThemeResource}, as alterações no recurso de pincel compartilhado serão refletidas na outra subárvore, o que não é o resultado pretendido.

Para corrigir isso, substitua o dicionário "Padrão" por dicionários de temas separados para os dois temas "Claro" e "Escuro", além de "HighContrast":

<!-- DO NOT USE. THIS XAML DEMONSTRATES AN ERROR. -->
<ResourceDictionary>
  <ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Light">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="Dark">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="HighContrast">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
    </ResourceDictionary>
  </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

No entanto, ainda haverá problemas se qualquer um desses recursos forem referenciados em propriedades herdadas, como Foreground. O modelo de controle personalizado pode especificar a cor de primeiro plano de um elemento usando a extensão de marcação {ThemeResource}, mas, quando a estrutura propaga o valor herdado para elementos filho, ela fornece uma referência direta para o recurso que foi resolvido pela expressão da extensão de marcação {ThemeResource}. Isso causa problemas quando a estrutura processa alterações de tema à medida que percorre a árvore visual do controle. Ela reavalia a expressão da extensão de marcação {ThemeResource} para obter um novo recurso pincel, mas ainda não propaga essa referência para os filhos do controle; isso acontecerá mais tarde como, por exemplo, durante a próxima passagem de medição.

Dessa forma, depois de percorrer a árvore visual do controle em resposta a uma alteração feita no tema, a estrutura orienta os filhos e atualiza todas as expressões da extensão de marcação {ThemeResource} definidas neles ou em objetos definidos nas propriedades. É onde o problema ocorre; a estrutura percorre o recurso pincel e, como ela especifica a cor usando uma extensão de marcação {ThemeResource}, ela é reavaliada.

A esta altura, a estrutura aparentemente poluiu o dicionário de temas porque agora tem um recurso de um dicionário com a cor definida de outro dicionário.

Para corrigir esse problema, use a extensão de marcação {StaticResource} no lugar da extensão de marcação {ThemeResource}. Com as diretrizes aplicadas, os dicionários de temas terão esta aparência:

<ResourceDictionary>
  <ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Light">
      <SolidColorBrush x:Key="myBrush" Color="{StaticResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="Dark">
      <SolidColorBrush x:Key="myBrush" Color="{StaticResource ControlFillColorDefault}"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="HighContrast">
      <SolidColorBrush x:Key="myBrush" Color="{ThemeResource SystemColorButtonFaceColor}"/>
    </ResourceDictionary>
  </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

Observe que a extensão de marcação {ThemeResource} ainda é usada no dicionário "HighContrast" no lugar da extensão de marcação {StaticResource}. Essa situação se enquadra na exceção apresentada anteriormente nas diretrizes. A maioria dos valores de pincéis usados para o tema "HighContrast" está usando opções de cores controladas globalmente pelo sistema, mas expostas a XAML como um recurso especialmente nomeado (os prefixados com "SystemColor" no nome). O sistema permite que o usuário defina as cores específicas que devem ser usadas nas configurações de alto contraste por meio da Central de Facilidade de Acesso. Essas opções de cores são aplicadas aos recursos especialmente nomeados. A estrutura XAML usa o mesmo evento de tema alterado para também atualizar esses pincéis ao detectar que eles foram alterados no nível do sistema. É por isso que a extensão de marcação {ThemeResource} é usada aqui.