Compartir a través de


Creación de una plantilla para un control (WPF.NET)

Con Windows Presentation Foundation (WPF), puedes personalizar la estructura visual y el comportamiento de un control existente con tu propia plantilla reutilizable. Las plantillas se pueden aplicar globalmente a la aplicación, ventanas y páginas, o directamente a los controles. La mayoría de los escenarios que requieren crear un nuevo control se pueden tratar mediante la creación de una nueva plantilla para un control existente.

En este artículo, exploraréis la creación de un nuevo ControlTemplate para el control Button.

Cuándo crear un ControlTemplate

Los controles tienen muchas propiedades, como Background, Foregroundy FontFamily. Estas propiedades controlan distintos aspectos de la apariencia del control, pero los cambios que puede realizar estableciendo estas propiedades son limitados. Por ejemplo, puede establecer la propiedad Foreground a azul y FontStyle a cursiva en un CheckBox. Cuando desee personalizar la apariencia del control más allá de lo que puede hacer la configuración de las otras propiedades en el control, cree un ControlTemplate.

En la mayoría de las interfaces de usuario, un botón tiene la misma apariencia general: un rectángulo con algún texto. Si desea crear un botón redondeado, podría crear un nuevo control que herede del botón o volver a crear la funcionalidad del botón. Además, el nuevo control de usuario proporcionaría el visual circular.

Puede evitar la creación de nuevos controles personalizando el diseño visual de un control existente. Con un botón redondeado, se crea un ControlTemplate con el diseño visual deseado.

Por otro lado, si necesita un control con nuevas funcionalidades, propiedades diferentes y nuevas configuraciones, crearía un nuevo UserControl.

Prerrequisitos

Cree una nueva aplicación WPF y, en MainWindow.xaml (u otra ventana de su elección) establezca las siguientes propiedades en el <elemento Window> :

Propiedad Importancia
Title Template Intro Sample
SizeToContent WidthAndHeight
MinWidth 250

Establezca el contenido del <elemento Window> en el código XAML siguiente:

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button>Button 2</Button>
</StackPanel>

Al final, el archivo MainWindow.xaml debe tener un aspecto similar al siguiente:

<Window x:Class="IntroToStylingAndTemplating.Window1"
        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="Template Intro Sample" SizeToContent="WidthAndHeight" MinWidth="250">
    <StackPanel Margin="10">
        <Label>Unstyled Button</Label>
        <Button>Button 1</Button>
        <Label>Rounded Button</Label>
        <Button>Button 2</Button>
    </StackPanel>
</Window>

Si ejecuta la aplicación, tiene el siguiente aspecto:

Ventana WPF con dos botones sin estilo

Crear una plantilla de control

La forma más común de declarar un ControlTemplate elemento es como un recurso en la Resources sección de un archivo XAML. Dado que las plantillas son recursos, cumplen las mismas reglas de ámbito que se aplican a todos los recursos. En pocas palabras, donde declara una plantilla afecta a dónde se puede aplicar la plantilla. Por ejemplo, si declaras la plantilla en el elemento raíz del archivo XAML de definición de aplicación, la plantilla se puede usar en cualquier lugar de la aplicación. Si define la plantilla en una ventana, solo los controles de esa ventana pueden usar la plantilla.

Para empezar, agregue un Window.Resources elemento al archivo MainWindow.xaml :

<Window x:Class="IntroToStylingAndTemplating.Window2"
        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="Template Intro Sample" SizeToContent="WidthAndHeight" MinWidth="250">
    <Window.Resources>
        
    </Window.Resources>
    <StackPanel Margin="10">
        <Label>Unstyled Button</Label>
        <Button>Button 1</Button>
        <Label>Rounded Button</Label>
        <Button>Button 2</Button>
    </StackPanel>
</Window>

Cree una nueva <ControlTemplate> con las siguientes propiedades establecidas:

Propiedad Importancia
x:Key roundbutton
TargetType Button

Esta plantilla de control será sencilla:

  • un elemento raíz para el control , un Grid
  • Ellipse para dibujar la apariencia redondeada del botón
  • ContentPresenter para mostrar el contenido del botón especificado por el usuario
<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

TemplateBinding

Al crear un nuevo ControlTemplate, es posible que quiera usar las propiedades públicas para cambiar la apariencia del control. La extensión de marcado TemplateBinding enlaza una propiedad de un elemento que se encuentra en ControlTemplate a una propiedad pública definida por el control . Cuando se usa TemplateBinding, se habilitan las propiedades del control para que actúen como parámetros en la plantilla. Es decir, cuando se establece una propiedad en un control, ese valor se pasa al elemento que tiene templateBinding en él.

Elipse

Observe que las propiedades Fill y Stroke del elemento <Ellipse> están enlazadas a las propiedades Foreground y Background del control.

PresentadorDeContenido

También se agrega un <elemento ContentPresenter> a la plantilla. Dado que esta plantilla está diseñada para un botón, tenga en cuenta que el botón hereda de ContentControl. El botón presenta el contenido del elemento. Puede establecer cualquier elemento dentro del botón, como texto sin formato o incluso otro control. Ambos son botones válidos:

<Button>My Text</Button>

<!-- and -->

<Button>
    <CheckBox>Checkbox in a button</CheckBox>
</Button>

En ambos ejemplos anteriores, el texto y la casilla se establecen como la propiedad Button.Content . Lo que sea que se establezca como contenido se puede presentar a través de un <ContentPresenter>, que es lo que hace la plantilla.

