Aplicación de estilo mediante XAML

Las aplicaciones de interfaz de usuario de aplicaciones multiplataforma de .NET (.NET MAUI) suelen contener varios controles que tienen una apariencia idéntica. Por ejemplo, una aplicación puede tener varias Label instancias que tengan las mismas opciones de fuente y opciones de diseño:

<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" />

En este ejemplo, cada Label objeto tiene valores de propiedad idénticos para controlar la apariencia del texto mostrado por .Label Sin embargo, establecer la apariencia de cada control individual puede ser repetitivo y propenso a errores. En su lugar, se puede crear un estilo que defina la apariencia y, a continuación, se aplique a los controles necesarios.

Introducción a los estilos

Se puede aplicar un estilo a una aplicación mediante la Style clase para agrupar una colección de valores de propiedad en un objeto que, a continuación, se puede aplicar a varios elementos visuales. Esto ayuda a reducir el marcado repetitivo y permite cambiar más fácilmente la apariencia de las aplicaciones.

Aunque los estilos están diseñados principalmente para aplicaciones basadas en XAML, también se pueden crear en C#:

  • Style Los objetos creados en XAML normalmente se definen en un ResourceDictionary que se asigna a la Resources colección de un control, página o a la Resources colección de la aplicación.
  • Style Los objetos creados en C# normalmente se definen en la clase de la página o en una clase a la que se puede acceder globalmente.

La elección de dónde se puede definir un elemento Style afecta a dónde se puede usar:

  • Style Las instancias definidas en el nivel de control solo se pueden aplicar al control y a sus elementos secundarios.
  • Style Las instancias definidas en el nivel de página solo se pueden aplicar a la página y a sus elementos secundarios.
  • Style Las instancias definidas en el nivel de aplicación se pueden aplicar en toda la aplicación.

Cada Style objeto contiene una colección de uno o varios Setter objetos, con cada Setter uno con y Property .Value Property es el nombre de la propiedad enlazable del elemento al que se aplica el estilo y Value es el valor que se aplica a la propiedad.

Cada Style objeto puede ser explícito o implícito:

  • Un objeto explícitoStyle se define especificando y TargetType un x:Key valor, y estableciendo la propiedad del Style elemento de destino en la x:Key referencia. Para obtener más información, vea Estilos explícitos.
  • Un objeto implícitoStyle se define especificando solo un TargetType. A continuación, el Style objeto se aplicará automáticamente a todos los elementos de ese tipo. Sin embargo, las subclases de TargetType no tienen aplicado automáticamente .Style Para obtener más información, vea Estilos implícitos.

Al crear un elemento Style, siempre se requiere la propiedad TargetType. En el ejemplo siguiente se muestra un 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 un Style, el objeto de destino debe ser un VisualElement que coincida con el TargetType valor de propiedad de Style:

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

Los estilos inferiores en la jerarquía de vistas tienen prioridad sobre los definidos más arriba. Por ejemplo, si se establece en Label.TextColorStyleRed en el nivel de aplicación, se invalidará un estilo de nivel de página que establece en .Label.TextColorGreen Del mismo modo, un estilo de nivel de página se invalidará mediante un estilo de nivel de control. Además, si Label.TextColor se establece directamente en una propiedad de control, esto tiene prioridad sobre cualquier estilo.

Los estilos no responden a los cambios de propiedad y permanecen sin cambios durante la duración de una aplicación. Sin embargo, las aplicaciones pueden responder a cambios de estilo dinámicamente en tiempo de ejecución mediante recursos dinámicos. Para obtener más información, vea Estilos dinámicos.

Estilos explícitos

Para crear un objeto Style en el nivel de página, debe agregarse a la página y, a continuación, ResourceDictionary se pueden incluir una o varias Style declaraciones en .ResourceDictionary Un Style elemento se hace explícito al proporcionar su declaración un x:Key atributo , que le proporciona una clave descriptiva en .ResourceDictionary A continuación, los estilos explícitos se deben aplicar a elementos visuales específicos estableciendo sus Style propiedades.

En el ejemplo siguiente se muestran estilos explícitos en los objetos de ResourceDictionaryuna página y aplicados a los objetos de Label la 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>

En este ejemplo, ResourceDictionary define tres estilos que se establecen explícitamente en los objetos de Label la página. Cada Style se usa para mostrar texto en un color diferente, al tiempo que se establecen también las opciones de tamaño de fuente y diseño horizontal y vertical. Cada Style una de ellas se aplica a otro Label estableciendo sus Style propiedades mediante la StaticResource extensión de marcado. Además, mientras que la final Label tiene un Style conjunto en él, también invalida la TextColor propiedad en un valor diferente Color .

Estilos implícitos

