Cómo crear un estilo para un control (WPF .NET)

Con Windows Presentation Foundation (WPF), puede usar su propia plantilla reutilizable para personalizar la apariencia de un control existente. Los estilos se pueden aplicar de forma global a la aplicación, las ventanas y las páginas, o bien directamente a los controles.

Importante

La documentación de la guía de escritorio para .NET 7 y .NET 6 está en proceso de elaboración.

Crear un estilo

Puede considerar Style como una forma cómoda de aplicar un conjunto de valores de propiedad a uno o varios elementos. Puede usar un estilo en cualquier elemento que se derive de FrameworkElement o FrameworkContentElement, como Window o Button.

La manera más común de declarar un estilo es como un recurso en la sección Resources de un archivo XAML. Como los estilos son recursos, siguen las mismas reglas de ámbito que se aplican a todos los recursos. Simplemente, la ubicación de la declaración de un estilo afecta a dónde se puede aplicar el estilo. Por ejemplo, si declara el estilo en el elemento raíz del archivo XAML de definición de la aplicación, el estilo se puede usar en cualquier parte de la aplicación.

<Application x:Class="IntroToStylingAndTemplating.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:IntroToStylingAndTemplating"
             StartupUri="WindowExplicitStyle.xaml">
    <Application.Resources>
        <ResourceDictionary>
            
            <Style x:Key="Header1" TargetType="TextBlock">
                <Setter Property="FontSize" Value="15" />
                <Setter Property="FontWeight" Value="ExtraBold" />
            </Style>
            
        </ResourceDictionary>
    </Application.Resources>
</Application>

Si lo declara en uno de los archivos XAML de la aplicación, solo se podrá usar en ese archivo XAML. Para más información acerca de las reglas de ámbito para los recursos, consulte Información general sobre los recursos XAML.

<Window x:Class="IntroToStylingAndTemplating.WindowSingleResource"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:IntroToStylingAndTemplating"
        mc:Ignorable="d"
        Title="WindowSingleResource" Height="450" Width="800">
    <Window.Resources>
        
        <Style x:Key="Header1" TargetType="TextBlock">
            <Setter Property="FontSize" Value="15" />
            <Setter Property="FontWeight" Value="ExtraBold" />
        </Style>
        
    </Window.Resources>
    <Grid />
</Window>

Un estilo se compone de elementos secundarios <Setter> que establecen propiedades en los elementos a los que el estilo en cuestión se aplica. En el ejemplo anterior, observe que el estilo está configurado para aplicarse a tipos TextBlock a través del atributo TargetType. El estilo establecerá FontSize en 15 y FontWeight en ExtraBold. Agregue un elemento <Setter> por cada propiedad que el estilo cambie.

Aplicación implícita de un estilo

Style es una forma cómoda de aplicar un conjunto de valores de propiedad a más de un elemento. Por ejemplo, fíjese en los siguientes elementos TextBlock y en su apariencia predeterminada en una ventana.

<StackPanel>
    <TextBlock>My Pictures</TextBlock>
    <TextBlock>Check out my new pictures!</TextBlock>
</StackPanel>

Captura de pantalla de ejemplo de aplicación de estilo (antes)

Para cambiar la apariencia predeterminada, puede establecer propiedades como, por ejemplo, FontSize y FontFamily en cada elemento TextBlock directamente. Si, en cambio, quiere que los elementos TextBlock compartan algunas propiedades, puede crear un elemento Style en la sección Resources del archivo XAML, como se muestra aquí.

<Window.Resources>
    <!--A Style that affects all TextBlocks-->
    <Style TargetType="TextBlock">
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="FontFamily" Value="Comic Sans MS"/>
        <Setter Property="FontSize" Value="14"/>
    </Style>
</Window.Resources>

Al establecer el elemento TargetType del estilo en el tipo TextBlock y omitir el atributo x:Key, el estilo se aplica a todos los elementos TextBlock cuyo ámbito sea ese estilo, que suele ser el propio archivo XAML.

Ahora los elementos TextBlock aparecen del modo siguiente.

Captura de pantalla de ejemplo de aplicación de estilo base

Aplicación explícita de un estilo

Si agrega al estilo un atributo x:Key con valor, dicho estilo ya no se aplicará implícitamente a todos los elementos TargetType, sino únicamente a los elementos que hagan referencia explícitamente al estilo.

Este es el estilo de la sección anterior, pero declarado con el atributo x:Key.

<Window.Resources>
    <Style x:Key="TitleText" TargetType="TextBlock">
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="FontFamily" Value="Comic Sans MS"/>
        <Setter Property="FontSize" Value="14"/>
    </Style>
</Window.Resources>

Para aplicar el estilo, establezca la propiedad Style del elemento en el valor x:Key, usando para ello una extensión de marcado StaticResource, como se muestra aquí.

<StackPanel>
    <TextBlock Style="{StaticResource TitleText}">My Pictures</TextBlock>
    <TextBlock>Check out my new pictures!</TextBlock>
</StackPanel>