Si el ControlTemplate se aplica a un tipo ContentControl, como un Button, se busca un ContentPresenter en el árbol de elementos. Si se encuentra el ContentPresenter, la plantilla enlaza automáticamente la propiedad del control Content a ContentPresenter.

Uso de la plantilla

Busque los botones declarados al principio de este artículo.

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button>Button 2</Button>
</StackPanel>

Establezca la propiedad del Template segundo botón en el roundbutton recurso:

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button Template="{StaticResource roundbutton}">Button 2</Button>
</StackPanel>

Si ejecuta el proyecto y observa el resultado, verá que el botón tiene un fondo redondeado.

Ventana de WPF con un botón oval de plantilla

Es posible que haya observado que el botón no es un círculo, pero está sesgado. Debido a la forma en que funciona el <elemento Ellipse> , siempre se expande para rellenar el espacio disponible. Haga que el círculo sea uniforme cambiando las propiedades width y height del botón al mismo valor.

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button Template="{StaticResource roundbutton}" Width="65" Height="65">Button 2</Button>
</StackPanel>

Ventana de WPF con un botón circular de plantilla

Agregar un desencadenador

Aunque un botón con una plantilla aplicada tiene un aspecto diferente, se comporta igual que cualquier otro botón. Si presiona el botón, el evento Click se activa. Sin embargo, es posible que haya observado que al mover el mouse sobre el botón, los objetos visuales del botón no cambian. Estas interacciones visuales se definen mediante la plantilla.

Con los sistemas de propiedades y eventos dinámicos que proporciona WPF, puede ver una propiedad específica para un valor y, a continuación, volver a diseñar la plantilla cuando corresponda. En este ejemplo, verá la propiedad del botón IsMouseOver. Cuando el mouse está sobre el control, estilo la <elipse> con un nuevo color. Este tipo de desencadenador se conoce como PropertyTrigger.

Para que esto funcione, deberá agregar un nombre a la <elipse> al que puede hacer referencia. Asígnele el nombre de backgroundElement.

<Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />

A continuación, agregue un nuevo Trigger elemento a la colección ControlTemplate.Triggers . El desencadenador observará el IsMouseOver evento para el valor true.

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="true">

        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

A continuación, agregue un <establecedor> al <desencadenador> que cambie la propiedad Fill de la elipse<> a un nuevo color.

<Trigger Property="IsMouseOver" Value="true">
    <Setter Property="Fill" TargetName="backgroundElement" Value="AliceBlue"/>
</Trigger>

Ejecute el proyecto. Observe que al mover el mouse sobre el botón, cambia el color de la <elipse> .

el mouse se mueve sobre el botón WPF para cambiar el color de relleno

Uso de VisualState

Un control define y desencadena los estados visuales. Por ejemplo, cuando el mouse se mueve encima del control, se desencadena el CommonStates.MouseOver estado. Puede animar los cambios de propiedad en función del estado actual del control. En la sección anterior, se usó un <PropertyTrigger> para cambiar el fondo del botón a AliceBlue cuando la IsMouseOver propiedad era true. En su lugar, cree un estado visual que anima el cambio de este color, lo que proporciona una transición suave. Para obtener más información sobre VisualStates, vea Estilos y plantillas en WPF.

Para convertir PropertyTrigger<> en un estado visual animado, primero quite el <elemento ControlTemplate.Triggers> de la plantilla.

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

A continuación, en el <Grid> raíz de la plantilla de control, agregue el <elemento VisualStateManager.VisualStateGroups> con un <elemento VisualStateGroup> para CommonStates. Defina dos estados, Normal y MouseOver.

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="CommonStates">
                <VisualState Name="Normal">
                </VisualState>
                <VisualState Name="MouseOver">
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

Las animaciones definidas en un <objeto VisualState> se aplican cuando se desencadena ese estado. Cree animaciones para cada estado. Las animaciones se colocan dentro de un <elemento Storyboard> . Para obtener más información sobre guiones gráficos, consulte Información general sobre guiones gráficos.

  • Normal

    Este estado anima el relleno de la elipse, restaurándolo al color del control Background.

    <Storyboard>
        <ColorAnimation Storyboard.TargetName="backgroundElement" 
            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
            To="{TemplateBinding Background}"
            Duration="0:0:0.3"/>
    </Storyboard>
    
  • Pasar el ratón por encima

    Este estado anima el color de la elipse Background a un nuevo color: Yellow.

    <Storyboard>
        <ColorAnimation Storyboard.TargetName="backgroundElement" 
            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" 
            To="Yellow" 
            Duration="0:0:0.3"/>
    </Storyboard>
    

ControlTemplate<> debería tener ahora un aspecto similar al siguiente.

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="CommonStates">
                <VisualState Name="Normal">
                    <Storyboard>
                        <ColorAnimation Storyboard.TargetName="backgroundElement" 
                            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
                            To="{TemplateBinding Background}"
                            Duration="0:0:0.3"/>
                    </Storyboard>
                </VisualState>
                <VisualState Name="MouseOver">
                    <Storyboard>
                        <ColorAnimation Storyboard.TargetName="backgroundElement" 
                            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" 
                            To="Yellow" 
                            Duration="0:0:0.3"/>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Ellipse Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter x:Name="contentPresenter" HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

Ejecute el proyecto. Note que al mover el ratón sobre el botón, el color de la <elipse> se anima.

el ratón se mueve sobre un botón WPF para cambiar el color de relleno mediante un estado visual

Pasos siguientes