Plantillas de control

Puedes personalizar la estructura visual y el comportamiento visual de un control mediante la creación de una plantilla de control en el marco XAML. Los controles tienen muchas propiedades, como Background, Foreground y FontFamily, que puede establecer para especificar diferentes aspectos de la apariencia del control. Pero los cambios que puede realizar estableciendo estas propiedades son limitados. Puede especificar personalizaciones adicionales mediante la creación de una plantilla mediante la clase ControlTemplate. Aquí se muestra cómo crear un ControlTemplate para personalizar la apariencia de un control CheckBox.

API importantes: Clase ControlTemplate, propiedad Control.Template

Ejemplo de plantilla de control personalizado

De forma predeterminada, un control CheckBox coloca su contenido (la cadena o el objeto situado junto a CheckBox) a la derecha del cuadro de selección y una marca de verificación indica que un usuario seleccionó checkBox. Estas características representan la estructura visual y el comportamiento visual de CheckBox.

Este es un CheckBox con el controlTemplate predeterminado que se muestra en los Uncheckedestados , Checkedy Indeterminate .

plantilla de casilla predeterminada

Puede cambiar estas características mediante la creación de una clase ControlTemplate para checkBox. Por ejemplo, si desea que el contenido de la casilla esté debajo de la casilla de selección y desea usar una X para indicar que un usuario ha seleccionado la casilla. Estas características se especifican en controlTemplate de CheckBox.

Para usar una plantilla personalizada con un control, asigne controlTemplate a la propiedad Template del control. Este es un CheckBox mediante un controlTemplate denominado CheckBoxTemplate1. En la sección siguiente se muestra el lenguaje de marcado extensible de aplicaciones (XAML) para ControlTemplate.

<CheckBox Content="CheckBox" Template="{StaticResource CheckBoxTemplate1}" IsThreeState="True" Margin="20"/>

Este es el aspecto de checkBox en los Uncheckedestados , Checkedy Indeterminate después de aplicar la plantilla.

plantilla de casilla personalizada

Especificar la estructura visual de un control

Cuando se crea un ControlTemplate, se combinan objetos FrameworkElement para crear un único control. Un ControlTemplate debe tener solo un FrameworkElement como elemento raíz. El elemento raíz normalmente contiene otros objetos FrameworkElement . La combinación de objetos constituye la estructura visual del control.

Este XAML crea un ControlTemplate para un CheckBox que especifica que el contenido del control está debajo del cuadro de selección. El elemento raíz es border. En el ejemplo se especifica una ruta de acceso para crear una X que indica que un usuario seleccionó checkBox y una elipse que indica un estado indeterminado. Tenga en cuenta que la opacidad se establece en 0 en la ruta de acceso y la elipse para que, de forma predeterminada, tampoco aparezcan.

Un TemplateBinding es un enlace especial que vincula el valor de una propiedad de una plantilla de control al valor de otra propiedad expuesta en el control basado en modelo. TemplateBinding solo se puede usar dentro de una definición ControlTemplate en XAML. Consulta Extensión de revisión de TemplateBinding para obtener más información.

Nota:

A partir de Windows 10, versión 1809 (SDK 17763), puedes usar las extensiones de marcado x:Bind allí donde utilices TemplateBinding. Consulta Extensión de revisión de TemplateBinding para obtener más información.

<ControlTemplate x:Key="CheckBoxTemplate1" TargetType="CheckBox">
    <Border BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}"
            Background="{TemplateBinding Background}">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="25"/>
            </Grid.RowDefinitions>
            <Rectangle x:Name="NormalRectangle" Fill="Transparent" Height="20" Width="20"
                       Stroke="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
                       StrokeThickness="{ThemeResource CheckBoxBorderThemeThickness}"
                       UseLayoutRounding="False"/>
            <!-- Create an X to indicate that the CheckBox is selected. -->
            <Path x:Name="CheckGlyph"
                  Data="M103,240 L111,240 119,248 127,240 135,240 123,252 135,264 127,264 119,257 111,264 103,264 114,252 z"
                  Fill="{ThemeResource CheckBoxForegroundThemeBrush}"
                  FlowDirection="LeftToRight"
                  Height="14" Width="16" Opacity="0" Stretch="Fill"/>
            <Ellipse x:Name="IndeterminateGlyph"
                     Fill="{ThemeResource CheckBoxForegroundThemeBrush}"
                     Height="8" Width="8" Opacity="0" UseLayoutRounding="False" />
            <ContentPresenter x:Name="ContentPresenter"
                              ContentTemplate="{TemplateBinding ContentTemplate}"
                              Content="{TemplateBinding Content}"
                              Margin="{TemplateBinding Padding}" Grid.Row="1"
                              HorizontalAlignment="Center"
                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
        </Grid>
    </Border>
