Estilos XAML

Puedes personalizar la apariencia de tus aplicaciones de muchas maneras mediante el marco XAML. Los estilos permiten establecer propiedades de control y reutilizar esas opciones para una apariencia coherente en varios controles.

Conceptos básicos de estilo

Utiliza estilos para extraer configuraciones de propiedades visuales y convertirlas en recursos reutilizables. Este es un ejemplo que muestra 3 botones con un estilo que establece las propiedades de BorderBrush, BorderThickness y foreground. Al aplicar un estilo, puede hacer que los controles aparezcan iguales sin tener que establecer estas propiedades en cada control por separado.

Captura de pantalla de tres botones con estilo dispuestos uno al lado del otro.

Puedes definir un estilo insertado en el XAML para un control o como un recurso reutilizable. Defina recursos en el archivo XAML de una página individual, en el archivo App.xaml o en un archivo XAML de diccionario de recursos independiente. Un archivo XAML de diccionario de recursos se puede compartir entre aplicaciones y se puede combinar más de un diccionario de recursos en una sola aplicación. Cuando se define el recurso, determina el ámbito en el que se puede usar. Los recursos de nivel de página solo están disponibles en la página donde se definen. Si los recursos con la misma clave se definen en App.xaml y en una página, el recurso de la página invalida el recurso en App.xaml. Si un recurso se define en un archivo de diccionario de recursos independiente, su ámbito viene determinado por dónde se hace referencia al diccionario de recursos.

En la definición de Style, necesitas un atributo TargetType y una colección de uno o más elementos Setter. El atributo TargetType es una cadena que especifica un tipo de FrameworkElement al que se aplicará el estilo. El valor TargetType debe especificar un tipo derivado de FrameworkElementdefinido por Windows Runtime o un tipo personalizado disponible en un ensamblado al que se hace referencia. Si intenta aplicar un estilo a un control y el tipo del control no coincide con el TargetType atributo del estilo que intenta aplicar, se produce una excepción.

Cada elemento Setter requiere una propiedad Property y una propiedad Value. Esta configuración de propiedad indica a qué propiedad de control se aplica la configuración y el valor que se va a establecer para esa propiedad. Puede establecer Setter.Value con sintaxis de elemento de atributo o propiedad. El XAML aquí muestra el estilo aplicado a los botones mostrados anteriormente. En este XAML, los dos primeros elementos Setter usan sintaxis de atributo, pero el último Setter, para la propiedad BorderBrush, utiliza sintaxis de elemento de propiedad. En el ejemplo no se usa el atributo atributo x:Key, por lo que el estilo se aplica implícitamente a los botones. La aplicación de estilos implícita o explícitamente se explica en la sección siguiente.

<Page.Resources>
    <Style TargetType="Button">
        <Setter Property="BorderThickness" Value="5" />
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="BorderBrush" >
            <Setter.Value>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Color="Yellow" Offset="0.0" />
                    <GradientStop Color="Red" Offset="0.25" />
                    <GradientStop Color="Blue" Offset="0.75" />
                    <GradientStop Color="LimeGreen" Offset="1.0" />
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
    </Style>
</Page.Resources>

<StackPanel Orientation="Horizontal">
    <Button Content="Button"/>
    <Button Content="Button"/>
    <Button Content="Button"/>
</StackPanel>

Aplicar un estilo implícito o explícito

Si define un estilo como recurso, hay dos maneras de aplicarlo a los controles:

Si un estilo contiene el atributo x:Key, solo puede aplicarlo a un control estableciendo la propiedad Style del control en el estilo con clave. Por el contrario, un estilo sin un atributo x:Key se aplica automáticamente a todos los controles de su tipo de destino, que de lo contrario no tiene una configuración de estilo explícita.

Estos son dos botones que muestran estilos implícitos y explícitos.

botones de estilo implícito y explícito.

En este ejemplo, el primer estilo tiene un atributo x:Key y su tipo de destino es un botón Button. La propiedad Style del primer botón se establece en esta clave, por lo que este estilo se aplica de manera explícita. El segundo estilo se aplica implícitamente al segundo botón porque su tipo de destino es Button y el estilo no tiene un atributo x:Key.

<Page.Resources>
    <Style x:Key="PurpleStyle" TargetType="Button">
        <Setter Property="FontFamily" Value="Segoe UI"/>
        <Setter Property="FontSize" Value="14"/>
        <Setter Property="Foreground" Value="Purple"/>
    </Style>

    <Style TargetType="Button">
        <Setter Property="FontFamily" Value="Segoe UI"/>
        <Setter Property="FontSize" Value="14"/>
        <Setter Property="RenderTransform">
            <Setter.Value>
                <RotateTransform Angle="25"/>
            </Setter.Value>
        </Setter>
        <Setter Property="BorderBrush" Value="Green"/>
        <Setter Property="BorderThickness" Value="2"/>
        <Setter Property="Foreground" Value="Green"/>
    </Style>
</Page.Resources>

<Grid x:Name="LayoutRoot">
    <Button Content="Button" Style="{StaticResource PurpleStyle}"/>
    <Button Content="Button"/>
