Настройка внешнего вида существующего элемента управления путем создания объекта ControlTemplateCustomizing the Appearance of an Existing Control by Creating a ControlTemplate

ControlTemplate задает визуальную структуру и визуальное поведение элемента управления.A ControlTemplate specifies the visual structure and visual behavior of a control. Внешний вид элемента управления можно настроить, присвоив ему новый ControlTemplate.You can customize the appearance of a control by giving it a new ControlTemplate. При создании ControlTemplateвы заменяете внешний вид существующего элемента управления, не изменяя его функциональность.When you create a ControlTemplate, you replace the appearance of an existing control without changing its functionality. Например, можно сделать так, чтобы кнопки в приложении округлялись вместо квадратной фигуры по умолчанию, но кнопка по-прежнему будет вызывать событие Click.For example, you can make the buttons in your application round instead of the default square shape, but the button will still raise the Click event.

В этом разделе объясняются различные части ControlTemplate, демонстрируется создание простого ControlTemplate для Buttonи объясняется, как определить контракт элемента управления, чтобы можно было настроить его внешний вид.This topic explains the various parts of a ControlTemplate, demonstrates creating a simple ControlTemplate for a Button, and explains how to understand the control contract of a control so that you can customize its appearance. Поскольку вы создаете ControlTemplate в XAMLXAML, можно изменить внешний вид элемента управления без написания кода.Because you create a ControlTemplate in XAMLXAML, you can change a control's appearance without writing any code. Для создания пользовательских шаблонов элементов управления можно также использовать конструктор, например Blend для Visual Studio.You can also use a designer, such as Blend for Visual Studio, to create custom control templates. В этом разделе приведены примеры в XAMLXAML, которые настраивают внешний вид Button и список полных примеров в конце раздела.This topic shows examples in the XAMLXAML that customize the appearance of a Button and lists the complete example at the end of the topic. Дополнительные сведения об использовании Blend для Visual Studio см. в разделе Стилизация элемента управления, поддерживающего шаблоны.For more information about using Blend for Visual Studio, see Styling a control that supports templates.

На следующих иллюстрациях показан Button, использующий ControlTemplate, созданный в этом разделе.The following illustrations show a Button that uses the ControlTemplate that is created in this topic.

Кнопка, использующая пользовательский шаблон элемента управления.A button with a custom control template. Кнопка, использующая пользовательский шаблон элемента управленияA button that uses a custom control template

Кнопка с красной границей.A button with a red border. Кнопка, использующая пользовательский шаблон элемента управления, когда на ней находится указатель мышиA button that uses a custom control template and has the mouse pointer over it

Необходимые компонентыPrerequisites

В этом разделе предполагается, что пользователь понимает процесс создания и использования элементов управления и стилей, который рассматривался в разделе Элементы управления.This topic assumes that you understand how to create and use controls and styles as discussed in Controls. Концепции, обсуждаемые в этом разделе, применяются к элементам, которые наследуются от класса Control, за исключением UserControl.The concepts discussed in this topic apply to elements that inherit from the Control class, except for the UserControl. К UserControlнельзя применить ControlTemplate.You cannot apply a ControlTemplate to a UserControl.

Когда следует создавать объект ControlTemplateWhen You Should Create a ControlTemplate

Элементы управления имеют множество свойств, таких как Background, Foregroundи FontFamily, которые можно задать, чтобы указать различные аспекты внешнего вида элемента управления, но изменения, которые можно выполнить путем установки этих свойств, ограничены.Controls have many properties, such as Background, Foreground, and FontFamily, that you can set to specify different aspects of the control's appearance, but the changes that you can make by setting these properties are limited. Например, можно задать для свойства Foreground значение Blue, а FontStyle курсивом на CheckBox.For example, you can set the Foreground property to blue and FontStyle to italic on a CheckBox.

Без возможности создания новых ControlTemplate для элементов управления все элементы управления в каждом WPFWPFном приложении будут иметь одинаковый общий внешний вид, что ограничит возможность создания приложения с пользовательским видом и поведением.Without the ability to create a new ControlTemplate for controls, all controls in every WPFWPF-based application would have the same general appearance, which would limit the ability to create an application with a custom look and feel. По умолчанию каждый CheckBox имеет аналогичные характеристики.By default, every CheckBox has similar characteristics. Например, содержимое CheckBox всегда находится справа от индикатора выбора, а галочка всегда используется для указания того, что CheckBox выбрано.For example, the content of the CheckBox is always to the right of the selection indicator, and the check mark is always used to indicate that the CheckBox is selected.

Вы создаете ControlTemplate, если хотите настроить внешний вид элемента управления, помимо того, что устанавливает другие свойства элемента управления.You create a ControlTemplate when you want to customize the control's appearance beyond what setting the other properties on the control will do. В примере CheckBoxПредположим, что содержимое флажка должно располагаться над индикатором выбора, и необходимо указать X, чтобы показать, что CheckBox выбрано.In the example of the CheckBox, suppose that you want the content of the check box to be above the selection indicator and you want an X to indicate that the CheckBox is selected. Эти изменения указываются в ControlTemplate CheckBox.You specify these changes in the ControlTemplate of the CheckBox.

На следующем рисунке показана CheckBox, использующая ControlTemplateпо умолчанию.The following illustration shows a CheckBox that uses a default ControlTemplate.

Элемент управления CheckBox, использующий шаблон элемента управления по умолчанию.A checkbox with the default control template. Элемент управления CheckBox, использующий шаблон элемента управления по умолчаниюA CheckBox that uses the default control template

На следующем рисунке показана CheckBox, использующая пользовательский ControlTemplate для размещения содержимого CheckBox над индикатором выбора и отображения X при выборе CheckBox.The following illustration shows a CheckBox that uses a custom ControlTemplate to place the content of the CheckBox above the selection indicator and displays an X when the CheckBox is selected.

Элемент управления CheckBox, использующий пользовательский шаблон элемента управления.A checkbox with a custom control template. Элемент управления CheckBox, использующий пользовательский шаблон элемента управленияA CheckBox that uses a custom control template

ControlTemplate для CheckBox в этом образце относительно сложны, поэтому в этом разделе используется более простой пример создания ControlTemplate для Button.The ControlTemplate for the CheckBox in this sample is relatively complex, so this topic uses a simpler example of creating a ControlTemplate for a Button.

Изменение визуальной структуры элемента управленияChanging the Visual Structure of a Control

В WPFWPFэлемент управления часто является составным FrameworkElement объектами.In WPFWPF, a control is often a composite FrameworkElement objects. При создании ControlTemplateвы объединяете FrameworkElement объекты для создания одного элемента управления.When you create a ControlTemplate, you combine FrameworkElement objects to build a single control. У ControlTemplate должен быть только один FrameworkElement в качестве корневого элемента.A ControlTemplate must have only one FrameworkElement as its root element. Корневой элемент обычно содержит другие объекты FrameworkElement.The root element usually contains other FrameworkElement objects. Комбинация объектов составляет визуальную структуру элемента управления.The combination of objects makes up the control's visual structure.

