Пошаговое руководство. Создание кнопки с помощью XAML
Это пошаговое руководство предназначено для изучения того, как с помощью XAML можно создать анимированную кнопку для использования в приложении Windows Presentation Foundation. В этом пошаговом руководстве для создания ресурса настраиваемой кнопки используются стили и шаблон, что обеспечивает повторное использование кода и разделение логики кнопки от объявления кнопки. Код в этом пошаговом руководстве полностью написан на языке XAML.
Внимание
В этом пошаговом руководстве показано, как можно создать приложение путем ввода или копирования и вставки кода на языке XAML в Visual Studio. Если вы хотите узнать, как для создания того же приложения использовать конструктор, см. статью Создание кнопки с помощью Microsoft Expression Blend.
На следующем рисунке показаны готовые кнопки.
Создание основных кнопок
Начнем с создания проекта и добавления нескольких кнопок в окно.
Создание проекта WPF и добавление кнопок в окно
Запустите Visual Studio.
Создайте новый проект WPF: в меню Файл нажмите сначала пункт Создать, а затем Проект. Найдите шаблон Приложение Windows (WPF) и назначьте проекту имя «AnimatedButton». В результате будет создана основная структура для приложения.
Добавьте основные кнопки по умолчанию: Все файлы, необходимые для этого пошагового руководства, предоставляются шаблоном. Откройте файл Window1.xaml, дважды щелкнув его в Обозревателе решений. По умолчанию в файле Window1.xaml есть элемент Grid. Удалите элемент Grid и добавьте несколько кнопок на страницу XAML, введя или вставив следующий фрагмент кода в файл Window1.xaml:
<Window x:Class="AnimatedButton.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="AnimatedButton" Height="300" Width="300" Background="Black"> <!-- Buttons arranged vertically inside a StackPanel. --> <StackPanel HorizontalAlignment="Left"> <Button>Button 1</Button> <Button>Button 2</Button> <Button>Button 3</Button> </StackPanel> </Window>
Нажмите клавишу F5, чтобы запустить приложение; вы увидите набор кнопок, как это представлено на следующем рисунке.
Теперь, после создания основных кнопок, можно завершить работу с файлом Window1.xaml. Дальше в этом пошаговом руководстве основное внимание уделяется файлу app.xaml, в котором определяются стили и шаблон кнопок.
Задание основных свойств
Давайте настроим некоторые свойств этих кнопок, чтобы осуществить управление их внешним видом и макетом. Вместо того, чтобы настраивать свойства кнопок по отдельности, нужно использовать ресурсы для определения свойств кнопок во всем приложении. Ресурсы приложений концептуально похожи на внешние каскадные таблицы стилей (CSS) для веб-страниц; однако ресурсы намного эффективней каскадных таблицы стилей (CSS), в чем можно будет убедиться в конце этого пошагового руководства. Дополнительные сведения о ресурсах см. в разделе Ресурсы XAML.
Использование стилей для задания основных свойств кнопок
Определите блок Application.Resources: откройте файл app.xaml и добавьте следующий выделенный фрагмент разметки, если он еще отсутствует в этом файле:
<Application x:Class="AnimatedButton.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Window1.xaml" > <Application.Resources> <!-- Resources for the entire application can be defined here. --> </Application.Resources> </Application>
Область ресурса определяется тем, где определяется ресурс. Определение ресурсов в
Application.Resources
в файле app.xaml позволяет использовать ресурсы в любом месте приложения. Дополнительные сведения об определении области ресурсов см. в статье Ресурсы XAML.Создайте стиль и определите с его помощью базовые значения свойств: добавьте следующую разметку в блок
Application.Resources
. Эта разметка создает объект Style, который применяется ко всем кнопкам в приложении и задает для свойства кнопок Width значение 90 и для свойства Margin значение 10:<Application.Resources> <Style TargetType="Button"> <Setter Property="Width" Value="90" /> <Setter Property="Margin" Value="10" /> </Style> </Application.Resources>
Свойство TargetType определяет то, что стиль применяется ко всем объектам типа Button. Каждый объект Setter задает разные значения свойств для объекта Style. Таким образом, на этом этапе каждая кнопка в приложении имеет ширину величиной 90 и отступ величиной 10. Если нажать клавишу F5 для запуска приложения, отобразится следующее окно.
Существует намного больше возможностей по работе со стилями, включая различные способы точной настройки целевых объектов, указание сложных значений свойств и даже использование стилей в качестве входных данных для других стилей. Более подробную информацию см. в разделе Стилизация и использование шаблонов.
Задайте для ресурса значение свойства стиля: ресурсы позволяют простым образом использовать часто определяемые объекты и значения. Особенно эффективным является определение с помощью ресурсов сложных значений для того, чтобы сделать код более модульным. Добавьте следующий выделенный фрагмент с разметкой в файл app.xaml.
<Application.Resources> <LinearGradientBrush x:Key="GrayBlueGradientBrush" StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="DarkGray" Offset="0" /> <GradientStop Color="#CCCCFF" Offset="0.5" /> <GradientStop Color="DarkGray" Offset="1" /> </LinearGradientBrush> <Style TargetType="{x:Type Button}"> <Setter Property="Background" Value="{StaticResource GrayBlueGradientBrush}" /> <Setter Property="Width" Value="80" /> <Setter Property="Margin" Value="10" /> </Style> </Application.Resources>
Непосредственно под блоком
Application.Resources
вы создали ресурс с именем GrayBlueGradientBrush. Этот ресурс определяет горизонтальный градиент. Этот ресурс можно использовать в качестве значения свойства из любого места в приложении, включая метод задания стиля кнопки для свойства Background. Теперь у всех кнопок имеется значение свойства Background как у этого градиента.Нажмите клавишу F5 для запуска приложения. Это должно выглядеть примерно так.
Создание шаблона, определяющего внешний вид кнопки
В этом разделе создается шаблон, который настраивает внешний вид (представление) кнопки. Внешний вид кнопки состоит из нескольких объектов, включая прямоугольники и другие компоненты, позволяющие придать кнопке уникальный вид.
До сих пор управление тем, как кнопки выглядят в приложении, ограничивалось изменением свойств кнопки. А что если нужно внести более радикальные изменения во внешний вид кнопки? Шаблоны предоставляют более широкие возможности по управлению внешним видом объекта. Так как шаблоны можно использовать в стилях, шаблоны применимы ко всем объектам, к которым применяется стиль (в этом пошаговом руководстве это кнопка).
Использование шаблона для определения внешнего вида кнопки
Настройте шаблон: вследствие того, что у таких элементов управления, как Button имеется свойство Template, можно определить значение свойства шаблона также, как другие значения свойств, которые задавались ранее в объекте Style с помощью объекта Setter. Добавьте следующий выделенный фрагмент с разметкой в стиль кнопки.
<Application.Resources> <LinearGradientBrush x:Key="GrayBlueGradientBrush" StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="DarkGray" Offset="0" /> <GradientStop Color="#CCCCFF" Offset="0.5" /> <GradientStop Color="DarkGray" Offset="1" /> </LinearGradientBrush> <Style TargetType="{x:Type Button}"> <Setter Property="Background" Value="{StaticResource GrayBlueGradientBrush}" /> <Setter Property="Width" Value="80" /> <Setter Property="Margin" Value="10" /> <Setter Property="Template"> <Setter.Value> <!-- The button template is defined here. --> </Setter.Value> </Setter> </Style> </Application.Resources>
Измените внешний вид кнопки: на этом этапе нужно определить шаблон. Добавьте следующий выделенный фрагмент с разметкой. В этой разметке задаются два элемента Rectangle с округленными краями, после которых задается объект DockPanel. Объект DockPanel используется для размещения объекта ContentPresenter кнопки. Объект ContentPresenter выполняет отображение содержимого кнопки. В этом пошаговом руководстве содержимое — это текст («Кнопка 1», «Кнопка 2», «Кнопка 3»). Все компоненты шаблона (прямоугольники и объект DockPanel) размещаются внутри объекта Grid.
<Setter.Value> <ControlTemplate TargetType="Button"> <Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" ClipToBounds="True"> <!-- Outer Rectangle with rounded corners. --> <Rectangle x:Name="outerRectangle" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stroke="{TemplateBinding Background}" RadiusX="20" RadiusY="20" StrokeThickness="5" Fill="Transparent" /> <!-- Inner Rectangle with rounded corners. --> <Rectangle x:Name="innerRectangle" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stroke="Transparent" StrokeThickness="20" Fill="{TemplateBinding Background}" RadiusX="20" RadiusY="20" /> <!-- Present Content (text) of the button. --> <DockPanel Name="myContentPresenterDockPanel"> <ContentPresenter x:Name="myContentPresenter" Margin="20" Content="{TemplateBinding Content}" TextBlock.Foreground="Black" /> </DockPanel> </Grid> </ControlTemplate> </Setter.Value>
Нажмите клавишу F5 для запуска приложения. Это должно выглядеть примерно так.
Добавьте в шаблон эффект стекла: далее нужно добавить эффект стекла. Сначала нужно создать некоторые ресурсы, которые воспроизводят эффект градиента как у стекла. Добавьте эти ресурсы градиента в любое место в блоке
Application.Resources
:<Application.Resources> <GradientStopCollection x:Key="MyGlassGradientStopsResource"> <GradientStop Color="WhiteSmoke" Offset="0.2" /> <GradientStop Color="Transparent" Offset="0.4" /> <GradientStop Color="WhiteSmoke" Offset="0.5" /> <GradientStop Color="Transparent" Offset="0.75" /> <GradientStop Color="WhiteSmoke" Offset="0.9" /> <GradientStop Color="Transparent" Offset="1" /> </GradientStopCollection> <LinearGradientBrush x:Key="MyGlassBrushResource" StartPoint="0,0" EndPoint="1,1" Opacity="0.75" GradientStops="{StaticResource MyGlassGradientStopsResource}" /> <!-- Styles and other resources below here. -->
Эти ресурсы используются как свойство Fill для прямоугольника, который вставляется в объект Grid из шаблона кнопки. Добавьте в шаблон следующий выделенный фрагмент с разметкой.
<Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" ClipToBounds="True"> <!-- Outer Rectangle with rounded corners. --> <Rectangle x:Name="outerRectangle" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stroke="{TemplateBinding Background}" RadiusX="20" RadiusY="20" StrokeThickness="5" Fill="Transparent" /> <!-- Inner Rectangle with rounded corners. --> <Rectangle x:Name="innerRectangle" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stroke="Transparent" StrokeThickness="20" Fill="{TemplateBinding Background}" RadiusX="20" RadiusY="20" /> <!-- Glass Rectangle --> <Rectangle x:Name="glassCube" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" StrokeThickness="2" RadiusX="10" RadiusY="10" Opacity="0" Fill="{StaticResource MyGlassBrushResource}" RenderTransformOrigin="0.5,0.5"> <Rectangle.Stroke> <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> <LinearGradientBrush.GradientStops> <GradientStop Offset="0.0" Color="LightBlue" /> <GradientStop Offset="1.0" Color="Gray" /> </LinearGradientBrush.GradientStops> </LinearGradientBrush> </Rectangle.Stroke> <!-- These transforms have no effect as they are declared here. The reason the transforms are included is to be targets for animation (see later). --> <Rectangle.RenderTransform> <TransformGroup> <ScaleTransform /> <RotateTransform /> </TransformGroup> </Rectangle.RenderTransform> <!-- A BevelBitmapEffect is applied to give the button a "Beveled" look. --> <Rectangle.BitmapEffect> <BevelBitmapEffect /> </Rectangle.BitmapEffect> </Rectangle> <!-- Present Text of the button. --> <DockPanel Name="myContentPresenterDockPanel"> <ContentPresenter x:Name="myContentPresenter" Margin="20" Content="{TemplateBinding Content}" TextBlock.Foreground="Black" /> </DockPanel> </Grid> </ControlTemplate> </Setter.Value>
Обратите внимание, что свойство Opacity прямоугольника (у которого помимо этого есть свойство
x:Name
равное «glassCube») имеет значение 0, поэтому при запуске этого примера не отображается «стеклянный» прямоугольник, наложенный сверху. Это сделано для того, чтобы позже добавить в шаблон триггеры для реализации взаимодействия пользователя с кнопкой. Однако можно посмотреть, как выглядит кнопка сейчас, изменив значение Opacity на 1 и запустив приложение. См. следующий рисунок. Прежде чем переходить к следующему шагу, измените значение Opacity обратно на 0.
Создание интерактивности для кнопки
В этом разделе создаются триггеры свойств и триггеры событий, выполняющие изменение значений свойств и запуск анимации в ответ на действия пользователя, такие как перемещение указателя мыши на кнопку и нажатие кнопки.
Простым способом для добавления интерактивности (при наведении указателя мыши, выходе указателя мыши, нажатии кнопки мыши и т. д.) является определение триггеров в шаблоне или стиле. Чтобы создать Trigger, нужно определить условие для свойства, например: значение свойства IsMouseOver у кнопки равняется true
. Затем нужно определить методы задания (действия), которые выполняются, когда условие триггера имеет значение «true».
Создание интерактивности для кнопки
Добавьте в шаблон триггеры: добавьте выделенный фрагмент с разметкой в свой шаблон.
<Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" ClipToBounds="True"> <!-- Outer Rectangle with rounded corners. --> <Rectangle x:Name="outerRectangle" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stroke="{TemplateBinding Background}" RadiusX="20" RadiusY="20" StrokeThickness="5" Fill="Transparent" /> <!-- Inner Rectangle with rounded corners. --> <Rectangle x:Name="innerRectangle" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stroke="Transparent" StrokeThickness="20" Fill="{TemplateBinding Background}" RadiusX="20" RadiusY="20" /> <!-- Glass Rectangle --> <Rectangle x:Name="glassCube" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" StrokeThickness="2" RadiusX="10" RadiusY="10" Opacity="0" Fill="{StaticResource MyGlassBrushResource}" RenderTransformOrigin="0.5,0.5"> <Rectangle.Stroke> <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> <LinearGradientBrush.GradientStops> <GradientStop Offset="0.0" Color="LightBlue" /> <GradientStop Offset="1.0" Color="Gray" /> </LinearGradientBrush.GradientStops> </LinearGradientBrush> </Rectangle.Stroke> <!-- These transforms have no effect as they are declared here. The reason the transforms are included is to be targets for animation (see later). --> <Rectangle.RenderTransform> <TransformGroup> <ScaleTransform /> <RotateTransform /> </TransformGroup> </Rectangle.RenderTransform> <!-- A BevelBitmapEffect is applied to give the button a "Beveled" look. --> <Rectangle.BitmapEffect> <BevelBitmapEffect /> </Rectangle.BitmapEffect> </Rectangle> <!-- Present Text of the button. --> <DockPanel Name="myContentPresenterDockPanel"> <ContentPresenter x:Name="myContentPresenter" Margin="20" Content="{TemplateBinding Content}" TextBlock.Foreground="Black" /> </DockPanel> </Grid> <ControlTemplate.Triggers> <!-- Set action triggers for the buttons and define what the button does in response to those triggers. --> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value>
Добавьте триггеры свойств: добавьте выделенный фрагмент с разметкой в блок
ControlTemplate.Triggers
:<ControlTemplate.Triggers> <!-- Set properties when mouse pointer is over the button. --> <Trigger Property="IsMouseOver" Value="True"> <!-- Below are three property settings that occur when the condition is met (user mouses over button). --> <!-- Change the color of the outer rectangle when user mouses over it. --> <Setter Property ="Rectangle.Stroke" TargetName="outerRectangle" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" /> <!-- Sets the glass opacity to 1, therefore, the glass "appears" when user mouses over it. --> <Setter Property="Rectangle.Opacity" Value="1" TargetName="glassCube" /> <!-- Makes the text slightly blurry as though you were looking at it through blurry glass. --> <Setter Property="ContentPresenter.BitmapEffect" TargetName="myContentPresenter"> <Setter.Value> <BlurBitmapEffect Radius="1" /> </Setter.Value> </Setter> </Trigger> <ControlTemplate.Triggers/>
Нажмите клавишу F5, чтобы запустить приложение и посмотреть проявление эффекта при наведении указателя мыши на кнопку.
Добавьте триггер фокуса: далее можно добавить аналогичные методы задания для обработки ситуации, когда кнопка получает фокус (например, после ее нажатия пользователем).
<ControlTemplate.Triggers> <!-- Set properties when mouse pointer is over the button. --> <Trigger Property="IsMouseOver" Value="True"> <!-- Below are three property settings that occur when the condition is met (user mouses over button). --> <!-- Change the color of the outer rectangle when user mouses over it. --> <Setter Property ="Rectangle.Stroke" TargetName="outerRectangle" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" /> <!-- Sets the glass opacity to 1, therefore, the glass "appears" when user mouses over it. --> <Setter Property="Rectangle.Opacity" Value="1" TargetName="glassCube" /> <!-- Makes the text slightly blurry as though you were looking at it through blurry glass. --> <Setter Property="ContentPresenter.BitmapEffect" TargetName="myContentPresenter"> <Setter.Value> <BlurBitmapEffect Radius="1" /> </Setter.Value> </Setter> </Trigger> <!-- Set properties when button has focus. --> <Trigger Property="IsFocused" Value="true"> <Setter Property="Rectangle.Opacity" Value="1" TargetName="glassCube" /> <Setter Property="Rectangle.Stroke" TargetName="outerRectangle" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" /> <Setter Property="Rectangle.Opacity" Value="1" TargetName="glassCube" /> </Trigger> </ControlTemplate.Triggers>
Нажмите клавишу F5, чтобы запустить приложение, а затем нажмите одну из кнопок. Обратите внимание, что кнопка остается выделенной после нажатия кнопки, так как на ней по-прежнему сохраняется фокус. Если нажать другую кнопку, то фокус получит эта новая кнопка, а прежняя кнопка его потеряет.
Добавьте анимацию дляMouseEnter триггеров и MouseLeave : далее мы добавим некоторые анимации в триггеры. Добавьте следующую разметку в любое место блока
ControlTemplate.Triggers
.<!-- Animations that start when mouse enters and leaves button. --> <EventTrigger RoutedEvent="Mouse.MouseEnter"> <EventTrigger.Actions> <BeginStoryboard Name="mouseEnterBeginStoryboard"> <Storyboard> <!-- This animation makes the glass rectangle shrink in the X direction. --> <DoubleAnimation Storyboard.TargetName="glassCube" Storyboard.TargetProperty= "(Rectangle.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" By="-0.1" Duration="0:0:0.5" /> <!-- This animation makes the glass rectangle shrink in the Y direction. --> <DoubleAnimation Storyboard.TargetName="glassCube" Storyboard.TargetProperty= "(Rectangle.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" By="-0.1" Duration="0:0:0.5" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> <EventTrigger RoutedEvent="Mouse.MouseLeave"> <EventTrigger.Actions> <!-- Stopping the storyboard sets all animated properties back to default. --> <StopStoryboard BeginStoryboardName="mouseEnterBeginStoryboard" /> </EventTrigger.Actions> </EventTrigger>
«Стеклянный» прямоугольник сокращается в размере, когда указатель мыши перемещается над кнопкой и возвращается к нормальному размеру при выходе указателя мыши из области кнопки.
Здесь есть две анимации, которые активируются, когда указатель мыши находится над кнопкой (вызывается событие MouseEnter). В этих анимациях «стеклянный» прямоугольник уменьшается в размерах по осям X и Y. Обратите внимание на свойства у элементов DoubleAnimation — Duration и By. Свойство Duration указывает, что анимация выполняется в течение половины секунды, а свойство By указывает, что «стеклянный» слой сжимается на 10 %.
Второй триггер события (MouseLeave) просто останавливает первый. При остановке объекта Storyboard все анимированные свойства возвращаются к значениям по умолчанию. Таким образом, когда пользователь убирает указатель с кнопки, кнопка возвращается к тому виду, который у нее был до перемещения указателя мыши на кнопку. Дополнительные сведения об анимации см. в разделе Общие сведения об эффектах анимации.
Добавьте анимацию при нажатии кнопки: в качестве последнего шага добавим триггер для случая, когда пользователь нажимает кнопку. Добавьте следующую разметку в любое место блока
ControlTemplate.Triggers
:<!-- Animation fires when button is clicked, causing glass to spin. --> <EventTrigger RoutedEvent="Button.Click"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetName="glassCube" Storyboard.TargetProperty= "(Rectangle.RenderTransform).(TransformGroup.Children)[1].(RotateTransform.Angle)" By="360" Duration="0:0:0.5" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger>
Нажмите клавишу F5, чтобы запустить приложение, а затем нажмите одну из кнопок. При нажатии кнопки «стеклянный» прямоугольник вращается.
Итоги
В этом пошаговом руководстве были выполнены следующие практические задания:
Создание объекта Style в соответствии с целевым типом объекта (Button).
Управление основными свойствами кнопок во всем приложении с помощью объекта Style.
Создание таких ресурсов, как градиенты, используемые в соответствии со значениями свойств из методов задания Style.
Настройка внешнего вида кнопок во всем приложении путем применения к кнопкам шаблона.
Настройка поведения кнопок в ответ на действия пользователя (такие как MouseEnter, MouseLeave и Click) с эффектами анимации.
См. также
.NET Desktop feedback