Compartir a través de


Recursos de temas XAML

Los recursos de tema en XAML son un conjunto de recursos que aplican valores diferentes en función del tema del sistema que esté activo. Hay 3 temas que admite el marco XAML: "Light", "Dark" y "HighContrast".

Requisitos previos: en este tema se da por supuesto que has leído referencias a recursos ResourceDictionary y XAML.

Recursos de tema v. recursos estáticos

Hay dos extensiones de marcado XAML que pueden hacer referencia a un recurso XAML desde un diccionario de recursos XAML existente: extensión de marcado {StaticResource} y extensión de marcado {ThemeResource}.

La evaluación de una extensión de marcado {ThemeResource} se produce cuando la aplicación se carga y, posteriormente, cada vez que cambia el tema en tiempo de ejecución. Este suele ser el resultado de que el usuario cambie la configuración del dispositivo o de un cambio mediante programación dentro de la aplicación que modifique su tema actual.

Por el contrario, una extensión de marcado {StaticResource} solo se evalúa cuando la aplicación carga por primera vez el CÓDIGO XAML. No se actualiza. Es similar a buscar y reemplazar en xaml por el valor real en tiempo de ejecución en el inicio de la aplicación.

Recursos de tema en la estructura del diccionario de recursos

Cada recurso de tema forma parte del archivo XAML themeresources.xaml. Con fines de diseño, themeresources.xaml está disponible en la carpeta \(Archivos de programa)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\<SDK version>\Generic desde una instalación del Kit de desarrollo de software (SDK) de Windows. Los diccionarios de recursos de themeresources.xaml también se reproducen en generic.xaml en el mismo directorio.

Windows Runtime no usa estos archivos físicos para la búsqueda en tiempo de ejecución. Por eso están específicamente en una carpeta DesignTime y no se copian en aplicaciones de forma predeterminada. En su lugar, estos diccionarios de recursos existen en memoria como parte del propio Windows Runtime y las referencias de recursos XAML de la aplicación a los recursos de tema (o recursos del sistema) se resuelven allí en tiempo de ejecución.

Directrices para recursos de temas personalizados

Siga estas instrucciones al definir y consumir sus propios recursos de tema personalizados:

Precaución

Si no sigues estas directrices, es posible que veas un comportamiento inesperado relacionado con los temas de la aplicación. Para obtener más información, consulte la sección Solución de problemas de recursos del tema .

La rampa de colores XAML y los pinceles dependientes del tema

El conjunto combinado de colores para los temas "Light", "Dark" y "HighContrast" componen la rampa de colores de Windows en XAML. Tanto si quieres modificar los temas del sistema como aplicar un tema a tus propios elementos XAML, es importante comprender cómo se estructuran los recursos de color.

Para obtener información adicional sobre cómo aplicar color en la aplicación de Windows, consulta Color en aplicaciones de Windows.

Colores de tema claro y oscuro

El marco XAML proporciona un conjunto de recursos de color con nombre con valores que se adaptan a los temas "Claro" y "Oscuro". Para WinUI 2, los recursos del tema se definen en el archivo Xaml de recursos de tema comunes. Los nombres de color son muy descriptivos de su uso previsto y hay un recurso SolidColorBrush correspondiente para cada recurso color.

Sugerencia

Para obtener información general visual sobre estos colores, consulte la aplicación WinUI 3 Gallery: Colors

La aplicación Galería de WinUI 3 incluye ejemplos interactivos de la mayoría de los controles, características y funcionalidades de WinUI 3. Obtención de la aplicación desde Microsoft Store o obtención del código fuente en GitHub

Colores del tema de contraste del sistema Windows

Además del conjunto de recursos proporcionados por el marco XAML, hay un conjunto de valores de color derivados de la paleta del sistema de Windows. Estos colores no son específicos de las aplicaciones de Windows Runtime o Windows. Sin embargo, muchos de los recursos de Pincel XAML consumen estos colores cuando el sistema está funcionando (y la aplicación se está ejecutando) con el tema "HighContrast". El marco XAML proporciona estos colores para todo el sistema como recursos clave. Las claves siguen el formato de nomenclatura: SystemColor[name]Color.

Para obtener más información sobre cómo admitir temas de contraste, vea Temas de contraste.

Color de énfasis del sistema

Además de los colores del tema de contraste del sistema, el color de énfasis del sistema se proporciona como un recurso de color especial mediante la clave SystemAccentColor. En tiempo de ejecución, este recurso obtiene el color que el usuario ha especificado como color de énfasis en la configuración de personalización de Windows.