В следующем примере создается настраиваемый ControlTemplate для Button.The following example creates a custom ControlTemplate for the Button. ControlTemplate создает визуальную структуру Button.The ControlTemplate creates the visual structure of the Button. В этом примере не меняется внешний вид кнопки при перемещении на нее указателя мыши или при ее нажатии.This example does not change the button's appearance when you move the mouse pointer over it or click it. Изменение внешнего вида кнопки при ее переходе в другое состояние рассматривается далее в этом разделе.Changing the button's appearance when it is in a different state is discussed later in this topic.

В данном примере визуальная структура состоит из следующих частей:In this example, the visual structure consists of the following parts:

  • Border с именем RootElement, который служит корневым FrameworkElementшаблона.A Border named RootElement that serves as the template's root FrameworkElement.

  • Grid, являющийся дочерним элементом RootElement.A Grid that is a child of RootElement.

  • ContentPresenter, отображающая содержимое кнопки.A ContentPresenter that displays the button's content. ContentPresenter позволяет отображать любой тип объекта.The ContentPresenter enables any type of object to be displayed.

<ControlTemplate TargetType="Button">
  <Border Name="RootElement">

    <!--Create the SolidColorBrush for the Background 
        as an object elemment and give it a name so 
        it can be referred to elsewhere in the
        control template.-->
    <Border.Background>
      <SolidColorBrush x:Name="BorderBrush" Color="Black"/>
    </Border.Background>

    <!--Create a border that has a different color
        by adding smaller grid. The background of 
        this grid is specificied by the button's 
        Background property.-->
    <Grid Margin="4" Background="{TemplateBinding Background}">

      <!--Use a ContentPresenter to display the Content of
          the Button.-->
      <ContentPresenter
        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
        Margin="4,5,4,4" />
    </Grid>

  </Border>
</ControlTemplate>

Сохранение функциональности свойств элемента управления с помощью TemplateBindingPreserving the Functionality of a Control's Properties by Using TemplateBinding

При создании нового ControlTemplateможет потребоваться использовать открытые свойства для изменения внешнего вида элемента управления.When you create a new ControlTemplate, you still might want to use the public properties to change the control's appearance. Расширение разметки TemplateBinding привязывает свойство элемента, который находится в ControlTemplate, к открытому свойству, определенному элементом управления.The TemplateBinding markup extension binds a property of an element that is in the ControlTemplate to a public property that is defined by the control. Использование расширения TemplateBinding позволяет свойствам элемента управления действовать в качестве параметров шаблона.When you use TemplateBinding, you enable properties on the control to act as parameters to the template. Это означает, что при задании свойства элемента управления соответствующее значение передается в элемент, который содержит TemplateBinding.That is, when a property on a control is set, that value is passed on to the element that has the TemplateBinding on it.

В следующем примере повторяется часть предыдущего примера, в которой используется расширение разметки TemplateBinding для привязки свойств элементов, которые находятся в ControlTemplate, к общим свойствам, определенным кнопкой.The following example repeats the part of the preceding example that uses the TemplateBinding markup extension to bind properties of elements that are in the ControlTemplate to public properties that are defined by the button.

<Grid Margin="4" Background="{TemplateBinding Background}">

  <!--Use a ContentPresenter to display the Content of
      the Button.-->
  <ContentPresenter
    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
    Margin="4,5,4,4" />
</Grid>

В этом примере Grid имеет шаблон свойства Panel.Background, привязанный к Control.Background.In this example, the Grid has its Panel.Background property template bound to Control.Background. Поскольку Panel.Background является привязанным шаблоном, можно создать несколько кнопок, использующих один и тот же ControlTemplate, и задать для Control.Background различные значения для каждой кнопки.Because Panel.Background is template bound, you can create multiple buttons that use the same ControlTemplate and set the Control.Background to different values on each button. Если Control.Background не был привязан к свойству элемента в ControlTemplate, установка Control.Background кнопки не повлияет на внешний вид кнопки.If Control.Background was not template bound to a property of an element in the ControlTemplate, setting the Control.Background of a button would have no impact on the button's appearance.

Следует отметить, что не требуется, чтобы имена этих двух свойств совпадали.Note that the names of the two properties do not need to be identical. В предыдущем примере свойство Control.HorizontalContentAlignment Button является шаблоном, привязанным к свойству FrameworkElement.HorizontalAlignment ContentPresenter.In the preceding example, the Control.HorizontalContentAlignment property of the Button is template bound to the FrameworkElement.HorizontalAlignment property of the ContentPresenter. Это позволяет располагать содержимое кнопки горизонтально.This enables the content of the button to be positioned horizontally. ContentPresenter не имеет свойства с именем HorizontalContentAlignment, но Control.HorizontalContentAlignment можно привязать к FrameworkElement.HorizontalAlignment.ContentPresenter does not have a property named HorizontalContentAlignment, but Control.HorizontalContentAlignment can be bound to FrameworkElement.HorizontalAlignment. Перед привязкой свойства к шаблону следует убедиться, что целевое и исходное свойства имеют один и тот же тип.When you template bind a property, be sure that the target and source properties are the same type.

Класс Control определяет несколько свойств, которые должны использоваться шаблоном элемента управления для получения влияния на элемент управления, когда они заданы.The Control class defines several properties that must be used by the control template to have an effect on the control when they are set. То, как ControlTemplate использует свойство, зависит от свойства.How the ControlTemplate uses the property depends on the property. ControlTemplate должен использовать свойство одним из следующих способов:The ControlTemplate must use the property in one of the following ways:

В следующей таблице перечислены визуальные свойства, наследуемые элементом управления от класса Control.The following table lists the visual properties inherited by a control from the Control class. В таблице также указывается, использует ли шаблон элемента управления по умолчанию значение наследуемого свойства или оно должно быть привязано к шаблону.It also indicates whether the default control template of a control uses the inherited property value or if it must be template bound.

свойство;Property Способ использованияUsage method
Background Привязка к шаблонуTemplate binding
BorderThickness Привязка к шаблонуTemplate binding
BorderBrush Привязка к шаблонуTemplate binding
FontFamily Наследование свойства или привязка к шаблонуProperty inheritance or template binding
FontSize Наследование свойства или привязка к шаблонуProperty inheritance or template binding
FontStretch Наследование свойства или привязка к шаблонуProperty inheritance or template binding
FontWeight Наследование свойства или привязка к шаблонуProperty inheritance or template binding
Foreground Наследование свойства или привязка к шаблонуProperty inheritance or template binding
HorizontalContentAlignment Привязка к шаблонуTemplate binding
Padding Привязка к шаблонуTemplate binding
VerticalContentAlignment Привязка к шаблонуTemplate binding

