Создание шаблона для элемента управления (WPF.NET)
В Windows Presentation Foundation (WPF) можно настраивать визуальную структуру и функциональные возможности существующего элемента управления с помощью своего собственного шаблона многократного использования. Шаблоны можно применять глобально ко всему приложению, отдельным окнам и страницам или непосредственно к элементам управления. Большинство сценариев, в которых требуется создать новый элемент управления, можно реализовать, создав вместо этого новый шаблон для существующего элемента управления.
В этой статье будет показано, как создать новый шаблон ControlTemplate для элемента управления Button.
Когда следует создавать ControlTemplate
Элементы управления имеют много свойств, таких, например, как Background, Foreground и FontFamily. Эти свойства управляют различными аспектами внешнего вида элемента управления, но с помощью них можно внести не так много изменений. Например, для элемента управления CheckBox можно задать синий цвет фона с помощью свойства Foreground и курсив с помощью свойства FontStyle. Если вы хотите внести такие изменения внешнего вида элемента управления, которые не предусмотрены его свойствами, можно создать шаблон ControlTemplate.
В большинстве пользовательских интерфейсов кнопка обычно выглядит как прямоугольник с текстом. Если вы хотите создать круглую кнопку, можно создать новый элемент управления, который наследует функциональность от кнопки или воссоздает функциональность кнопки. И вдобавок этот новый пользовательский элемент управления будет иметь круглую форму.
Вы можете не создавать новые элементы управления, а просто настроить визуальный макет существующего элемента управления. Например, для круглой кнопки можно создать шаблон ControlTemplate с желаемым визуальным макетом.
С другой стороны, если вам нужен элемент управления с новыми функциями, другими свойствами и новыми параметрами, лучше создать новый UserControl.
Необходимые компоненты
Создайте новое приложение WPF и в окне MainWindow.xaml (или в другом окне по вашему выбору) установите следующие свойства элемента <Окно>:
Свойство | Значение |
---|---|
Title | Template Intro Sample |
SizeToContent | WidthAndHeight |
MinWidth | 250 |
В качестве содержимого элемента <Окно> задайте следующий код XAML:
<StackPanel Margin="10">
<Label>Unstyled Button</Label>
<Button>Button 1</Button>
<Label>Rounded Button</Label>
<Button>Button 2</Button>
</StackPanel>
В итоге файл MainWindow.xaml должен выглядеть следующим образом.
<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>
Если запустить приложение, оно будет выглядеть так.
Создание шаблона ControlTemplate
Чаще всего ControlTemplate объявляется как ресурс в разделе Resources
файла XAML. Так как шаблоны являются ресурсами, для них действуют те же правила определения области, что и для всех других ресурсов. Проще говоря, то, где вы объявляете шаблон, влияет на то, где этот шаблон может быть применен. Например, если вы объявите шаблон в корневом элементе XAML-файла определения приложения, этот шаблон можно будет использовать в любом месте вашего приложения. Если вы определяете шаблон в окне, его смогут использовать только элементы управления из этого окна.
Для начала добавьте элемент Window.Resources
в свой файл 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>
Создайте новый шаблон <ControlTemplate> со следующими заданными свойствами:
Свойство | Значение |
---|---|
x:Key | roundbutton |
TargetType | Button |
Этот шаблон элемента управления будет простым:
- корневой элемент этого элемента управления, Grid;
- Ellipse, чтобы кнопка отображалась круглой;
- ContentPresenter для вывода указанного пользователем содержимого кнопки.
<ControlTemplate x:Key="roundbutton" TargetType="Button">
<Grid>
<Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
TemplateBinding
Создавая новый шаблон ControlTemplate, вы можете захотеть использовать общие свойства для изменения внешнего вида элемента управления. Расширение разметки TemplateBinding привязывает свойство элемента из шаблона ControlTemplate к общему свойству, которое задается элементом управления. При использовании расширения TemplateBinding свойства элемента управления могут действовать в качестве параметров шаблона. Это означает, что при задании свойства элемента управления соответствующее значение передается в элемент, который содержит TemplateBinding.
Эллипс
Обратите внимание, что свойства Fill и Stroke элемента <Эллипс> привязаны к свойствам элемента управления Foreground и свойствам Background.
ContentPresenter
Элемент <ContentPresenter> также добавляется в шаблон. Так как этот шаблон предназначен для кнопки, необходимо учитывать, что эта кнопка наследует от ContentControl. Кнопка представляет содержимое элемента. Можно задать что-либо внутри кнопки, например обычный текст или даже другой элемент управления. Оба следующих варианта — правильные кнопки:
<Button>My Text</Button>
<!-- and -->
<Button>
<CheckBox>Checkbox in a button</CheckBox>
</Button>
В обоих приведенных выше примерах текст и флажок задаются как свойство Button.Content. Все, что задано в качестве содержимого, может быть представлено с помощью <ContentPresenter>, что и делается в шаблоне.
Если ControlTemplate применяется к типу ContentControl, такому как Button
, выполняется поиск ContentPresenter в дереве элементов. Если ContentPresenter
обнаруживается, шаблон автоматически привязывает свойство Content элемента управления к элементу ContentPresenter
.
Использование шаблона
Найдите кнопки, которые были объявлены в начале этой статьи.
<StackPanel Margin="10">
<Label>Unstyled Button</Label>
<Button>Button 1</Button>
<Label>Rounded Button</Label>
<Button>Button 2</Button>
</StackPanel>
Задайте в свойстве Template второй кнопки ресурс roundbutton
:
<StackPanel Margin="10">
<Label>Unstyled Button</Label>
<Button>Button 1</Button>
<Label>Rounded Button</Label>
<Button Template="{StaticResource roundbutton}">Button 2</Button>
</StackPanel>
Если теперь вы запустите проект и посмотрите на результат, то фон кнопки будет иметь овальную форму.
Вы, конечно, заметили, что кнопка не круглая, а вытянутая. Элемент <Ellipse> всегда расширяется и заполняет доступное пространство. Сделайте кнопку круглой, задав в свойствах width и height одно и то же значение:
<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>
Добавление триггера
Хотя кнопка с примененным шаблоном выглядит иначе, она ведет себя так же, как и любая другая кнопка. При нажатии кнопки срабатывает событие Click. Однако, как вы могли заметить, при наведении указателя мыши на кнопку визуально ничего не меняется. Все визуальные взаимодействия определяются шаблоном.
В системах динамических событий и свойств, предоставляемых WPF, можно отслеживать значение конкретного свойства, а затем при необходимости изменять стиль шаблона. В данном примере вы будете отслеживать свойство IsMouseOver кнопки. Задайте новый цвет для элемента <Ellipse>, который должен отображаться, когда указатель мыши наводится над этим элементом управления. Триггер такого типа называется PropertyTrigger.
Чтобы это работало, необходимо добавить в элемент <Ellipse> имя, на которое можно ссылаться. Задайте имя backgroundElement.
<Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
Затем добавьте новый Trigger в коллекцию ControlTemplate.Triggers. Этот триггер будет отслеживать, когда событие IsMouseOver
принимает значение 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>
Затем добавьте метод <Setter> в <Trigger>, который задает для свойства Fill элемента <Ellipse> новое значение цвета.
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Fill" TargetName="backgroundElement" Value="AliceBlue"/>
</Trigger>
Запустите проект. Обратите внимание, что при наведении мыши на кнопку цвет элемента <Ellipse> меняется.
Использование VisualState
Визуальные состояния определяются и активируются элементом управления. Например, при наведении указателя мыши на элемент управления активируется состояние CommonStates.MouseOver
. Изменения свойств можно анимировать на основе текущего состояния элемента управления. В предыдущем разделе свойство СвойствоTrigger> использовалось для изменения фона кнопки на AliceBlue
то, когда IsMouseOver
свойство былоtrue
.< Теперь вместо этого создайте визуальное состояние, которое анимирует изменение этого цвета, обеспечивая плавный переход. Дополнительные сведения об элементе VisualStates см. в разделе Стили и шаблоны в WPF.
Чтобы преобразовать <PropertyTrigger> в анимированное визуальное состояние, сначала нужно удалить из шаблона элемент <ControlTemplate.Triggers>.
<ControlTemplate x:Key="roundbutton" TargetType="Button">
<Grid>
<Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
Затем в раздел <Grid> шаблона элемента управления добавьте элемент <VisualStateManager.VisualStateGroups> с элементом <VisualStateGroup> для параметров CommonStates
. Определите два состояния — Normal
и 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>
Все анимации, определенные в <VisualState>, применяются при активации соответствующего состояния. Создайте анимации для каждого состояния. Анимации помещаются в элемент <Раскадровка>. Дополнительные сведения о раскадровках см. в статье Общие сведения о Storyboard.
Обычная
Это состояние анимирует заливку эллипса, восстанавливающую цвет
Background
элемента управления.<Storyboard> <ColorAnimation Storyboard.TargetName="backgroundElement" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" To="{TemplateBinding Background}" Duration="0:0:0.3"/> </Storyboard>
MouseOver
Это состояние анимирует изменение цвета
Background
эллипса на новый цвет:Yellow
.<Storyboard> <ColorAnimation Storyboard.TargetName="backgroundElement" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" To="Yellow" Duration="0:0:0.3"/> </Storyboard>
Теперь <ControlTemplate> должен выглядеть следующим образом.
<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>
Запустите проект. Обратите внимание, что при наведении мыши на кнопку изменение цвета элемента <Ellipse> анимируется.
Следующие шаги
.NET Desktop feedback