Поделиться через


Создание шаблона для элемента управления

С помощью Windows Presentation Foundation (WPF) можно настроить визуальную структуру и поведение существующего элемента управления с помощью собственного повторно используемого шаблона. Шаблоны можно применять глобально к приложению, окнам и страницам или напрямую к элементам управления. Большинство сценариев, требующих создания нового элемента управления, можно охватывать путем создания нового шаблона для существующего элемента управления.

В этой статье вы узнаете, как создать новый ControlTemplate для элемента управления Button.

Когда необходимо создать ControlTemplate

Элементы управления имеют множество свойств, таких как Background, Foregroundи FontFamily. Эти свойства управляют различными аспектами внешнего вида элемента управления, но изменения, которые можно внести, задав эти свойства, ограничены. Например, можно задать для свойства Foreground значение синий и свойству FontStyle установить курсив на CheckBox. Если вы хотите настроить внешний вид элемента управления за пределами настройки других свойств элемента управления, создайте ControlTemplate.

В большинстве пользовательских интерфейсов кнопка имеет тот же общий вид: прямоугольник с некоторым текстом. Если вы хотите создать округленную кнопку, можно создать новый элемент управления, наследуемый от кнопки или повторно создающий функциональные возможности кнопки. Кроме того, новый пользовательский элемент управления предоставит круговой визуальный элемент.

Вы можете избежать создания новых элементов управления, настраивая визуальный макет существующего элемента управления. С помощью округленной кнопки вы создаёте ControlTemplate с нужным визуальным оформлением.

С другой стороны, если вам нужен элемент управления с новыми функциями, различными свойствами и новыми параметрами, вы создадите новый UserControl.

Необходимые условия

Создайте приложение WPF и в MainWindow.xaml (или другое окно) задайте следующие свойства в элементе <Window>:

Свойство Ценность
Title Template Intro Sample
SizeToContent WidthAndHeight
MinWidth 250

Задайте для содержимого элемента <Window> следующий код 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>

Если вы запускаете приложение, это выглядит следующим образом:

окно WPF с двумя кнопками без стиля

Создайте 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 элемента <Ellipse> привязаны к свойствам 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>

Если вы запускаете проект и посмотрите на результат, вы увидите, что кнопка имеет округленный фон.

окно WPF с одной овальной кнопкой шаблона

Возможно, вы заметили, что кнопка не круглая, а искажена. Из-за того, как работает элемент <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>

окно WPF с одной шаблонной круглой кнопкой

Добавление триггера

Несмотря на то, что кнопка с примененным шаблоном выглядит иначе, она ведет себя так же, как и любая другая кнопка. Если нажать кнопку, событие Click запускается. Однако, возможно, вы заметили, что при перемещении мыши на кнопку визуальные элементы кнопки не изменяются. Эти визуальные взаимодействия определяются шаблоном.

С помощью динамических систем событий и свойств, которые предоставляет WPF, вы можете следить за определённым свойством на наличие значения и затем обновить стиль шаблона при необходимости. В этом примере вы будете следить за свойством IsMouseOver кнопки. При наведении мыши на элемент управления, примените новый цвет к стильному эллипсу <>. Этот тип триггера называется PropertyTrigger.

Для этого необходимо добавить имя для <Эллипса>, на который можно ссылаться. Присвойте ему имя 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 у <Эллипс> на новый цвет.

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

Запустите проект. Обратите внимание, что при перемещении мыши на кнопку цвет <Ellipse> изменяется.

мышь перемещается по кнопке WPF, чтобы изменить цвет заливки

Использование VisualState

Визуальные состояния определяются и активируются элементом управления. Например, при перемещении мыши поверх элемента управления активируется состояние CommonStates.MouseOver. Вы можете анимировать изменения свойств в зависимости от текущего состояния элемента управления. В предыдущем разделе <PropertyTrigger> использовался для изменения цвета текста кнопки на 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>

В сетке <в корне шаблона элемента управления> добавьте элемент <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>. Дополнительные сведения о раскадровках см. в разделе Storyboards Overview.

  • Нормальный

    Это состояние анимирует заливку эллипса, восстанавливая его цвет до Background управляющего элемента.

    <Storyboard>
        <ColorAnimation Storyboard.TargetName="backgroundElement" 
            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
            To="{TemplateBinding Background}"
            Duration="0:0:0.3"/>
    </Storyboard>
    
  • Наведение курсора

    Это состояние анимирует многоточие 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>

Запустите проект. Обратите внимание, что при перемещении мыши на кнопку цвет <Эллипс> анимирует.

мышь перемещается по кнопке WPF, чтобы изменить визуальное состояние

Дальнейшие действия