В таблице перечислены только визуальные свойства, унаследованные от класса Control.The table lists only the visual properties inherited from the Control class. Помимо свойств, перечисленных в таблице, элемент управления может также наследовать свойства DataContext, Languageи TextDecorations из родительского элемента Framework.Apart from the properties listed in the table, a control may also inherit the DataContext, Language, and TextDecorations properties from the parent framework element.

Кроме того, если ContentPresenter находится в ControlTemplate ContentControl, ContentPresenter будет автоматически привязан к свойствам ContentTemplate и Content.Also, if the ContentPresenter is in the ControlTemplate of a ContentControl, the ContentPresenter will automatically bind to the ContentTemplate and Content properties. Аналогичным образом ItemsPresenter, который находится в ControlTemplate ItemsControl, автоматически привязывается к свойствам Items и ItemsPresenter.Likewise, an ItemsPresenter that is in the ControlTemplate of an ItemsControl will automatically bind to the Items and ItemsPresenter properties.

В следующем примере создаются две кнопки, использующие ControlTemplate, определенные в предыдущем примере.The following example creates two buttons that use the ControlTemplate defined in the preceding example. В примере задаются свойства Background, Foregroundи FontSize для каждой кнопки.The example sets the Background, Foreground, and FontSize properties on each button. Установка свойства Background оказывает воздействие, так как оно привязано к шаблону в ControlTemplate.Setting the Background property has an effect because it is template bound in the ControlTemplate. Несмотря на то, что свойства Foreground и FontSize не привязаны к шаблону, их установка оказывает воздействие, поскольку их значения наследуются.Even though the Foreground and FontSize properties are not template bound, setting them has an effect because their values are inherited.

<StackPanel>
  <Button Style="{StaticResource newTemplate}" 
          Background="Navy" Foreground="White" FontSize="14"
          Content="Button1"/>

  <Button Style="{StaticResource newTemplate}" 
          Background="Purple" Foreground="White" FontSize="14"
          Content="Button2" HorizontalContentAlignment="Left"/>
</StackPanel>

В предыдущем примере получался результат, аналогичный показанному на следующем рисунке.The preceding example produces output that is similar to the following illustration.

Две кнопки, одна синяя, а другая — фиолетовая.Two buttons, one blue and one purple. Две кнопки с различными цветами фонаTwo buttons with different background colors

Изменение внешнего вида элемента управления в зависимости от его состоянияChanging the Appearance of a Control Depending on Its State

Разница между кнопкой с внешним видом по умолчанию и кнопкой из предыдущего примера состоит в том, что кнопка по умолчанию слегка изменяется при переходе в другое состояние.The difference between a button with its default appearance and the button in the preceding example is that the default button subtly changes when it is in different states. Например, внешний вид кнопки по умолчанию изменяется при нажатии этой кнопки или при наведении на нее указателя мыши.For example, the default button's appearance changes when the button is pressed, or when the mouse pointer is over the button. Хотя ControlTemplate не изменяет функциональность элемента управления, он изменяет визуальное поведение элемента управления.Although the ControlTemplate does not change the functionality of a control, it does change the control's visual behavior. Визуальное поведение описывает внешний вид элемента управления в определенных состояниях.A visual behavior describes the control appearance when it is in a certain state. Чтобы лучше понять разницу между функциональностью и визуальным поведением элемента управления, рассмотрим пример с кнопкой.To understand the difference between the functionality and visual behavior of a control, consider the button example. Функциональность кнопки заключается в вызове события Click при щелчке, но визуальное поведение кнопки заключается в том, чтобы изменить внешний вид, на который указывает или нажата кнопка.The button's functionality is to raise the Click event when it is clicked, but the button's visual behavior is to change its appearance when it is pointed to or pressed.

Чтобы задать внешний вид элемента управления, когда он находится в определенном состоянии, используются VisualState объекты.You use VisualState objects to specify the appearance of a control when it is in a certain state. VisualState содержит Storyboard, который изменяет внешний вид элементов в ControlTemplate.A VisualState contains a Storyboard that changes the appearance of the elements that are in the ControlTemplate. Для этого не нужно писать код, так как логика элемента управления меняет состояние с помощью VisualStateManager.You do not have to write any code to make this occur because the control's logic changes state by using the VisualStateManager. Когда элемент управления переходит в состояние, заданное свойством VisualState.Name, начинается Storyboard.When the control enters the state that is specified by the VisualState.Name property, the Storyboard begins. Когда элемент управления выходит из состояния, Storyboard останавливается.When the control exits the state, the Storyboard stops.

В следующем примере показаны VisualState, которые изменяют внешний вид Button при наведении на нее указателя мыши.The following example shows the VisualState that changes the appearance of a Button when the mouse pointer is over it. Storyboard изменяет цвет границы кнопки, изменяя цвет BorderBrush.The Storyboard changes the button's border color by changing the color of the BorderBrush. Если вы ссылаетесь на ControlTemplate пример в начале этого раздела, вы помните, что BorderBrush является именем SolidColorBrush, назначенного Background Border.If you refer to the ControlTemplate example at the beginning of this topic, you will recall that BorderBrush is the name of the SolidColorBrush that is assigned to the Background of the Border.

<!--Change the border of the button to red when the
    mouse is over the button.-->
<VisualState x:Name="MouseOver">
  <Storyboard>
    <ColorAnimation Storyboard.TargetName="BorderBrush"     
                    Storyboard.TargetProperty="Color"
                    To="Red" />

  </Storyboard>
</VisualState>

Элемент управления отвечает за определение состояний как часть своего контракта, что подробно рассматривается в подразделе Настройка других элементов управления через понимание контракта элемента управления далее в этом разделе.The control is responsible for defining the states as part of its control contract, which is discussed in detail in Customizing Other Controls by Understanding the Control Contract later in this topic. В следующей таблице перечислены состояния, указанные для Button.The following table lists the states that are specified for the Button.

Имя VisualStateVisualState Name Имя VisualStateGroupVisualStateGroup Name ОписаниеDescription
Норм.Normal CommonStatesCommonStates Состояние по умолчанию.The default state.
MouseOverMouseOver CommonStatesCommonStates Указатель мыши расположен над элементом управления.The mouse pointer is positioned over the control.
НажатоPressed CommonStatesCommonStates Элемент управления нажат.The control is pressed.
Отключено.Disabled CommonStatesCommonStates Элемент управления отключен.The control is disabled.
FocusedFocused FocusStatesFocusStates Элемент управления имеет фокус.The control has focus.
Без фокуса вводаUnfocused FocusStatesFocusStates Элемент управления не имеет фокуса.The control does not have focus.

Button определяет две группы состояний: Группа CommonStates содержит Normal, MouseOver, Pressedи Disabled состояния.The Button defines two state groups: the CommonStates group contains the Normal, MouseOver, Pressed, and Disabled states. Группа FocusStates содержит состояния Focused и Unfocused.The FocusStates group contains the Focused and Unfocused states. Состояния, входящие в одну группу, являются взаимоисключающими.States in the same state group are mutually exclusive. Элемент управления всегда находится строго в одном состоянии из каждой группы.The control is always in exactly one state per group. Например, Button может иметь фокус, даже если указатель мыши находится не над ним, так что Button в Focused состоянии может находиться в MouseOver, Pressedили Normal.For example, a Button can have focus even when the mouse pointer is not over it, so a Button in the Focused state can be in the MouseOver, Pressed, or Normal state.