</ControlTemplate>

Especificar el comportamiento visual de un control

Un comportamiento visual especifica la apariencia de un control cuando se encuentra en un estado determinado. El control CheckBox tiene 3 estados de comprobación: Checked, Uncheckedy Indeterminate. El valor de la propiedad IsChecked determina el estado de checkBox y su estado determina lo que aparece en el cuadro.

En esta tabla se enumeran los posibles valores de IsChecked, los estados correspondientes de CheckBox y la apariencia de CheckBox.

Valor IsChecked Estado de CheckBox Apariencia de CheckBox
true Checked Contiene una "X".
false Unchecked Vacía.
nulo Indeterminate Contiene un círculo.

Especifique la apariencia de un control cuando se encuentra en un estado determinado mediante objetos VisualState . Un objeto VisualState contiene un establecedor o guión gráfico que cambia la apariencia de los elementos en controlTemplate. Cuando el control entra en el estado que especifica la propiedad VisualState.Name, se aplican los cambios de propiedad en Setter o Storyboard. Cuando el control sale del estado, se quitan los cambios. Agregue objetos VisualState a objetos VisualStateGroup. Los objetos VisualStateGroup se agregan a la propiedad adjunta VisualStateManager.VisualStateGroups, que se establece en la raíz FrameworkElement de ControlTemplate.

Este XAML muestra los objetos VisualState para los Checkedestados , Uncheckedy Indeterminate . En el ejemplo se establece la propiedad adjunta VisualStateManager.VisualStateGroups en border, que es el elemento raíz de ControlTemplate. El objeto VisualState Checked especifica que el valor de Opacity de la clase Path con el nombre CheckGlyph (que mostramos en el ejemplo anterior) es 1. El objeto VisualState Indeterminate especifica que el valor de Opacity de la clase Ellipse con nombre IndeterminateGlyph es 1. El objeto VisualState Unchecked no tiene ninguna clase Setter o Storyboard, por lo que CheckBox vuelve a su apariencia predeterminada.

<ControlTemplate x:Key="CheckBoxTemplate1" TargetType="CheckBox">
    <Border BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}"
            Background="{TemplateBinding Background}">

        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CheckStates">
                <VisualState x:Name="Checked">
                    <VisualState.Setters>
                        <Setter Target="CheckGlyph.Opacity" Value="1"/>
                    </VisualState.Setters>
                    <!-- This Storyboard is equivalent to the Setter. -->
                    <!--<Storyboard>
                        <DoubleAnimation Duration="0" To="1"
                         Storyboard.TargetName="CheckGlyph" Storyboard.TargetProperty="Opacity"/>
                    </Storyboard>-->
                </VisualState>
                <VisualState x:Name="Unchecked"/>
                <VisualState x:Name="Indeterminate">
                    <VisualState.Setters>
                        <Setter Target="IndeterminateGlyph.Opacity" Value="1"/>
                    </VisualState.Setters>
                    <!-- This Storyboard is equivalent to the Setter. -->
                    <!--<Storyboard>
                        <DoubleAnimation Duration="0" To="1"
                         Storyboard.TargetName="IndeterminateGlyph" Storyboard.TargetProperty="Opacity"/>
                    </Storyboard>-->
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="25"/>
            </Grid.RowDefinitions>
            <Rectangle x:Name="NormalRectangle" Fill="Transparent" Height="20" Width="20"
                       Stroke="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
                       StrokeThickness="{ThemeResource CheckBoxBorderThemeThickness}"
                       UseLayoutRounding="False"/>
            <!-- Create an X to indicate that the CheckBox is selected. -->
            <Path x:Name="CheckGlyph"
                  Data="M103,240 L111,240 119,248 127,240 135,240 123,252 135,264 127,264 119,257 111,264 103,264 114,252 z"
                  Fill="{ThemeResource CheckBoxForegroundThemeBrush}"
                  FlowDirection="LeftToRight"
                  Height="14" Width="16" Opacity="0" Stretch="Fill"/>
            <Ellipse x:Name="IndeterminateGlyph"
                     Fill="{ThemeResource CheckBoxForegroundThemeBrush}"
                     Height="8" Width="8" Opacity="0" UseLayoutRounding="False" />
            <ContentPresenter x:Name="ContentPresenter"
                              ContentTemplate="{TemplateBinding ContentTemplate}"
                              Content="{TemplateBinding Content}"
                              Margin="{TemplateBinding Padding}" Grid.Row="1"
                              HorizontalAlignment="Center"
                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
        </Grid>
    </Border>