Para crear un objeto Style en el nivel de página, debe agregarse a la página y, a continuación, ResourceDictionary se pueden incluir una o varias Style declaraciones en .ResourceDictionary Un Style elemento se convierte en implícito al no especificar un x:Key atributo . A continuación, el estilo se aplicará a en los elementos visuales de ámbito que coincidan TargetType exactamente, pero no a los elementos derivados del TargetType valor.

En el ejemplo de código siguiente se muestra un estilo implícito en el objeto de una página ResourceDictionaryy se aplica a los objetos de Entry la 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>

En este ejemplo, ResourceDictionary define un único estilo implícito que se establece implícitamente en los objetos de Entry la página. Style se usa para mostrar texto azul en un fondo amarillo, al tiempo que se establecen otras opciones de apariencia. Style se agrega a la página ResourceDictionary sin especificar un x:Key atributo . Por lo tanto, Style se aplica a todos los Entry objetos implícitamente a medida que coinciden con la TargetType propiedad de exactamente Style . Sin embargo, Style no se aplica al CustomEntry objeto , que es una subclase Entry. Además, la cuarta Entry invalida las BackgroundColor propiedades y TextColor del estilo en valores diferentes Color .

Aplicar un estilo a los tipos derivados

La Style.ApplyToDerivedTypes propiedad permite aplicar un estilo a los controles derivados del tipo base al que hace referencia la TargetType propiedad . Por lo tanto, establecer esta propiedad en true permite que un único estilo tenga como destino varios tipos, siempre que los tipos deriven del tipo base especificado en la TargetType propiedad .

En el ejemplo siguiente se muestra un estilo implícito que establece el color de fondo de Button las instancias en rojo:

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

Colocar este estilo en un nivel ResourceDictionary de página dará lugar a que se aplique a todos los objetos de la página y también a los Button controles que derivan de Button. Sin embargo, si la ApplyToDerivedTypes propiedad permanece sin establecer, el estilo solo se aplicaría a Button los objetos .

Estilos globales

Los estilos se pueden definir globalmente agregándolos al diccionario de recursos de la aplicación. Estos estilos se pueden consumir a lo largo de una aplicación y ayudar a evitar la duplicación de estilos entre páginas y controles.

En el ejemplo siguiente se muestra un Style definido en el nivel de aplicación:


<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>

En este ejemplo, ResourceDictionary define un único estilo explícito , buttonStyle, que se usará para establecer la apariencia de Button los objetos.

Nota:

Los estilos globales pueden ser explícitos o implícitos.

En el ejemplo siguiente se muestra una página que consume los buttonStyle objetos de Button la 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>

Herencia de estilo

Los estilos pueden heredar de otros estilos para reducir la duplicación y habilitar la reutilización. Esto se logra estableciendo la Style.BasedOn propiedad en un objeto existente Style. En XAML, esto se puede lograr estableciendo la BasedOn propiedad en una StaticResource extensión de marcado que hace referencia a un objeto creado Styleanteriormente.

Los estilos que heredan de un estilo base pueden incluir Setter instancias de nuevas propiedades o usarlas para invalidar establecedores del estilo base. Además, los estilos que heredan de un estilo base deben tener como destino el mismo tipo o un tipo que se derive del tipo de destino del estilo base. Por ejemplo, si un estilo base tiene View como destino objetos, los estilos basados en el estilo base pueden tener como destino View objetos o tipos que derivan de la View clase , como Label los objetos y Button .

Un estilo solo puede heredar de estilos en el mismo nivel, o superior, en la jerarquía de vistas. Esto significa que:

  • Un estilo de nivel de aplicación solo puede heredar de otros estilos de nivel de aplicación.
  • Un estilo de nivel de página puede heredar de estilos de nivel de aplicación y otros estilos de nivel de página.
  • Un estilo de nivel de control puede heredar de estilos de nivel de aplicación, estilos de nivel de página y otros estilos de nivel de control.

En el ejemplo siguiente se muestra la herencia 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>

En este ejemplo, los baseStyle objetos de destino View y establecen las HorizontalOptions propiedades y VerticalOptions . baseStyle no se establece directamente en ningún control. En su lugar, labelStyle y buttonStyle heredan de él, estableciendo valores de propiedad enlazables adicionales. A continuación, los labelStyle objetos y buttonStyle se establecen en y LabelButton.

Importante

Un estilo implícito se puede derivar de un estilo explícito, pero un estilo explícito no se puede derivar de un estilo implícito.

Estilos dinámicos

Los estilos no responden a los cambios de propiedad y permanecen sin cambios durante la duración de una aplicación. Por ejemplo, después de asignar un Style objeto a un elemento visual, si uno de los Setter objetos se modifica, quita o se agrega un nuevo Setter , los cambios no se aplicarán al elemento visual. Sin embargo, las aplicaciones pueden responder a cambios de estilo dinámicamente en tiempo de ejecución mediante recursos dinámicos.