VisualState объекты добавляются к VisualStateGroup объектам.You add VisualState objects to VisualStateGroup objects. VisualStateGroup объекты добавляются в присоединенное к VisualStateGroups свойство.You add VisualStateGroup objects to the VisualStateGroups attached property. В следующем примере определяются объекты VisualState для состояний Normal, MouseOverи Pressed, которые находятся в группе CommonStates.The following example defines the VisualState objects for the Normal, MouseOver, and Pressed states, which are all in the CommonStates group. Name каждого VisualState совпадает с именем в предыдущей таблице.The Name of each VisualState matches the name in the preceding table. Состояние Disabled и состояния из группы FocusStates опущены для сохранения краткости примера, но они включены в полный пример, приведенный в конце этого раздела.The Disabled state and the states in the FocusStates group are omitted to keep the example short, but they are included in the entire example at the end of this topic.

Примечание

Не забудьте задать VisualStateGroups присоединенное свойство в корневой FrameworkElement ControlTemplate.Be sure to set the VisualStateGroups attached property on the root FrameworkElement of the ControlTemplate.

<ControlTemplate TargetType="Button">
  <Border Name="RootElement">

    <VisualStateManager.VisualStateGroups>

      <!--Define the states and transitions for the common states.
          The states in the VisualStateGroup are mutually exclusive to
          each other.-->
      <VisualStateGroup x:Name="CommonStates">

        <!--The Normal state is the state the button is in
            when it is not in another state from this VisualStateGroup.-->
        <VisualState x:Name="Normal" />

        <!--Change the SolidColorBrush, BorderBrush, to red when the
            mouse is over the button.-->
        <VisualState x:Name="MouseOver">
          <Storyboard>
            <ColorAnimation Storyboard.TargetName="BorderBrush" 
                            Storyboard.TargetProperty="Color" 
                            To="Red" />
          </Storyboard>
        </VisualState>

        <!--Change the SolidColorBrush, BorderBrush, to Transparent when the
            button is pressed.-->
        <VisualState x:Name="Pressed">
          <Storyboard>
            <ColorAnimation Storyboard.TargetName="BorderBrush" 
                            Storyboard.TargetProperty="Color"
                            To="Transparent"/>
          </Storyboard>
        </VisualState>

        <!--The Disabled state is omitted for brevity.-->
      </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    <Border.Background>
      <SolidColorBrush x:Name="BorderBrush" Color="Black"/>
    </Border.Background>

    <Grid Background="{TemplateBinding Background}" Margin="4">
      <ContentPresenter
        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
        Margin="4,5,4,4" />
    </Grid>
  </Border>
</ControlTemplate>

В предыдущем примере получался результат, аналогичный показанному на следующих рисунках.The preceding example produces output that is similar to the following illustrations.

Кнопка, использующая пользовательский шаблон элемента управления.A button with a custom control template. Кнопка, использующая пользовательский шаблон элемента управления в нормальном состоянииA button that uses a custom control template in the normal state

Кнопка с красной границей.A button with a red border. Кнопка, использующая пользовательский шаблон элемента управления в состоянии, когда указатель мыши находится над кнопкойA button that uses a custom control template in the mouse over state

Граница прозрачна на нажатой кнопке.The border is transparent on a pressed button. Кнопка, использующая пользовательский шаблон элемента управления в состоянии, когда она нажатаA button that uses a custom control template in the pressed state

Визуальные состояния элементов управления, включенных в WPFWPF, см. в разделе Стили и шаблоны элемента Control.To find the visual states for controls that are included with WPFWPF, see Control Styles and Templates.

Задание поведения элемента управления, когда он переходит между состояниямиSpecifying the Behavior of a Control When It Transitions Between States

В предыдущем примере внешний вид кнопки изменяется, когда пользователь нажимает ее, но если кнопка остается нажатой менее полной секунды, то пользователь не видит эффекта нажатия.In the preceding example, the appearance of the button also changes when the user clicks it, but unless the button is pressed for a full second, the user does not see the effect. По умолчанию для запуска анимации требуется одна секунда.By default, the animation takes one second to occur. Поскольку пользователи, скорее всего, щелкают и освобождают кнопку в гораздо меньше времени, визуальная обратная связь не будет действовать, если вы оставите ControlTemplate в своем состоянии по умолчанию.Because users are likely to click and release a button in much less time, the visual feedback will not be effective if you leave the ControlTemplate in its default state.

Можно указать время, затрачиваемое на выполнение анимации, чтобы плавно перевести элемент управления из одного состояния в другое, добавив VisualTransition объекты в ControlTemplate.You can specify the amount of time that it takes an animation to occur to smoothly transition a control from one state to another by adding VisualTransition objects to the ControlTemplate. При создании VisualTransitionнеобходимо указать один или несколько из следующих элементов:When you create a VisualTransition, you specify one or more of the following:

  • время, которое занимает переход между состояниями;The time it takes for a transition between states to occur.

  • дополнительные изменения внешнего вида элемента управления, возникающие во время перехода;Additional changes in the control's appearance that occur at the time of the transition.

  • Состояние, к которому применяется VisualTransition.Which states the VisualTransition is applied to.

Задание продолжительности переходаSpecifying the Duration of a Transition

Можно указать, как долго выполняется переход, установив свойство GeneratedDuration.You can specify how long a transition takes by setting the GeneratedDuration property. В предыдущем примере имеется VisualState, указывающий, что граница кнопки становится прозрачной при нажатии кнопки, но анимация занимает слишком много времени, чтобы быть заметной при быстром нажатии и отпускании кнопки.The preceding example has a VisualState that specifies that the button's border becomes transparent when the button is pressed, but the animation takes too long to be noticeable if the button is quickly pressed and released. Можно использовать VisualTransition, чтобы указать период времени, в течение которого элемент управления переходит в состояние нажатия.You can use a VisualTransition to specify the amount of time it takes the control to transition into the pressed state. В следующем примере задается, что переход элемента управления в нажатое состояние занимает одну сотую секунды.The following example specifies that the control takes one hundredth of a second to go into the pressed state.

<!--Take one hundredth of a second to transition to the
    Pressed state.-->
<VisualTransition To="Pressed" 
                  GeneratedDuration="0:0:0.01" />

Задание изменений внешнего вида элемента управления во время переходаSpecifying Changes to the Control's Appearance During a Transition