Nota:

Aunque es posible invalidar los recursos de color del sistema, se recomienda respetar las opciones de color del usuario, especialmente para la configuración del tema de contraste.

Pinceles dependientes del tema

Los recursos de color que se muestran en las secciones anteriores se usan para establecer la propiedad Color de los recursos de SolidColorBrush en los diccionarios de recursos del tema del sistema. Usas los recursos de pincel para aplicar el color a los elementos XAML.

Veamos cómo se determina el valor de color de este pincel en tiempo de ejecución. En los diccionarios de recursos "Light" y "Dark", este pincel se define de la siguiente manera:

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

En el diccionario de recursos "HighContrast", este pincel se define de la siguiente manera:

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

Cuando este pincel se aplica a un elemento XAML, el tema actual determina su color en tiempo de ejecución, como se muestra en esta tabla.

Tema Recurso de color Valor en tiempo de ejecución
Ligero TextFillColorPrimary #E4000000
Oscuro TextFillColorPrimary #FFFFFFFF
HighContrast SystemColorWindowTextColor Color especificado en la configuración de Text.

Rampa de tipos XAML

El archivo themeresources.xaml define varios recursos que definen un estilo que se puede aplicar a los contenedores de texto de la interfaz de usuario, específicamente para TextBlock o RichTextBlock. Estos no son los estilos implícitos predeterminados. Se proporcionan para facilitar la creación de definiciones de interfaz de usuario XAML que coincidan con la rampa de tipos de Windows documentada en Directrices para fuentes.

Estos estilos son para atributos de texto que desea aplicar al contenedor de texto completo. Si desea aplicar estilos solo a secciones del texto, establezca atributos en los elementos de texto del contenedor, como en una ejecución en TextBlock.Inlines o en un párrafo en RichTextBlock.Blocks.

Los estilos tienen este aspecto cuando se aplican a un TextBlock:

estilos de bloque de texto

Estilo Weight Tamaño
Subtítulo Regular 12
Body Regular 14
Cuerpo fuerte Semibold 14
Cuerpo grande Regular 18
Subtítulo Semibold 20
Title Semibold 28
Título grande Semibold 40
Display Semibold 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 obtener instrucciones sobre cómo usar la rampa de tipos de Windows en la aplicación, consulta Tipografía en aplicaciones de Windows.

Para obtener más información sobre los estilos XAML, consulte WinUI en GitHub:

Sugerencia

Para obtener información general visual de estos estilos, consulte la aplicación Galería de WinUI 3: Tipografía.

BaseRichTextBlockStyle

TargetType: RichTextBlock

Proporciona las propiedades comunes para todos los demás estilos de contenedor 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>

Nota: Los estilos RichTextBlock no tienen todos los estilos de rampa de texto que hace TextBlock , principalmente porque el modelo de objetos de documento basado en bloques para RichTextBlock facilita el establecimiento de atributos en los elementos de texto individuales. Además, establecer TextBlock.Text con la propiedad de contenido XAML presenta una situación en la que no hay ningún elemento de texto para aplicar estilo y, por tanto, tendrías que aplicar estilo al contenedor. No es un problema para RichTextBlock porque su contenido de texto siempre tiene que estar en elementos de texto específicos, como Paragraph, que es donde podría aplicar estilos XAML para el encabezado de página, el subpartido de página y definiciones de rampa de texto similares.

Estilos con nombre varios

Hay un conjunto adicional de definiciones de estilo con clave que puede aplicar para aplicar un estilo a Button de forma diferente a su estilo implícito predeterminado.

TargetType: Button

Este estilo proporciona una plantilla completa para un botón que puede ser el botón atrás de navegación de una aplicación de navegación. Las dimensiones predeterminadas son de 40 x 40 píxeles. Para adaptar el estilo, puede establecer explícitamente las propiedades Height, Width, FontSize y otras propiedades del botón o crear un estilo derivado mediante BasedOn.

Este es un botón con el recurso NavigationBackButtonNormalStyle aplicado.

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

Tiene este aspecto:

Un botón con estilo de botón atrás

TargetType: Button

Este estilo proporciona una plantilla completa para un botón que puede ser el botón atrás de navegación de una aplicación de navegación. Es similar a NavigationBackButtonNormalStyle, pero sus dimensiones son de 30 x 30 píxeles.