</ControlTemplate>

Para comprender mejor cómo funcionan los objetos VisualState, tenga en cuenta lo que sucede cuando checkBox pasa del Unchecked estado al Checked estado y, a continuación, al Indeterminate estado y, a continuación, al Unchecked estado. Estas son las transiciones.

Transición de estado Qué sucede Apariencia de CheckBox cuando se completa la transición
De Unchecked a Checked. Se aplica el valor Setter del objeto VisualState Checked. Por lo tanto, el valor de Opacity de CheckGlyph es 1. Se muestra una X.
De Checked a Indeterminate. Se aplica el valor Setter del objeto VisualState Indeterminate. Por lo tanto, el valor de Opacity de IndeterminateGlyph es 1. Se quita el valor Setter del objeto VisualState Checked. Por lo tanto, el valor de Opacity de CheckGlyph es 0. Se muestra un círculo.
De Indeterminate a Unchecked. Se quita el valor Setter del objeto VisualState Indeterminate. Por lo tanto, el valor de Opacity de IndeterminateGlyph es 0. No se muestra nada.

  Para obtener más información sobre cómo crear estados visuales para controles y, en particular, cómo usar la clase Storyboard y los tipos de animación, consulta Animaciones con guion gráfico para estados visuales.

Uso de herramientas para trabajar con temas fácilmente

Una manera rápida de aplicar temas a los controles es hacer clic con el botón derecho en un control en el esquema del documento de Microsoft Visual Studio y seleccionar Editar tema o Editar estilo (según el control en el que haga clic con el botón derecho). A continuación, puede aplicar un tema existente seleccionando Aplicar recurso o definir uno nuevo seleccionando Crear vacío.

Controles y accesibilidad

Al crear una nueva plantilla para un control, además de cambiar posiblemente el comportamiento del control y la apariencia visual, es posible que también cambie la forma en que el control se representa a los marcos de accesibilidad. La aplicación de Windows admite el marco de automatización de la interfaz de usuario de Microsoft para la accesibilidad. Todos los controles predeterminados y sus plantillas tienen compatibilidad con los tipos y patrones de control de Automatización de la interfaz de usuario comunes que son adecuados para el propósito y la función del control. Estos tipos de control y patrones se interpretan mediante Automatización de la interfaz de usuario clientes, como tecnologías de asistencia, y esto permite que un control sea accesible como parte de una interfaz de usuario de aplicación accesible más grande.

Para separar la lógica de control básica y también para satisfacer algunos de los requisitos arquitectónicos de Automatización de la interfaz de usuario, las clases de control incluyen su compatibilidad de accesibilidad en una clase independiente, un elemento del mismo nivel de automatización. A veces, los elementos del mismo nivel de automatización tienen interacciones con las plantillas de control porque los elementos del mismo nivel esperan que existan determinados elementos con nombre en las plantillas, de modo que la funcionalidad como habilitar tecnologías de asistencia para invocar acciones de botones es posible.

Al crear un control personalizado completamente nuevo, a veces también querrá crear un nuevo elemento del mismo nivel de automatización para continuar con él. Para obtener más información, consulte Automatización personalizada del mismo nivel.

Más información sobre la plantilla predeterminada de un control

Los temas que documentan los estilos y plantillas para los controles XAML muestran extractos del mismo XAML inicial que vería si usaste las técnicas Editar tema o Editar estilo que se han explicado anteriormente. Cada tema enumera los nombres de los estados visuales, los recursos del tema usados y el XAML completo para el estilo que contiene la plantilla. Los temas pueden ser útiles si ya ha empezado a modificar una plantilla y desea ver el aspecto de la plantilla original o para comprobar que la nueva plantilla tiene todos los estados visuales con nombre necesarios.

Recursos de tema en plantillas de control

Para algunos de los atributos de los ejemplos XAML, es posible que haya observado referencias a recursos que usan la extensión de marcado {ThemeResource}. Se trata de una técnica que permite que una sola plantilla de control use recursos que pueden ser valores diferentes en función del tema que esté activo actualmente. Esto es especialmente importante para pinceles y colores, ya que el propósito principal de los temas es permitir a los usuarios elegir si quieren un tema oscuro, claro o de contraste alto aplicado al sistema en general. Las aplicaciones que usan el sistema de recursos XAML pueden usar un conjunto de recursos adecuado para ese tema, de modo que las opciones de tema de la interfaz de usuario de una aplicación reflejen la elección del tema en todo el sistema del usuario.

Obtener el código de ejemplo