VisualTransition содержит Storyboard, который начинается при переходе элемента управления между состояниями.The VisualTransition contains a Storyboard that begins when the control transitions between states. Например, можно указать, что определенная анимация происходит при переходе элемента управления из состояния MouseOver в состояние Normal.For example, you can specify that a certain animation occurs when the control transitions from the MouseOver state to the Normal State. В следующем примере создается VisualTransition, указывающий, что когда пользователь перемещает указатель мыши за пределы кнопки, граница кнопки меняется на синий, затем на желтый, затем на черный через 1,5 секунд.The following example creates a VisualTransition that specifies that when the user moves the mouse pointer away from the button, the button's border changes to blue, then to yellow, then to black in 1.5 seconds.

<!--Take one and a half seconds to transition from the
    MouseOver state to the Normal state. 
    Have the SolidColorBrush, BorderBrush, fade to blue, 
    then to yellow, and then to black in that time.-->
<VisualTransition From="MouseOver" To="Normal" 
                      GeneratedDuration="0:0:1.5">
  <Storyboard>
    <ColorAnimationUsingKeyFrames
      Storyboard.TargetProperty="Color"
      Storyboard.TargetName="BorderBrush"
      FillBehavior="HoldEnd" >

      <ColorAnimationUsingKeyFrames.KeyFrames>

        <LinearColorKeyFrame Value="Blue" 
          KeyTime="0:0:0.5" />
        <LinearColorKeyFrame Value="Yellow" 
          KeyTime="0:0:1" />
        <LinearColorKeyFrame Value="Black" 
          KeyTime="0:0:1.5" />

      </ColorAnimationUsingKeyFrames.KeyFrames>
    </ColorAnimationUsingKeyFrames>
  </Storyboard>
</VisualTransition>

Указание места применения VisualTransitionSpecifying When a VisualTransition Is Applied

VisualTransition может быть ограничена только определенными состояниями, или его можно применять при каждом переходе элемента управления между состояниями.A VisualTransition can be restricted to apply to only certain states, or it can be applied any time the control transitions between states. В предыдущем примере VisualTransition применяется при переходе элемента управления из состояния MouseOver в состояние Normal; в приведенном выше примере VisualTransition применяется при переходе элемента управления в состояние Pressed.In the preceding example, the VisualTransition is applied when the control goes from the MouseOver state to the Normal state; in the example before that, the VisualTransition is applied when the control goes into the Pressed state. Вы ограничиваете применение VisualTransition, устанавливая свойства To и From.You restrict when a VisualTransition is applied by setting the To and From properties. В таблице ниже приведено описание уровней ограничений от наибольшего до наименьшего.The following table describes the levels of restriction from most restrictive to least restrictive.

Тип ограниченияType of restriction Состояние, из которого выполняется переходValue of From Состояние, в которое выполняется переходValue of To
Из указанного состояния в другое указанное состояниеFrom a specified state to another specified state Имя VisualStateThe name of a VisualState Имя VisualStateThe name of a VisualState
Из любого состояния в указанное состояниеFrom any state to a specified state Не заданоNot set Имя VisualStateThe name of a VisualState
Из указанного состояния в любое состояниеFrom a specified state to any state Имя VisualStateThe name of a VisualState Не заданоNot set
Из любого состояния в любое другое состояниеFrom any state to any other state Не заданоNot set Не заданоNot set

В VisualStateGroup, которые ссылаются на одно и то же состояние, можно использовать несколько VisualTransition объектов, но они будут использоваться в порядке, указанном в предыдущей таблице.You can have multiple VisualTransition objects in a VisualStateGroup that refer to the same state, but they will be used in the order that the previous table specifies. В следующем примере имеется два VisualTransition объектов.In the following example, there are two VisualTransition objects. Когда элемент управления переходит из состояния Pressed в состояние MouseOver, используется VisualTransition, в котором заданы как From, так и To.When the control transitions from the Pressed state to the MouseOver state, the VisualTransition that has both From and To set is used. Когда элемент управления переходит из состояния, отличного от Pressed, в состояние MouseOver, используется другой объект.When the control transitions from a state that is not Pressed to the MouseOver state, the other state is used.

<!--Take one half second to trasition to the MouseOver state.-->
<VisualTransition To="MouseOver" 
                  GeneratedDuration="0:0:0.5" />

<!--Take one hundredth of a second to transition from the
    Pressed state to the MouseOver state.-->
<VisualTransition From="Pressed" To="MouseOver" 
                  GeneratedDuration="0:0:0.01" />

VisualStateGroup имеет свойство Transitions, содержащее объекты VisualTransition, которые применяются к VisualState объектам в VisualStateGroup.The VisualStateGroup has a Transitions property that contains the VisualTransition objects that apply to the VisualState objects in the VisualStateGroup. Как автор ControlTemplate, вы можете включать любые VisualTransition.As the ControlTemplate author, you are free to include any VisualTransition you want. Однако если для свойств To и From заданы имена состояний, которые не находятся в VisualStateGroup, VisualTransition игнорируется.However, if the To and From properties are set to state names that are not in the VisualStateGroup, the VisualTransition is ignored.

В следующем примере показаны VisualStateGroup для CommonStates.The following example shows the VisualStateGroup for the CommonStates. В примере определяется VisualTransition для каждого из следующих переходов кнопки.The example defines a VisualTransition for each of the button's following transitions.

  • в состояние Pressed;To the Pressed state.

  • в состояние MouseOver;To the MouseOver state.

  • из состояния Pressed в состояние MouseOver;From the Pressed state to the MouseOver state.

  • из состояния MouseOver в состояние Normal;From the MouseOver state to the Normal state.

<VisualStateGroup x:Name="CommonStates">

  <!--Define the VisualTransitions that
      can be used when the control transitions 
      between VisualStates that are defined in the
      VisualStatGroup.-->
  <VisualStateGroup.Transitions>

    <!--Take one hundredth of a second to 
        transition to the Pressed state.-->
    <VisualTransition To="Pressed" 
                      GeneratedDuration="0:0:0.01" />

    <!--Take one half second to trasition 
        to the MouseOver state.-->
    <VisualTransition To="MouseOver" 
                      GeneratedDuration="0:0:0.5" />

    <!--Take one hundredth of a second to transition from the
        Pressed state to the MouseOver state.-->
    <VisualTransition From="Pressed" To="MouseOver" 
                      GeneratedDuration="0:0:0.01" />

    <!--Take one and a half seconds to transition from the
        MouseOver state to the Normal state. 
        Have the SolidColorBrush, BorderBrush, fade to blue, 
        then to yellow, and then to black in that time.-->
    <VisualTransition From="MouseOver" To="Normal" 
                      GeneratedDuration="0:0:1.5">
      <Storyboard>
        <ColorAnimationUsingKeyFrames
          Storyboard.TargetProperty="Color"
          Storyboard.TargetName="BorderBrush"
          FillBehavior="HoldEnd" >

          <ColorAnimationUsingKeyFrames.KeyFrames>
            <LinearColorKeyFrame Value="Blue" 
              KeyTime="0:0:0.5" />
            <LinearColorKeyFrame Value="Yellow" 
              KeyTime="0:0:1" />
            <LinearColorKeyFrame Value="Black" 
              KeyTime="0:0:1.5" />

          </ColorAnimationUsingKeyFrames.KeyFrames>
        </ColorAnimationUsingKeyFrames>
      </Storyboard>
    </VisualTransition>
  </VisualStateGroup.Transitions>

  <!--The remainder of the VisualStateGroup is the
      same as the previous example.-->

  <VisualState x:Name="Normal" />

  <VisualState x:Name="MouseOver">
    <Storyboard>
      <ColorAnimation 
        Storyboard.TargetName="BorderBrush" 
        Storyboard.TargetProperty="Color" 
        To="Red" />

    </Storyboard>
  </VisualState>

  <VisualState x:Name="Pressed">
    <Storyboard>
      <ColorAnimation 
        Storyboard.TargetName="BorderBrush" 
        Storyboard.TargetProperty="Color" 
        To="Transparent"/>
    </Storyboard>
  </VisualState>

  <!--The Disabled state is omitted for brevity.-->