</Grid>

Uso en función de los estilos

Para que los estilos sean más fáciles de mantener y optimizar la reutilización del estilo, puede crear estilos que hereden de otros estilos. Usa la propiedad BasedOn para crear estilos heredados. Los estilos que heredan de otros estilos deben tener como destino el mismo tipo de control o un control que derive del tipo destinado al estilo base. Por ejemplo, si un estilo base tiene como destino ContentControl, los estilos basados en este estilo pueden tener como destino ContentControl o tipos que derivan de ContentControl como Button y ScrollViewer. Si un valor no se establece en el estilo sobre el que se basa, se hereda del estilo base. Para cambiar un valor del estilo base, el estilo en el que se basa en invalida ese valor. En el siguiente ejemplo se muestra un Button y un CheckBox con estilos que heredan del mismo estilo base.

botones con estilo en función del estilo.

El estilo base tiene como destino contentControly establece las propiedadesHeight y Width. Los estilos basados en este estilo apuntan a CheckBox y Button, que derivan de ContentControl. Los estilos heredados establecen distintos colores para las propiedades BorderBrush y Foreground. (Típicamente no se coloca un borde en un CheckBox. Lo hacemos aquí para mostrar los efectos del estilo.)

<Page.Resources>
    <Style x:Key="BasicStyle" TargetType="ContentControl">
        <Setter Property="Width" Value="130" />
        <Setter Property="Height" Value="30" />
    </Style>

    <Style x:Key="ButtonStyle" TargetType="Button"
           BasedOn="{StaticResource BasicStyle}">
        <Setter Property="BorderBrush" Value="Orange" />
        <Setter Property="BorderThickness" Value="2" />
        <Setter Property="Foreground" Value="Red" />
    </Style>

    <Style x:Key="CheckBoxStyle" TargetType="CheckBox"
           BasedOn="{StaticResource BasicStyle}">
        <Setter Property="BorderBrush" Value="Blue" />
        <Setter Property="BorderThickness" Value="2" />
        <Setter Property="Foreground" Value="Green" />
    </Style>
</Page.Resources>

<StackPanel>
    <Button Content="Button" Style="{StaticResource ButtonStyle}" Margin="0,10"/>
    <CheckBox Content="CheckBox" Style="{StaticResource CheckBoxStyle}"/>
</StackPanel>

Estilo ligero

Por norma general, los pinceles del sistema se reemplazan en el nivel de página o aplicación y, en cualquier caso, la sustitución del color afectará a todos los controles que hacen referencia a ese pincel. Además, en XAML, muchos controles pueden hacer referencia al mismo pincel del sistema.

Captura de pantalla de dos botones: uno en su estado de reposo y otro con estilo ligero aplicado.

<Page.Resources>
    <ResourceDictionary>
        <ResourceDictionary.ThemeDictionaries>
            <ResourceDictionary x:Key="Light">
                 <SolidColorBrush x:Key="ButtonBackground" Color="Transparent"/>
                 <SolidColorBrush x:Key="ButtonForeground" Color="MediumSlateBlue"/>
                 <SolidColorBrush x:Key="ButtonBorderBrush" Color="MediumSlateBlue"/>
            </ResourceDictionary>
        </ResourceDictionary.ThemeDictionaries>
    </ResourceDictionary>
</Page.Resources>

Para los estados como PointerOver (el mouse se mantiene sobre el botón), PointerPressed (se ha invocado el botón) o Deshabilitado (el botón no es interactable). Estas terminaciones se agregan a los nombres de estilos ligeros originales: ButtonBackgroundPointerOver, ButtonForegroundPressed, ButtonBorderBrushDisabled, etc. Modificar esos pinceles también, asegurará de que los controles estén coloreados de forma coherente con el tema de la aplicación.

Al colocar estas anulaciones de pincel en el nivel App.Resources, se modificarán todos los botones de toda la aplicación, en lugar de en una sola página.

Estilo por control

En otros casos, solo se desea cambiar un único control en una página para que tenga un aspecto determinado, sin modificar ninguna otra versión de ese control:

Captura de pantalla de tres botones estilizados organizados uno encima del otro.

<CheckBox Content="Normal CheckBox" Margin="5"/>
<CheckBox Content="Special CheckBox" Margin="5">
    <CheckBox.Resources>
        <ResourceDictionary>
            <ResourceDictionary.ThemeDictionaries>
                <ResourceDictionary x:Key="Light">
                    <SolidColorBrush x:Key="CheckBoxForegroundUnchecked"
                        Color="Purple"/>
                    <SolidColorBrush x:Key="CheckBoxForegroundChecked"
                        Color="Purple"/>
                    <SolidColorBrush x:Key="CheckBoxCheckGlyphForegroundChecked"
                        Color="White"/>
                    <SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeChecked"  
                        Color="Purple"/>
                    <SolidColorBrush x:Key="CheckBoxCheckBackgroundFillChecked"
                        Color="Purple"/>
                </ResourceDictionary>
            </ResourceDictionary.ThemeDictionaries>
        </ResourceDictionary>
    </CheckBox.Resources>
