Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
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:
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.
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>
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> .
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.
Pasos siguientes
.NET Desktop feedback