</VisualStateGroup>

Настройка других элементов управления через понимание контракта элемента управленияCustomizing Other Controls by Understanding the Control Contract

Элемент управления, использующий ControlTemplate для указания его визуальной структуры (с помощью FrameworkElement объектов) и визуального поведения (с помощью VisualState объектов) использует модель управления частями.A control that uses a ControlTemplate to specify its visual structure (by using FrameworkElement objects) and visual behavior (by using VisualState objects) uses the parts control model. Множество элементов управления, имеющихся в WPFWPF 4, используют эту модель.Many of the controls that are included with WPFWPF 4 use this model. Части, которые необходимо учитывать автору ControlTemplate, взаимодействуют через контракт управления.The parts that a ControlTemplate author needs to be aware of are communicated through the control contract. Поняв части контракта элемента управления, можно настроить внешний вид любого элемента управления, использующего модель частей элемента управления.When you understand the parts of a control contract, you can customize the appearance of any control that uses the parts control model.

Контракт элемента управления имеет три элемента:A control contract has three elements:

  • визуальный элемент, используемый логикой элемента управления;The visual elements that the control's logic uses.

  • состояния элемента управления и группа, к которой принадлежит каждое состояние;The states of the control and the group each state belongs to.

  • общие свойства, визуально воздействующие на элемент управления.The public properties that visually affect the control.

Визуальные элементы в контракте элемента управленияVisual Elements in the Control Contract

Иногда логика элемента управления взаимодействует с FrameworkElement, который находится в ControlTemplate.Sometimes a control's logic interacts with a FrameworkElement that is in the ControlTemplate. Например, элемент управления может обрабатывать событие одного из своих элементов.For example, the control might handle an event of one of its elements. Когда элемент управления планирует найти определенный FrameworkElement в ControlTemplate, он должен передать эту информацию автору ControlTemplate.When a control expects to find a particular FrameworkElement in the ControlTemplate, it must convey that information to the ControlTemplate author. Элемент управления использует TemplatePartAttribute, чтобы передать ожидаемый тип элемента и имя элемента.The control uses the TemplatePartAttribute to convey the type of element that is expected, and what the name of the element should be. Button не содержит FrameworkElement частей в контракте элемента управления, но другие элементы управления, такие как ComboBox, делают.The Button does not have FrameworkElement parts in its control contract, but other controls, such as the ComboBox, do.

В следующем примере показаны объекты TemplatePartAttribute, указанные в классе ComboBox.The following example shows the TemplatePartAttribute objects that are specified on the ComboBox class. Логика ComboBox планирует найти в PART_Popupе TextBox с именем PART_EditableTextBox и Popup с именем ControlTemplate.The logic of ComboBox expects to find a TextBox named PART_EditableTextBox and a Popup named PART_Popup in its ControlTemplate.

[TemplatePartAttribute(Name = "PART_EditableTextBox", Type = typeof(TextBox))]
[TemplatePartAttribute(Name = "PART_Popup", Type = typeof(Popup))]
public class ComboBox : ItemsControl
{
}
<TemplatePartAttribute(Name:="PART_EditableTextBox", Type:=GetType(TextBox))> _
<TemplatePartAttribute(Name:="Part_Popup", Type:=GetType(Popup))> _
Public Class ComboBox
    Inherits ItemsControl

End Class

В следующем примере показано упрощенное ControlTemplate для ComboBox, включающего элементы, заданные TemplatePartAttributeными объектами в классе ComboBox.The following example shows a simplified ControlTemplate for the ComboBox that includes the elements that are specified by the TemplatePartAttribute objects on the ComboBox class.

<ControlTemplate TargetType="ComboBox">
  <Grid>
    <ToggleButton x:Name="DropDownToggle"
      HorizontalAlignment="Stretch" VerticalAlignment="Stretch"  
      Margin="-1" HorizontalContentAlignment="Right"
      IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,
                  RelativeSource={RelativeSource TemplatedParent}}">
      <Path x:Name="BtnArrow" Height="4" Width="8" 
        Stretch="Uniform" Margin="0,0,6,0"  Fill="Black"
        Data="F1 M 300,-190L 310,-190L 305,-183L 301,-190 Z " />
    </ToggleButton>
    <ContentPresenter x:Name="ContentPresenter" Margin="6,2,25,2"
      Content="{TemplateBinding SelectionBoxItem}"
      ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
      ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}">
    </ContentPresenter>
    <TextBox x:Name="PART_EditableTextBox"
      Style="{x:Null}"
      Focusable="False"
      Background="{TemplateBinding Background}"
      HorizontalAlignment="Left" 
      VerticalAlignment="Center" 
      Margin="3,3,23,3"
      Visibility="Hidden"
      IsReadOnly="{TemplateBinding IsReadOnly}"/>

    <Popup x:Name="PART_Popup"
      IsOpen="{TemplateBinding IsDropDownOpen}">
      <Border x:Name="PopupBorder" 
        HorizontalAlignment="Stretch" Height="Auto" 
        MinWidth="{TemplateBinding ActualWidth}"
        MaxHeight="{TemplateBinding MaxDropDownHeight}"
        BorderThickness="{TemplateBinding BorderThickness}" 
        BorderBrush="Black" Background="White" CornerRadius="3">
        <ScrollViewer x:Name="ScrollViewer" BorderThickness="0" Padding="1">
          <ItemsPresenter/>
        </ScrollViewer>
      </Border>
    </Popup>

  </Grid>
</ControlTemplate>

Состояния в контракте элемента управленияStates in the Control Contract