</CheckBox>
<CheckBox Content="Normal CheckBox" Margin="5"/>

Esto solo afectaría a que existiera un "CheckBox especial" en la página donde existía ese control.

Controles personalizados

Cuando cree sus propios controles personalizados que puedan estar alineados visualmente o funcionalmente con nuestros controles integrados, considere la posibilidad de usar recursos de estilo implícito y estilo ligero para definir el contenido personalizado. Puede usar los recursos directamente o crear un alias para el recurso.

Uso de recursos de control directamente

Por ejemplo, si va a escribir un control similar a un botón, puede hacer que el control haga referencia directamente a los recursos del botón, de la siguiente manera:

<Style TargetType="local:MyCustomControl">
  <Setter Property="Background" Value="{ThemeResource ButtonBackground}" />
  <Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderBrush}" />
</Style>

Asignar alias a los recursos de control con nuevos nombres

Como alternativa, si prefiere crear sus propios recursos, debe aplicar un alias a esos nombres personalizados para nuestros recursos de estilo ligero predeterminados.

Por ejemplo, el estilo del control personalizado puede tener definiciones de recursos especiales:

<Style TargetType="local:MyCustomControl">
  <Setter Property="Background" Value="{ThemeResource MyCustomControlBackground}" />
  <Setter Property="BorderBrush" Value="{ThemeResource MyCustomControlBorderBrush}"/>
</Style>

En su diccionario de recursos o definición principal, enlazaría los recursos de estilo ligero con sus recursos personalizados:

<ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Default">
        <StaticResource x:Key="MyCustomControlBackground" ResourceKey="ButtonBackground" />
        <StaticResource x:Key="MyCustomControlBorderBrush" ResourceKey="ButtonBorderBrush" />
    </ResourceDictionary>        
    <ResourceDictionary x:Key="Light">
        <StaticResource x:Key="MyCustomControlBackground" ResourceKey="ButtonBackground" />
        <StaticResource x:Key="MyCustomControlBorderBrush" ResourceKey="ButtonBorderBrush" />
    </ResourceDictionary>
    <ResourceDictionary x:Key="HighContrast">
        <StaticResource x:Key="MyCustomControlBackground" ResourceKey="ButtonBackground" />
        <StaticResource x:Key="MyCustomControlBorderBrush" ResourceKey="ButtonBorderBrush" />
    </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>

Es necesario que use un ThemeDictionary que se duplica tres veces para controlar los tres cambios de tema diferentes correctamente (Default, Light, HighContrast).

Precaución

Si asigna un recurso de estilo ligero a un nuevo alias y también vuelve a definir el recurso De estilo ligero, es posible que la personalización no se aplique si la búsqueda de recursos no está en el orden correcto. Por ejemplo, si invalida ButtonBackground en un lugar que se busca antes de que se encuentre MyCustomControlBackground, se omitirá la invalidación.

Evitar el rediseño de controles

La mejor forma de mantenerse al día con nuestros últimos estilos visuales es evitar los estilos y plantillas personalizados (también conocido como re-templating). Los estilos siguen siendo una manera cómoda de aplicar un conjunto de valores de forma coherente entre los controles de la aplicación. Al hacerlo, asegúrese de basarse en nuestros estilos más recientes.

Para los controles del sistema que usan estilos WinUI (espacio de nombresMicrosoft.UI.Xaml.Controls), establezca BasedOn="{StaticResource Default<ControlName>Style}", donde <ControlName> es el nombre del control. Por ejemplo:

<Style TargetType="TextBox" BasedOn="{StaticResource DefaultTextBoxStyle}">
    <Setter Property="Foreground" Value="Blue"/>
</Style>

Para los controles WinUI (Microsoft.UI.Xaml.Controls espacio de nombres), el estilo predeterminado se define en los metadatos, por lo que se omite BasedOn.

Controles derivados

Si derivas un control personalizado de un control XAML existente, es posible que no siempre obtenga los estilos winUI de forma predeterminada. Para aplicar los estilos winUI:

  • Cree un nuevo estilo con su TargetType establecido en el control personalizado.
  • Basa el estilo en el estilo predeterminado del control desde el cual se deriva.

Un escenario común para esto es crear un nuevo control a partir de ContentDialog. En este ejemplo se muestra cómo crear un nuevo estilo que aplica DefaultContentDialogStyle a su diálogo personalizado.

<ContentDialog
    x:Class="ExampleApp.SignInContentDialog"
    ... >

    <ContentDialog.Resources>
        <Style TargetType="local:SignInContentDialog" BasedOn="{StaticResource DefaultContentDialogStyle}"/>
        ...
    </ContentDialog.Resources> 
    <!-- CONTENT -->
</ContentDialog>        

La propiedad de plantilla

Se puede usar un establecedor de estilo para la propiedad Template de Control. De hecho, esto conforma la mayor parte de un estilo XAML típico y los recursos XAML de una aplicación. Esto se describe con más detalle en el tema Plantillas de control.