La DynamicResource extensión de marcado es similar a la StaticResource extensión de marcado en que ambas usan una clave de diccionario para capturar un valor de .ResourceDictionary Sin embargo, mientras StaticResource realiza una búsqueda de diccionario único, DynamicResource mantiene un vínculo a la clave del diccionario. Por lo tanto, si se reemplaza la entrada del diccionario asociada a la clave, el cambio se aplica al elemento visual. Esto permite que los cambios de estilo en tiempo de ejecución se realicen en una aplicación.

En el ejemplo siguiente se muestran 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>

En este ejemplo, el SearchBar objeto usa la DynamicResource extensión de marcado para establecer un Style denominado blueSearchBarStyle. A SearchBar continuación, puede actualizar su Style definición en el código:

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

En este ejemplo, la blueSearchBarStyle definición se actualiza para usar los valores de la greenSearchBarStyle definición. Cuando se ejecute este código, SearchBar se actualizará para usar los Setter objetos definidos en greenSearchBarStyle.

Herencia de estilo dinámico

La derivación de un estilo de un estilo dinámico no se puede lograr mediante la Style.BasedOn propiedad . En su lugar, la Style clase incluye la BaseResourceKey propiedad , que se puede establecer en una clave de diccionario cuyo valor podría cambiar dinámicamente.

En el ejemplo siguiente se muestra la herencia 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>

En este ejemplo, el SearchBar objeto usa la StaticResource extensión de marcado para hacer referencia a un Style denominado tealSearchBarStyle. Esto Style establece algunas propiedades adicionales y usa la BaseResourceKey propiedad para hacer referencia blueSearchBarStylea . La DynamicResource extensión de marcado no es necesaria porque tealSearchBarStyle no cambiará, excepto por la Style derivada de . Por lo tanto, tealSearchBarStyle mantiene un vínculo a blueSearchBarStyle y se actualiza cuando cambia el estilo base.

La blueSearchBarStyle definición se puede actualizar en el código:

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

En este ejemplo, la blueSearchBarStyle definición se actualiza para usar los valores de la greenSearchBarStyle definición. Cuando se ejecute este código, SearchBar se actualizará para usar los Setter objetos definidos en greenSearchBarStyle.

Clases de estilo

Las clases de estilo permiten aplicar varios estilos a un control, sin recurrir a la herencia de estilos.

Se puede crear una clase de estilo estableciendo la Class propiedad en en un stringStyle objeto que representa el nombre de la clase. La ventaja que ofrece, sobre la definición de un estilo explícito mediante el x:Key atributo , es que se pueden aplicar varias clases de estilo a .VisualElement

Importante

Varios estilos pueden compartir el mismo nombre de clase, siempre que tengan como destino tipos diferentes. Esto permite que varias clases de estilo, que tienen un nombre idéntico, tienen como destino diferentes tipos.

En el ejemplo siguiente se muestran tres BoxView clases de estilo y una VisualElement clase 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>

En este ejemplo, las Separatorclases de estilo , Roundedy Circle establecen propiedades en BoxView valores específicos. La Rotated clase style tiene un TargetType de VisualElement, lo que significa que solo se puede aplicar a VisualElement las instancias. Sin embargo, su ApplyToDerivedTypes propiedad se establece trueen , lo que garantiza que se puede aplicar a cualquier control que derive de VisualElement, como BoxView. Para obtener más información sobre cómo aplicar un estilo a un tipo derivado, vea Aplicar un estilo a los tipos derivados.

Las clases de estilo se pueden consumir estableciendo la StyleClass propiedad del control, que es de tipo IList<string>, en una lista de nombres de clase de estilo. Se aplicarán las clases de estilo, siempre que el tipo del control coincida con el TargetType de las clases de estilo.

En el ejemplo siguiente se muestran tres BoxView instancias, cada una establecida en clases 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>    

En este ejemplo, el primero BoxView tiene el estilo para ser un separador de líneas, mientras que el tercero BoxView es circular. El segundo BoxView tiene dos clases de estilo aplicadas, que le dan esquinas redondeadas y giran 45 grados:

Screenshot of BoxViews styled with style classes.

Importante

Se pueden aplicar varias clases de estilo a un control porque la StyleClass propiedad es de tipo IList<string>. Cuando esto ocurre, las clases de estilo se aplican en orden de lista ascendente. Por lo tanto, cuando varias clases de estilo establecen propiedades idénticas, la propiedad de la clase de estilo que se encuentra en la posición de lista más alta tendrá prioridad.