Состояния элемента управления также являются частью контракта элемента управления.The states of a control are also a part of the control contract. Пример создания ControlTemplate для Button показывает, как задать внешний вид Button в зависимости от его состояний.The example of creating a ControlTemplate for a Button shows how to specify the appearance of a Button depending on its states. Можно создать VisualState для каждого заданного состояния и разместить все VisualState объекты, совместно использующие GroupName, в VisualStateGroup, как описано в разделе Изменение внешнего вида элемента управления в зависимости от его состояния ранее в этой статье.You create a VisualState for each specified state and put all VisualState objects that share a GroupName in a VisualStateGroup, as described in Changing the Appearance of a Control Depending on Its State earlier in this topic. Сторонние элементы управления должны указывать состояния с помощью TemplateVisualStateAttribute, что позволяет средствам конструктора, таким как конструктор XAML в Visual Studio и Blend для Visual Studio, предоставлять состояния элемента управления для создания шаблонов элементов управления.Third-party controls should specify states by using the TemplateVisualStateAttribute, which enables designer tools, such as the XAML Designer in Visual Studio and Blend for Visual Studio, to expose the control's states for authoring control templates.

Контракты элементов управления, включенных в WPFWPF, в разделе Стили и шаблоны элемента Control.To find the control contract for controls that are included with WPFWPF, see Control Styles and Templates.

Свойства в контракте элемента управленияProperties in the Control Contract

Общие свойства, визуально влияющие на элемент управления, также включаются в контракт элемента управления.The public properties that visually affect the control are also included in the control contract. Эти свойства можно задать, чтобы изменить внешний вид элемента управления без создания нового ControlTemplate.You can set these properties to change the appearance of the control without creating a new ControlTemplate. Расширение разметки TemplateBinding также можно использовать для привязки свойств элементов, которые находятся в ControlTemplate, к общим свойствам, определенным Button.You can also use the TemplateBinding markup extension to bind properties of elements that are in the ControlTemplate to public properties that are defined by the Button.

В следующем примере показан контракт элемента управления "Кнопка".The following example shows the control contract for the button.

[TemplateVisualState(Name = "Normal", GroupName = "CommonStates")]
[TemplateVisualState(Name = "MouseOver", GroupName = "CommonStates")]
[TemplateVisualState(Name = "Pressed", GroupName = "CommonStates")]
[TemplateVisualState(Name = "Disabled", GroupName = "CommonStates")]
[TemplateVisualState(Name = "Unfocused", GroupName = "FocusStates")]
[TemplateVisualState(Name = "Focused", GroupName = "FocusStates")]
public class Button : ButtonBase
{
    public static readonly DependencyProperty BackgroundProperty;
    public static readonly DependencyProperty BorderBrushProperty;
    public static readonly DependencyProperty BorderThicknessProperty;
    public static readonly DependencyProperty ContentProperty;
    public static readonly DependencyProperty ContentTemplateProperty;
    public static readonly DependencyProperty FontFamilyProperty;
    public static readonly DependencyProperty FontSizeProperty;
    public static readonly DependencyProperty FontStretchProperty;
    public static readonly DependencyProperty FontStyleProperty;
    public static readonly DependencyProperty FontWeightProperty;
    public static readonly DependencyProperty ForegroundProperty;
    public static readonly DependencyProperty HorizontalContentAlignmentProperty;
    public static readonly DependencyProperty PaddingProperty;
    public static readonly DependencyProperty TextAlignmentProperty;
    public static readonly DependencyProperty TextDecorationsProperty;
    public static readonly DependencyProperty TextWrappingProperty;
    public static readonly DependencyProperty VerticalContentAlignmentProperty;

    public Brush Background { get; set; }
    public Brush BorderBrush { get; set; }
    public Thickness BorderThickness { get; set; }
    public object Content { get; set; }
    public DataTemplate ContentTemplate { get; set; }
    public FontFamily FontFamily { get; set; }
    public double FontSize { get; set; }
    public FontStretch FontStretch { get; set; }
    public FontStyle FontStyle { get; set; }
    public FontWeight FontWeight { get; set; }
    public Brush Foreground { get; set; }
    public HorizontalAlignment HorizontalContentAlignment { get; set; }
    public Thickness Padding { get; set; }
    public TextAlignment TextAlignment { get; set; }
    public TextDecorationCollection TextDecorations { get; set; }
    public TextWrapping TextWrapping { get; set; }
    public VerticalAlignment VerticalContentAlignment { get; set; }
}
<TemplateVisualState(Name:="Normal", GroupName:="CommonStates")> _
<TemplateVisualState(Name:="MouseOver", GroupName:="CommonStates")> _
<TemplateVisualState(Name:="Pressed", GroupName:="CommonStates")> _
<TemplateVisualState(Name:="Disabled", GroupName:="CommonStates")> _
<TemplateVisualState(Name:="Unfocused", GroupName:="FocusStates")> _
<TemplateVisualState(Name:="Focused", GroupName:="FocusStates")> _
Public Class Button
    Inherits ButtonBase

    Public Shared ReadOnly BackgroundProperty As DependencyProperty
    Public Shared ReadOnly BorderBrushProperty As DependencyProperty
    Public Shared ReadOnly BorderThicknessProperty As DependencyProperty
    Public Shared ReadOnly ContentProperty As DependencyProperty
    Public Shared ReadOnly ContentTemplateProperty As DependencyProperty
    Public Shared ReadOnly FontFamilyProperty As DependencyProperty
    Public Shared ReadOnly FontSizeProperty As DependencyProperty
    Public Shared ReadOnly FontStretchProperty As DependencyProperty
    Public Shared ReadOnly FontStyleProperty As DependencyProperty
    Public Shared ReadOnly FontWeightProperty As DependencyProperty
    Public Shared ReadOnly ForegroundProperty As DependencyProperty
    Public Shared ReadOnly HorizontalContentAlignmentProperty As DependencyProperty
    Public Shared ReadOnly PaddingProperty As DependencyProperty
    Public Shared ReadOnly TextAlignmentProperty As DependencyProperty
    Public Shared ReadOnly TextDecorationsProperty As DependencyProperty
    Public Shared ReadOnly TextWrappingProperty As DependencyProperty
    Public Shared ReadOnly VerticalContentAlignmentProperty As DependencyProperty

    Public Background As Brush
    Public BorderBrush As Brush
    Public BorderThickness As Thickness
    Public Content As Object
    Public ContentTemplate As DataTemplate
    Public FontFamily As FontFamily
    Public FontSize As Double
    Public FontStretch As FontStretch
    Public FontStyle As FontStyle
    Public FontWeight As FontWeight
    Public Foreground As Brush
    Public HorizontalContentAlignment As HorizontalAlignment
    Public Padding As Thickness
    Public TextAlignment As TextAlignment
    Public TextDecorations As TextDecorationCollection
    Public TextWrapping As TextWrapping
    Public VerticalContentAlignment As VerticalAlignment
End Class

При создании ControlTemplateпроще всего начать с существующего ControlTemplate и внести в него изменения.When creating a ControlTemplate, it is often easiest to begin with an existing ControlTemplate and make changes to it. Для изменения существующего ControlTemplateможно выполнить одно из следующих действий.You can do one of the following to change an existing ControlTemplate:

Полный примерComplete Example

В следующем примере показан полный ButtonControlTemplate, который рассматривается в этом разделе.The following example shows the complete ButtonControlTemplate that is discussed in this topic.