Fíjese en que el primer elemento TextBlock tiene el estilo aplicado, mientras que el segundo elemento TextBlock permanece inalterado. El estilo implícito de la sección anterior cambió a un estilo que declaró el atributo x:Key, lo que significa que el único elemento afectado por el estilo es el que hacía referencia al estilo directamente.

Captura de pantalla de ejemplo de aplicación de estilo (bloque de texto)

Una vez aplicado un estilo, sea implícita o explícitamente, se bloquea y no se puede cambiar. Si desea cambiar un estilo que ya se ha aplicado, cree otro estilo para reemplazar al ya existente. Para obtener más información, vea la propiedad IsSealed.

Puede crear un objeto que elija un estilo que se aplique según una lógica personalizada. Puede ver el ejemplo relativo al código de la clase StyleSelector.

Aplicación de un estilo mediante programación

Para asignar un estilo con nombre a un elemento mediante programación, obtenga el estilo de la colección de recursos y asígnelo a la propiedad Style del elemento. Los elementos de una colección de recursos son de tipo Object, de modo que hay que convertir el estilo recuperado en un estilo System.Windows.Style para poder asignarlo a la propiedad Style. Por ejemplo, el siguiente código establece el estilo de un elemento TextBlock denominado textblock1 en el estilo definido TitleText.

textblock1.Style = (Style)Resources["TitleText"];
textblock1.Style = CType(Resources("TitleText"), Windows.Style)

Ampliación de un estilo

Puede que queramos que los dos elementos TextBlock compartan algunos valores de propiedad, como FontFamily y una alineación HorizontalAlignment centrada. Pero quizá también aplicar algunas propiedades más al texto My Pictures. Esto se puede realizar creando un estilo basado en el primero, como se muestra aquí.

<Window.Resources>
    <!-- .... other resources .... -->

    <!--A Style that affects all TextBlocks-->
    <Style TargetType="TextBlock">
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="FontFamily" Value="Comic Sans MS"/>
        <Setter Property="FontSize" Value="14"/>
    </Style>
    
    <!--A Style that extends the previous TextBlock Style with an x:Key of TitleText-->
    <Style BasedOn="{StaticResource {x:Type TextBlock}}"
           TargetType="TextBlock"
           x:Key="TitleText">
        <Setter Property="FontSize" Value="26"/>
        <Setter Property="Foreground">
            <Setter.Value>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <LinearGradientBrush.GradientStops>
                        <GradientStop Offset="0.0" Color="#90DDDD" />
                        <GradientStop Offset="1.0" Color="#5BFFFF" />
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<StackPanel>
    <TextBlock Style="{StaticResource TitleText}" Name="textblock1">My Pictures</TextBlock>
    <TextBlock>Check out my new pictures!</TextBlock>
</StackPanel>

Ahora, este estilo TextBlock está centrado y usa una fuente Comic Sans MS con un tamaño de 26 y el color de primer plano establecido en LinearGradientBrush, como se muestra en el ejemplo. Fíjese en que el valor de FontSize del estilo base se ha reemplazado. Si hay más de un elemento Setter que apunta a la misma propiedad en Style, el elemento Setter que se haya declarado más recientemente será el que tenga prioridad.

Aquí se muestra el aspecto de los elementos TextBlock ahora:

Bloques de texto con estilo

Este estilo de TitleText amplía el estilo que se ha creado para el tipo TextBlock, al que se hace referencia con BasedOn="{StaticResource {x:Type TextBlock}}". También se puede ampliar un estilo que tiene un elemento x:Key usando el elemento x:Key del estilo en cuestión. Por ejemplo, si hay un estilo denominado Header1 y queremos ampliarlo, usaríamos BasedOn="{StaticResource Header1}".

Relación entre la propiedad TargetType y el atributo x:Key

Como hemos visto antes, si la propiedad TargetType se establece en TextBlock sin asignar un elemento x:Key al estilo, esto hace que el estilo se aplique a todos los elementos TextBlock. En este caso, x:Key se establece implícitamente en {x:Type TextBlock}. Esto significa que si el valor de x:Key se establece explícitamente en algo distinto de {x:Type TextBlock}, Style no se aplicará automáticamente a todos los elementos TextBlock. En su lugar, deberá aplicar el estilo (usando el valor de x:Key) a los elementos TextBlock explícitamente. Si el estilo está en la sección de recursos y no se establece la propiedad TargetType en el estilo, deberá establecer el atributo x:Key.

Además de proporcionar un valor predeterminado para x:Key, la propiedad TargetType especifica el tipo al que se aplican las propiedades del establecedor. Si no especifica un elemento TargetType, deberá calificar las propiedades de los objetos Setter con un nombre de clase usando la sintaxis Property="ClassName.Property". Por ejemplo, en lugar de establecer Property="FontSize", deberá establecer Property en "TextBlock.FontSize" o en "Control.FontSize".

Tenga en cuenta también que muchos controles de WPF son una combinación de otros controles de WPF. Si crea un estilo aplicable a todos los controles de un tipo, podría obtener resultados inesperados. Por ejemplo, si crea un estilo que tiene como destino el tipo TextBlock en un elemento Window, el estilo se aplicará a todos los controles TextBlock de la ventana, aun cuando TextBlock forme parte de otro control, como ListBox.

Vea también