Este es un botón con el recurso NavigationBackButtonSmallStyle aplicado.

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

Solución de problemas de recursos de temas

Si no sigues las instrucciones para usar recursos de tema, es posible que veas un comportamiento inesperado relacionado con los temas de la aplicación.

Por ejemplo, al abrir un control flotante con temas claros, las partes de la aplicación con temas oscuros también cambian como si estuvieran en el tema claro. O bien, si navega a una página con temas claros y, a continuación, retroceda, la página original con temas oscuros (o partes de ella) ahora se ve como si estuviera en el tema claro.

Normalmente, estos tipos de problemas se producen cuando se proporciona un tema "Predeterminado" y un tema "HighContrast" para admitir escenarios de contraste alto y, a continuación, se usan temas "Claro" y "Oscuro" en diferentes partes de la aplicación.

Por ejemplo, considere esta definición de diccionario 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>

De forma intuitiva, este aspecto es correcto. Quiere cambiar el color al que apunta myBrush cuando está en contraste alto, pero cuando no está en contraste alto, se basa en la extensión de marcado {ThemeResource} para asegurarse de que myBrush apunta al color correcto para el tema. Si la aplicación nunca tiene FrameworkElement.RequestedTheme establecido en elementos dentro de su árbol visual, normalmente funcionará según lo previsto. Sin embargo, experimenta problemas en la aplicación tan pronto como empiece a volver a temar diferentes partes del árbol visual.

El problema se produce porque los pinceles son recursos compartidos, a diferencia de la mayoría de otros tipos XAML. Si tienes dos elementos en subárboles XAML con diferentes temas que hacen referencia al mismo recurso de pincel, a medida que el marco recorre cada subárbol para actualizar sus expresiones de extensión de marcado {ThemeResource} , los cambios en el recurso de pincel compartido se reflejan en el otro subárbol, que no es el resultado previsto.

Para corregirlo, reemplace el diccionario "Default" por diccionarios de temas independientes para temas "Light" y "Dark" además 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>

Sin embargo, se siguen produciendo problemas si se hace referencia a alguno de estos recursos en propiedades heredadas como Primer plano. La plantilla de control personalizada podría especificar el color de primer plano de un elemento mediante la extensión de marcado {ThemeResource}, pero cuando el marco propaga el valor heredado a los elementos secundarios, proporciona una referencia directa al recurso resuelto por la expresión de extensión de marcado {ThemeResource}. Esto provoca problemas cuando el marco procesa los cambios del tema a medida que recorre el árbol visual del control. Vuelve a evaluar la expresión de extensión de marcado {ThemeResource} para obtener un nuevo recurso de pincel, pero aún no propaga esta referencia a los elementos secundarios del control; esto sucede más adelante, como durante el próximo paso de medida.

Como resultado, después de caminar el árbol visual de control en respuesta a un cambio de tema, el marco recorre los elementos secundarios y actualiza las expresiones de extensión de marcado {ThemeResource} establecidas en ellos o en los objetos establecidos en sus propiedades. Aquí es donde se produce el problema; El marco recorre el recurso brush y, dado que especifica su color mediante una extensión de marcado {ThemeResource}, se vuelve a evaluar.

En este momento, el marco parece haber contaminado el diccionario de temas porque ahora tiene un recurso de un diccionario que tiene su color establecido desde otro diccionario.

Para corregir este problema, use la extensión de marcado {StaticResource} en lugar de la extensión de marcado {ThemeResource}. Con las directrices aplicadas, los diccionarios de temas tienen este aspecto:

<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 la extensión de marcado {ThemeResource} todavía se usa en el diccionario "HighContrast" en lugar de la extensión de marcado {StaticResource}. Esta situación se encuentra bajo la excepción dada anteriormente en las instrucciones. La mayoría de los valores de pincel que se usan para el tema "HighContrast" usan opciones de color controladas globalmente por el sistema, pero expuestas a XAML como un recurso con nombre especial (los prefijos con "SystemColor" en el nombre). El sistema permite al usuario establecer los colores específicos que se deben usar para su configuración del tema de contraste a través del Centro de accesibilidad. Esas opciones de color se aplican a los recursos con nombre especial. El marco XAML usa el mismo evento de cambio de tema para actualizar también estos pinceles cuando detecta que han cambiado en el nivel del sistema. Este es el motivo por el que la extensión de marcado {ThemeResource} se usa aquí.