<StackPanel>
  <StackPanel.Resources>
    <Style TargetType="Button" x:Key="newTemplate">
      <!--Set the Background, Foreground, FontSize, Width, 
                  Height, Margin, and Template properties for
                  the Button.-->
      <Setter Property="Background" Value="Navy"/>
      <Setter Property="Foreground" Value="White"/>
      <Setter Property="FontSize" Value="14"/>
      <Setter Property="Width" Value="100"/>
      <Setter Property="Height" Value="40"/>
      <Setter Property="Margin" Value="10"/>
      <Setter Property="HorizontalContentAlignment" Value="Center"/>
      <Setter Property="VerticalContentAlignment" Value="Center"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="Button">
            <Border x:Name="RootElement">
              <VisualStateManager.VisualStateGroups>

                <!--Define the states and transitions for the common states.
                    The states in the VisualStateGroup are mutually exclusive to
                    each other.-->
                <VisualStateGroup Name="CommonStates">

                  <!--Define the VisualTransitions that can be used when the control
                      transitions between VisualStates that are defined in the
                      VisualStatGroup.-->
                  <VisualStateGroup.Transitions>

                    <!--Take one hundredth of a second to transition to the
                        Pressed state.-->
                    <VisualTransition To="Pressed" 
                                      GeneratedDuration="0:0:0.01" />

                    <!--Take one half second to trasition to the MouseOver state.-->
                    <VisualTransition To="MouseOver" 
                                      GeneratedDuration="0:0:0.5" />

                    <!--Take one hundredth of a second to transition from the
                        Pressed state to the MouseOver state.-->
                    <VisualTransition From="Pressed" To="MouseOver" 
                                      GeneratedDuration="0:0:0.01" />

                    <!--Take one and a half seconds to transition from the
                        MouseOver state to the Normal state. 
                        Have the SolidColorBrush, BorderBrush, fade to blue, 
                        then to yellow, and then to black in that time.-->
                    <VisualTransition From="MouseOver" To="Normal" 
                                          GeneratedDuration="0:0:1.5">
                      <Storyboard>
                        <ColorAnimationUsingKeyFrames
                          Storyboard.TargetProperty="Color"
                          Storyboard.TargetName="BorderBrush"
                          FillBehavior="HoldEnd" >

                          <ColorAnimationUsingKeyFrames.KeyFrames>

                            <LinearColorKeyFrame Value="Blue" 
                              KeyTime="0:0:0.5" />
                            <LinearColorKeyFrame Value="Yellow" 
                              KeyTime="0:0:1" />
                            <LinearColorKeyFrame Value="Black" 
                              KeyTime="0:0:1.5" />

                          </ColorAnimationUsingKeyFrames.KeyFrames>
                        </ColorAnimationUsingKeyFrames>
                      </Storyboard>
                    </VisualTransition>
                  </VisualStateGroup.Transitions>

                  <!--The Normal state is the state the button is in
                      when it is not in another state from this VisualStateGroup.
                      There is no special visual behavior for this state, but
                      the VisualState must be defined in order for the button
                      to return to its initial state.-->
                  <VisualState x:Name="Normal" />

                  <!--Change the border of the button to red when the
                      mouse is over the button.-->
                  <VisualState x:Name="MouseOver">
                    <Storyboard>
                      <ColorAnimation Storyboard.TargetName="BorderBrush"     
                                      Storyboard.TargetProperty="Color"
                                      To="Red" />

                    </Storyboard>
                  </VisualState>

                  <!--Change the border of the button to Transparent when the
                      button is pressed.-->
                  <VisualState x:Name="Pressed">
                    <Storyboard >
                      <ColorAnimation Storyboard.TargetName="BorderBrush" 
                                      Storyboard.TargetProperty="Color" 
                                      To="Transparent" 
                                      />
                    </Storyboard>
                  </VisualState>

                  <!--Show the DisabledRect when the IsEnabled property on
                      the button is false.-->
                  <VisualState x:Name="Disabled">
                    <Storyboard>
                      <DoubleAnimation Storyboard.TargetName="DisabledRect" 
                                       Storyboard.TargetProperty="Opacity"
                                       To="1" Duration="0" />
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>

                <!--Define the states and transitions for the focus states.
                    The states in the VisualStateGroup are mutually exclusive to
                    each other.-->
                <VisualStateGroup x:Name="FocusStates">

                  <!--Define the VisualStates in this VistualStateGroup.-->
                  <VisualState x:Name="Focused">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames 
                        Storyboard.TargetName="FocusVisual" 
                        Storyboard.TargetProperty="Visibility" Duration
                        ="0">
                        
                        <DiscreteObjectKeyFrame KeyTime="0">
                          <DiscreteObjectKeyFrame.Value>
                            <Visibility>Visible</Visibility>
                          </DiscreteObjectKeyFrame.Value>
                        </DiscreteObjectKeyFrame>
                      </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>
                  <VisualState x:Name="Unfocused">
                    <Storyboard>
                      <ObjectAnimationUsingKeyFrames 
                        Storyboard.TargetName="FocusVisual" 
                        Storyboard.TargetProperty="Visibility"
                        Duration="0">
                        
                        <DiscreteObjectKeyFrame KeyTime="0">
                          <DiscreteObjectKeyFrame.Value>
                            <Visibility>Collapsed</Visibility>
                          </DiscreteObjectKeyFrame.Value>
                        </DiscreteObjectKeyFrame>
                      </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>
              </VisualStateManager.VisualStateGroups>

              <!--Create the SolidColorBrush for the Background 
                  as an object elemment and give it a name so 
                  it can be referred to elsewhere in the control template.-->
              <Border.Background>
                <SolidColorBrush x:Name="BorderBrush" Color="Black"/>
              </Border.Background>

              <!--Create a border that has a different color by adding smaller grid.
                  The background of this grid is specified by the button's Background
                  property.-->
              <Grid Background="{TemplateBinding Background}" Margin="4">

                <!--Create a Rectangle that indicates that the
                    Button has focus.-->
                <Rectangle Name="FocusVisual" 
                           Visibility="Collapsed" Margin="2" 
                           Stroke="{TemplateBinding Foreground}" 
                           StrokeThickness="1" 
                           StrokeDashArray="1.5 1.5"/>

                <!--Use a ContentPresenter to display the Content of
                    the Button.-->
                <ContentPresenter
                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                  Margin="4,5,4,4" />

                <!--Create a rectangle that causes the button to appear
                    grayed out when it is disabled.-->
                <Rectangle x:Name="DisabledRect" 
                         Fill="#A5FFFFFF"
                         Opacity="0" IsHitTestVisible="false" />
              </Grid>
            </Border>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>

  </StackPanel.Resources>

  <Button Style="{StaticResource newTemplate}" 
          Content="Button1"/>

  <Button Style="{StaticResource newTemplate}"
          Background="Purple" 
          Content="Button2" />
</StackPanel>

См. такжеSee also