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


Общие сведения о Storyboard

В этом разделе показано использование объектов Storyboard для организации и применения анимации. В нем описывается, как интерактивно использовать объекты Storyboard, а также синтаксис косвенного выбора свойств.

Предварительные требования

Чтобы разобраться в этом разделе, необходимо ознакомиться с различными типами анимации и их основными характеристиками. Введение в анимацию см. в разделе Общие сведения об эффектах анимации. Следует также знать, как использовать вложенные свойства зависимостей. Дополнительные сведения о вложенных свойствах зависимостей см. в разделе Общие сведения о вложенных свойствах зависимостей.

Что такое раскадровка?

Анимация является не единственным полезным типом шкалы времени. Другие классы шкал времени так же используются для организации наборов шкал времени и их применения к свойствам. Шкалы времени контейнера являются производными от класса TimelineGroup и включают ParallelTimeline и Storyboard.

Storyboard является типом шкалы времени контейнера, который содержит сведения о содержащихся в нем шкалах времени. Раскадровка может содержать тип Timeline, включая другие временные шкалы контейнера и анимации. Объекты Storyboard позволяют объединить временные шкалы, влияющие на различные объекты и свойства, в одно дерево временной шкалы, упрощая организацию и управление сложным поведением времени. Предположим, требуется кнопка, которая выполняет следующие три действия.

  • Увеличивается и изменяет цвет, когда пользователь выбирает кнопку.

  • Сжимается, а затем увеличивается до исходного размера при нажатии.

  • Сжимается и переходит в состояние 50-процентной прозрачности при отключении.

В этом случае имеется несколько наборов анимаций, которые применяются к одному объекту и которые требуется воспроизвести в разное время в зависимости от состояния кнопки. Объекты Storyboard позволяют организовывать анимации в группы и применять к одному или нескольким объектам.

Где можно использовать Storyboard?

Storyboard можно использовать для анимации свойств зависимости анимируемых классов (дополнительные сведения о том, что делает класс анимируемым, см. в разделе Общие сведения об эффектах анимации). Однако поскольку раскадровка является средством уровня структуры, объект должен относиться к NameScope FrameworkElement или FrameworkContentElement.

Например, Storyboard можно использовать для:

  • анимации SolidColorBrush (не является элементом структуры), которая рисует фон кнопок (тип FrameworkElement),

  • анимации SolidColorBrush (не является элементом структуры), которая рисует заливку GeometryDrawing (не является элементом структуры), которая отображается с помощью Image (FrameworkElement).

  • В коде анимация SolidColorBrush объявляется с помощью класса, который также содержит FrameworkElement, если SolidColorBrush зарегистрировал его имя с этим FrameworkElement.

Однако, нельзя использовать Storyboard для анимации SolidColorBrush, имя которого не зарегистрировано в качестве FrameworkElement или FrameworkContentElement, или не было использовано для задания свойства FrameworkElement или FrameworkContentElement.

Как применить анимацию с использованием Storyboard

Чтобы использовать Storyboard для организации и применения анимации, следует добавить анимацию в качестве дочерней шкалы времени для Storyboard. Класс Storyboard.TargetName содержит вложенные свойства Storyboard и Storyboard.TargetProperty. Эти свойства задаются для анимации, чтобы указать ее целевой объект и свойство.

Чтобы применить анимацию к целевому объекту, следует начать Storyboard с помощью действия триггера или метода. XAML использует объект BeginStoryboard с EventTrigger, Trigger или DataTrigger. В коде можно также использовать метод Begin.

В следующей таблице приведены различные способы запуска Storyboard: для каждого экземпляра, стиля, шаблона элемента управления и шаблона данных. " "Для каждого экземпляра" относится к способу применения анимации или раскадровки непосредственно к экземплярам объекта, а не к стилю, шаблону элемента управления или шаблону данных.

Запуск раскадровки при помощи…

«Для каждого экземпляра»

Стиль

Шаблон элемента управления

Шаблон данных

Пример

BeginStoryboard и EventTrigger

Да

Да

Да

Да

Практическое руководство. Анимирование свойства с помощью раскадровки (класс Storyboard)

BeginStoryboard и свойство Trigger

Нет

Да

Да

Да

Практическое руководство. Запуск анимации при изменении значения свойства

BeginStoryboard и DataTrigger

Нет

Да

Да

Да

Практическое руководство. Запуск анимации при изменении данных

Метод Begin

Да

Нет

Нет

Нет

Практическое руководство. Анимирование свойства с помощью раскадровки (класс Storyboard)

В следующем примере Storyboard используется для анимации Width элемента Rectangle и Color для SolidColorBrush, который используется для рисования Rectangle.

<!-- This example shows how to animate with a storyboard.-->
<Page xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="Microsoft.Samples.Animation.StoryboardsExample" 
  WindowTitle="Storyboards Example">
  <StackPanel Margin="20">

    <Rectangle Name="MyRectangle"
      Width="100"
      Height="100">
      <Rectangle.Fill>
        <SolidColorBrush x:Name="MySolidColorBrush" Color="Blue" />
      </Rectangle.Fill>
      <Rectangle.Triggers>
        <EventTrigger RoutedEvent="Rectangle.MouseEnter">
          <BeginStoryboard>
            <Storyboard>
              <DoubleAnimation 
                Storyboard.TargetName="MyRectangle"
                Storyboard.TargetProperty="Width"
                From="100" To="200" Duration="0:0:1" />

              <ColorAnimation 
                Storyboard.TargetName="MySolidColorBrush"
                Storyboard.TargetProperty="Color"
                From="Blue" To="Red" Duration="0:0:1" />  
            </Storyboard>
          </BeginStoryboard>
        </EventTrigger>
      </Rectangle.Triggers>
    </Rectangle> 
  </StackPanel>
</Page>

Imports System
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Media
Imports System.Windows.Media.Animation
Imports System.Windows.Data
Imports System.Windows.Shapes
Imports System.Windows.Input


Namespace Microsoft.Samples.Animation
    Public Class StoryboardsExample
        Inherits Page
        Public Sub New()
            Me.WindowTitle = "Storyboards Example"
            Dim myStackPanel As New StackPanel()
            myStackPanel.Margin = New Thickness(20)

            Dim myRectangle As New Rectangle()
            myRectangle.Name = "MyRectangle"

            ' Create a name scope for the page.
            NameScope.SetNameScope(Me, New NameScope())

            Me.RegisterName(myRectangle.Name, myRectangle)
            myRectangle.Width = 100
            myRectangle.Height = 100
            Dim mySolidColorBrush As New SolidColorBrush(Colors.Blue)
            Me.RegisterName("MySolidColorBrush", mySolidColorBrush)
            myRectangle.Fill = mySolidColorBrush

            Dim myDoubleAnimation As New DoubleAnimation()
            myDoubleAnimation.From = 100
            myDoubleAnimation.To = 200
            myDoubleAnimation.Duration = New Duration(TimeSpan.FromSeconds(1))
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name)
            Storyboard.SetTargetProperty(myDoubleAnimation, New PropertyPath(Rectangle.WidthProperty))

            Dim myColorAnimation As New ColorAnimation()
            myColorAnimation.From = Colors.Blue
            myColorAnimation.To = Colors.Red
            myColorAnimation.Duration = New Duration(TimeSpan.FromSeconds(1))
            Storyboard.SetTargetName(myColorAnimation, "MySolidColorBrush")
            Storyboard.SetTargetProperty(myColorAnimation, New PropertyPath(SolidColorBrush.ColorProperty))
            Dim myStoryboard As New Storyboard()
            myStoryboard.Children.Add(myDoubleAnimation)
            myStoryboard.Children.Add(myColorAnimation)

            AddHandler myRectangle.MouseEnter, Sub(sender As Object, e As MouseEventArgs) myStoryboard.Begin(Me)

            myStackPanel.Children.Add(myRectangle)
            Me.Content = myStackPanel
        End Sub
    End Class
End Namespace
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Data;
using System.Windows.Shapes;
using System.Windows.Input;


namespace Microsoft.Samples.Animation
{
    public class StoryboardsExample : Page
    {      
        public StoryboardsExample()
        {
            this.WindowTitle = "Storyboards Example";
            StackPanel myStackPanel = new StackPanel();
            myStackPanel.Margin = new Thickness(20);

            Rectangle myRectangle = new Rectangle();
            myRectangle.Name = "MyRectangle";

            // Create a name scope for the page.
            NameScope.SetNameScope(this, new NameScope());            

            this.RegisterName(myRectangle.Name, myRectangle);
            myRectangle.Width = 100;
            myRectangle.Height = 100;
            SolidColorBrush mySolidColorBrush = new SolidColorBrush(Colors.Blue);
            this.RegisterName("MySolidColorBrush", mySolidColorBrush);
            myRectangle.Fill = mySolidColorBrush;

            DoubleAnimation myDoubleAnimation = new DoubleAnimation();
            myDoubleAnimation.From = 100;
            myDoubleAnimation.To = 200;
            myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name);
            Storyboard.SetTargetProperty(myDoubleAnimation, 
                new PropertyPath(Rectangle.WidthProperty));

            ColorAnimation myColorAnimation = new ColorAnimation();
            myColorAnimation.From = Colors.Blue;
            myColorAnimation.To = Colors.Red;
            myColorAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
            Storyboard.SetTargetName(myColorAnimation, "MySolidColorBrush");
            Storyboard.SetTargetProperty(myColorAnimation, 
                new PropertyPath(SolidColorBrush.ColorProperty)); 
            Storyboard myStoryboard = new Storyboard();
            myStoryboard.Children.Add(myDoubleAnimation);
            myStoryboard.Children.Add(myColorAnimation);

            myRectangle.MouseEnter += delegate(object sender, MouseEventArgs e)
            {
                myStoryboard.Begin(this);
            };

            myStackPanel.Children.Add(myRectangle);
            this.Content = myStackPanel;
        } 
    }
}

В следующих разделах более подробно описываются вложенные свойства TargetName и TargetProperty.

Выбор элементов Framework, элементов содержимого Framework и элементов Freezable

В предыдущем разделе упоминалось, что для поиска целевого объекта, анимация должна знать его имя и анимируемое свойство. Указание свойства для анимации: просто задайте в Storyboard.TargetProperty имя анимируемого свойства. Укажите имя объекта, свойство которого требуется анимировать, задав для свойства Storyboard.TargetName значение анимации.

Для работы свойства TargetName целевой объект должен иметь имя. Присвоение имени объекту FrameworkElement или FrameworkContentElement в XAML отличается от присвоения имени объекту Freezable.

Элементами Framework являются те классы, которые наследуют от класса FrameworkElement. К элементам Framework относятся, например, Window, DockPanel, Button и Rectangle. По сути к ним относятся все окна, панели и элементы управления. Элементами содержимого Framework являются те классы, которые наследуют от класса FrameworkContentElement. К элементам содержимого Framework относятся FlowDocument и Paragraph. Если нет уверенности, является тип элементом Framework или элементом содержимого Framework, следует проверить наличие у него свойства Name. Если имеется свойство Name, вероятно, что это элемент Framework или элемент содержимого Framework. Чтобы убедиться в этом, следует проверить раздел иерархии наследования его страницы типа.

Чтобы включить заданный элемент Framework или элемент содержимого Framework в XAML, следует задать его свойство Name. В коде также необходимо использовать метод RegisterName для регистрации имени элемента с элементом, для которого был создан NameScope.

В следующем примере, взятом из предыдущего примера, присваивается имя MyRectangle для Rectangle, типа FrameworkElement.

<Rectangle Name="MyRectangle"
  Width="100"
  Height="100">
            Dim myRectangle As New Rectangle()
            myRectangle.Name = "MyRectangle"

            ' Create a name scope for the page.
            NameScope.SetNameScope(Me, New NameScope())

            Me.RegisterName(myRectangle.Name, myRectangle)
Rectangle myRectangle = new Rectangle();
myRectangle.Name = "MyRectangle";

// Create a name scope for the page.
NameScope.SetNameScope(this, new NameScope());            

this.RegisterName(myRectangle.Name, myRectangle);

После того, как элемент получит имя, можно анимировать его свойство.

<DoubleAnimation 
  Storyboard.TargetName="MyRectangle"
  Storyboard.TargetProperty="Width"
  From="100" To="200" Duration="0:0:1" />
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name)
            Storyboard.SetTargetProperty(myDoubleAnimation, New PropertyPath(Rectangle.WidthProperty))
Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name);
Storyboard.SetTargetProperty(myDoubleAnimation, 
    new PropertyPath(Rectangle.WidthProperty));

Типы Freezable являются классами, которые наследуют от класса Freezable. К примерам Freezable относятся SolidColorBrush, RotateTransform и GradientStop.

Чтобы включить настройку Freezable с помощью анимации в XAML, следует использовать Директива x:Name для назначения его имени. В коде используется метод RegisterName для регистрации его имени с элементом, для которого был создан NameScope.

В следующем примере присваивается имя для объекта Freezable.

<SolidColorBrush x:Name="MySolidColorBrush" Color="Blue" />
            Dim mySolidColorBrush As New SolidColorBrush(Colors.Blue)
            Me.RegisterName("MySolidColorBrush", mySolidColorBrush)
SolidColorBrush mySolidColorBrush = new SolidColorBrush(Colors.Blue);
this.RegisterName("MySolidColorBrush", mySolidColorBrush);

Затем объект может стать целевым объектом анимации.

<ColorAnimation 
  Storyboard.TargetName="MySolidColorBrush"
  Storyboard.TargetProperty="Color"
  From="Blue" To="Red" Duration="0:0:1" />  
            Storyboard.SetTargetName(myColorAnimation, "MySolidColorBrush")
            Storyboard.SetTargetProperty(myColorAnimation, New PropertyPath(SolidColorBrush.ColorProperty))
Storyboard.SetTargetName(myColorAnimation, "MySolidColorBrush");
Storyboard.SetTargetProperty(myColorAnimation, 
    new PropertyPath(SolidColorBrush.ColorProperty)); 

Объекты Storyboard используют области имен, чтобы разрешить свойство TargetName. Дополнительные сведения об области имен WPF см. в разделе Области видимости имен XAML в WPF. Если свойство TargetName опущено, анимация применяется к элементам, на которых она определена, или к элементам стиля.

Иногда имя не может быть присвоено объекту Freezable. Например, если Freezable объявлен как ресурс или используется для задания значения свойства в стиле, ему нельзя присвоить имя. Поскольку он не имеет имени, к нему нельзя обращаться непосредственно, но можно делать это косвенно. В следующих разделах объясняется, как использовать косвенные ссылки.

Косвенные ссылки

Бывают случаи, когда анимация не может ссылаться на Freezable напрямую, например, когда Freezable объявляется как ресурс или используется для задания значения свойства в стиле. В этих случаях объект Freezable все равно можно анимировать, даже при отсутствии возможности ссылаться на него напрямую. Вместо задания свойства TargetName с именем для Freezable присвойте ему имя элемента, к которому Freezable «принадлежит». Например, SolidColorBrush, используемый для задания Fill прямоугольника, «принадлежит» к этому прямоугольнику. Для анимации кисти следует задать элемент анимации TargetProperty с цепочкой свойств, которая начинается свойством элемента Framework или элемента содержимого Framework, установленного с помощью Freezable, и завершается анимируемым свойством Freezable.

<ColorAnimation 
  Storyboard.TargetName="Rectangle01"
  Storyboard.TargetProperty="Fill.Color"
  From="Blue" To="AliceBlue" Duration="0:0:1" />
            Dim propertyChain() As DependencyProperty = {Rectangle.FillProperty, SolidColorBrush.ColorProperty}
            Dim thePath As String = "(0).(1)"
            Dim myPropertyPath As New PropertyPath(thePath, propertyChain)
            Storyboard.SetTargetProperty(myColorAnimation, myPropertyPath)
DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {Rectangle.FillProperty, SolidColorBrush.ColorProperty};
string thePath = "(0).(1)";
PropertyPath myPropertyPath = new PropertyPath(thePath, propertyChain);
Storyboard.SetTargetProperty(myColorAnimation, myPropertyPath);

Обратите внимание, что если Freezable зафиксирован, будет создан клон и этот клон будет анимироваться. Если это происходит, свойство HasAnimatedProperties исходного объекта продолжает возвращать значение false, так как исходный объект не анимируется. Дополнительные сведения о клонировании см. в разделе Общие сведения об объектах класса Freezable.

Также обратите внимание, что использование косвенной ссылки на свойство позволяет ссылаться на несуществующий объект. Например, можно предположить, что Background конкретной кнопки был установлен с SolidColorBrush и пытается анимировать ее цвет, в то время как в действительности LinearGradientBrush используется для задания фона кнопки. В этих случаях исключение не вызывается. Анимация не имеет видимого эффекта, так как LinearGradientBrush не реагирует на изменение свойства Color.

В следующих разделах синтаксис косвенной ссылки на свойство описывается более подробно.

Косвенная ссылка на свойства Freezable в языке XAML

Чтобы обратиться к свойству Freezable в XAML, следует использовать следующий синтаксис.

ElementPropertyName.FreezablePropertyName

Where

  • ElementPropertyName является свойством FrameworkElement, которое задается с помощью Freezable, а

  • FreezablePropertyName является анимируемым свойством Freezable.

В следующем коде показано, как анимировать Color для SolidColorBrush, который используется для задания

Fill элемента прямоугольника.

<Rectangle
  Name="Rectangle01"
  Height="100"
  Width="100"
  Fill="{StaticResource MySolidColorBrushResource}">
  <Rectangle.Triggers>
    <EventTrigger RoutedEvent="Rectangle.MouseEnter">
      <BeginStoryboard>
        <Storyboard>
          <ColorAnimation 
            Storyboard.TargetName="Rectangle01"
            Storyboard.TargetProperty="Fill.Color"
            From="Blue" To="AliceBlue" Duration="0:0:1" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Rectangle.Triggers>
</Rectangle>

Иногда требуется обратиться к элементу Freezable, содержащемуся в коллекции или массиве.

Чтобы обратиться к элементу Freezable, содержащемуся в коллекции, следует использовать следующий синтаксис пути.

ElementPropertyName.Children[CollectionIndex].FreezablePropertyName

Где CollectionIndex — это индекс объекта в массиве или коллекции.

Например, предположим, что прямоугольник имеет ресурс TransformGroup, который применяется к его свойству RenderTransform, и требуется анимировать одно из содержащихся в нем преобразований.

<TransformGroup x:Key="MyTransformGroupResource"
  x:Shared="False">
  <ScaleTransform />
  <RotateTransform />
</TransformGroup>

В следующем коде демонстрируется анимация свойства Angle для RotateTransform, как показано в предыдущем примере.

<Rectangle
  Name="Rectangle02"
  Height="100"
  Width="100"
  Fill="Blue"
  RenderTransform="{StaticResource MyTransformGroupResource}">
  <Rectangle.Triggers>
    <EventTrigger RoutedEvent="Rectangle.MouseEnter">
      <BeginStoryboard>
        <Storyboard>
          <DoubleAnimation 
            Storyboard.TargetName="Rectangle02"
            Storyboard.TargetProperty="RenderTransform.Children[1].Angle"
            From="0" To="360" Duration="0:0:1" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Rectangle.Triggers>
</Rectangle>  

Косвенная ссылка на свойство элемента Freezable в коде

В коде можно создать объект PropertyPath. При создании PropertyPath, указывается Path и PathParameters.

Чтобы создать PathParameters, следует создать массив типа DependencyProperty, который содержит список полей идентификаторов свойств зависимостей. Первое поле идентификатора используется для свойства FrameworkElement или FrameworkContentElement, которое было задано с помощью Freezable. Следующее поле идентификатора представляет целевое свойство Freezable. Его следует представлять как цепочку свойств, которая соединяет Freezable и объект FrameworkElement.

Ниже приведен пример цепочки свойств зависимостей, которая обращается к Color для SolidColorBrush, который используется для задания Fill элемента прямоугольника.

            Dim propertyChain() As DependencyProperty = {Rectangle.FillProperty, SolidColorBrush.ColorProperty}
DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {Rectangle.FillProperty, SolidColorBrush.ColorProperty};

Кроме того, необходимо указать Path. Path является String, которая указывает Path, как интерпретировать его PathParameters. При этом используется следующий синтаксис.

(OwnerPropertyArrayIndex).(FreezablePropertyArrayIndex)

Where

  • OwnerPropertyArrayIndex — индекс массива DependencyProperty, который содержит идентификатор свойства объекта FrameworkElement, заданного с помощью Freezable, а

  • FreezablePropertyArrayIndex — индекс массива DependencyProperty, который содержит идентификатор целевого свойства.

В следующем примере показан Path, который будет сопровождать PathParameters, определенные в предыдущем примере.

            Dim propertyChain() As DependencyProperty = {Rectangle.FillProperty, SolidColorBrush.ColorProperty}
            Dim thePath As String = "(0).(1)"
DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {Rectangle.FillProperty, SolidColorBrush.ColorProperty};
string thePath = "(0).(1)";

В следующем примере объединяется код из предыдущих примеров для анимации Color SolidColorBrush используемой для задания Fill элемента прямоугольника.


            ' Create a name scope for the page.
            NameScope.SetNameScope(Me, New NameScope())

            Dim rectangle01 As New Rectangle()
            rectangle01.Name = "Rectangle01"
            Me.RegisterName(rectangle01.Name, rectangle01)
            rectangle01.Width = 100
            rectangle01.Height = 100
            rectangle01.Fill = CType(Me.Resources("MySolidColorBrushResource"), SolidColorBrush)

            Dim myColorAnimation As New ColorAnimation()
            myColorAnimation.From = Colors.Blue
            myColorAnimation.To = Colors.AliceBlue
            myColorAnimation.Duration = New Duration(TimeSpan.FromSeconds(1))
            Storyboard.SetTargetName(myColorAnimation, rectangle01.Name)

            Dim propertyChain() As DependencyProperty = {Rectangle.FillProperty, SolidColorBrush.ColorProperty}
            Dim thePath As String = "(0).(1)"
            Dim myPropertyPath As New PropertyPath(thePath, propertyChain)
            Storyboard.SetTargetProperty(myColorAnimation, myPropertyPath)

            Dim myStoryboard As New Storyboard()
            myStoryboard.Children.Add(myColorAnimation)
            Dim myBeginStoryboard As New BeginStoryboard()
            myBeginStoryboard.Storyboard = myStoryboard
            Dim myMouseEnterTrigger As New EventTrigger()
            myMouseEnterTrigger.RoutedEvent = Rectangle.MouseEnterEvent
            myMouseEnterTrigger.Actions.Add(myBeginStoryboard)
            rectangle01.Triggers.Add(myMouseEnterTrigger)

// Create a name scope for the page.
NameScope.SetNameScope(this, new NameScope()); 

Rectangle rectangle01 = new Rectangle();
rectangle01.Name = "Rectangle01";   
this.RegisterName(rectangle01.Name, rectangle01);
rectangle01.Width = 100;
rectangle01.Height = 100;
rectangle01.Fill = 
    (SolidColorBrush)this.Resources["MySolidColorBrushResource"];

ColorAnimation myColorAnimation = new ColorAnimation();
myColorAnimation.From = Colors.Blue;
myColorAnimation.To = Colors.AliceBlue;
myColorAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
Storyboard.SetTargetName(myColorAnimation, rectangle01.Name);

DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {Rectangle.FillProperty, SolidColorBrush.ColorProperty};
string thePath = "(0).(1)";
PropertyPath myPropertyPath = new PropertyPath(thePath, propertyChain);
Storyboard.SetTargetProperty(myColorAnimation, myPropertyPath);

Storyboard myStoryboard = new Storyboard();
myStoryboard.Children.Add(myColorAnimation);
BeginStoryboard myBeginStoryboard = new BeginStoryboard();
myBeginStoryboard.Storyboard = myStoryboard;
EventTrigger myMouseEnterTrigger = new EventTrigger();
myMouseEnterTrigger.RoutedEvent = Rectangle.MouseEnterEvent;
myMouseEnterTrigger.Actions.Add(myBeginStoryboard);
rectangle01.Triggers.Add(myMouseEnterTrigger);

Иногда требуется обратиться к элементу Freezable, содержащемуся в коллекции или массиве. Например, предположим, что прямоугольник имеет ресурс TransformGroup, который применяется к его свойству RenderTransform и требуется анимировать одно из содержащихся в нем преобразований.

<TransformGroup x:Key="MyTransformGroupResource"
  x:Shared="False">
  <ScaleTransform />
  <RotateTransform />
</TransformGroup>  

Чтобы обратиться к Freezable, содержащемуся в коллекции, следует использовать следующий синтаксис пути.

(OwnerPropertyArrayIndex).( CollectionChildrenPropertyArrayIndex) [CollectionIndex ].(FreezablePropertyArrayIndex)

Где CollectionIndex — это индекс объекта в массиве или коллекции.

Для обращения к свойству Angle RotateTransform, второго преобразования в TransformGroup, следует использовать следующий Path и PathParameters.

            Dim propertyChain() As DependencyProperty = { Rectangle.RenderTransformProperty, TransformGroup.ChildrenProperty, RotateTransform.AngleProperty }
            Dim thePath As String = "(0).(1)[1].(2)"
            Dim myPropertyPath As New PropertyPath(thePath, propertyChain)
            Storyboard.SetTargetProperty(myDoubleAnimation, myPropertyPath)
DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {
            Rectangle.RenderTransformProperty, 
            TransformGroup.ChildrenProperty,
            RotateTransform.AngleProperty
        };
string thePath = "(0).(1)[1].(2)";
PropertyPath myPropertyPath = new PropertyPath(thePath, propertyChain);
Storyboard.SetTargetProperty(myDoubleAnimation, myPropertyPath);

В следующем примере показан полный код анимации Angle для RotateTransform, содержащегося внутри TransformGroup.

            Dim rectangle02 As New Rectangle()
            rectangle02.Name = "Rectangle02"
            Me.RegisterName(rectangle02.Name, rectangle02)
            rectangle02.Width = 100
            rectangle02.Height = 100
            rectangle02.Fill = Brushes.Blue
            rectangle02.RenderTransform = CType(Me.Resources("MyTransformGroupResource"), TransformGroup)

            Dim myDoubleAnimation As New DoubleAnimation()
            myDoubleAnimation.From = 0
            myDoubleAnimation.To = 360
            myDoubleAnimation.Duration = New Duration(TimeSpan.FromSeconds(1))
            Storyboard.SetTargetName(myDoubleAnimation, rectangle02.Name)

            Dim propertyChain() As DependencyProperty = { Rectangle.RenderTransformProperty, TransformGroup.ChildrenProperty, RotateTransform.AngleProperty }
            Dim thePath As String = "(0).(1)[1].(2)"
            Dim myPropertyPath As New PropertyPath(thePath, propertyChain)
            Storyboard.SetTargetProperty(myDoubleAnimation, myPropertyPath)

            Dim myStoryboard As New Storyboard()
            myStoryboard.Children.Add(myDoubleAnimation)
            Dim myBeginStoryboard As New BeginStoryboard()
            myBeginStoryboard.Storyboard = myStoryboard
            Dim myMouseEnterTrigger As New EventTrigger()
            myMouseEnterTrigger.RoutedEvent = Rectangle.MouseEnterEvent
            myMouseEnterTrigger.Actions.Add(myBeginStoryboard)
            rectangle02.Triggers.Add(myMouseEnterTrigger)
Rectangle rectangle02 = new Rectangle();
rectangle02.Name = "Rectangle02";
this.RegisterName(rectangle02.Name, rectangle02);
rectangle02.Width = 100;
rectangle02.Height = 100;
rectangle02.Fill = Brushes.Blue;
rectangle02.RenderTransform = 
    (TransformGroup)this.Resources["MyTransformGroupResource"];

DoubleAnimation myDoubleAnimation = new DoubleAnimation();
myDoubleAnimation.From = 0;
myDoubleAnimation.To = 360;
myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
Storyboard.SetTargetName(myDoubleAnimation, rectangle02.Name);

DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {
            Rectangle.RenderTransformProperty, 
            TransformGroup.ChildrenProperty,
            RotateTransform.AngleProperty
        };
string thePath = "(0).(1)[1].(2)";
PropertyPath myPropertyPath = new PropertyPath(thePath, propertyChain);
Storyboard.SetTargetProperty(myDoubleAnimation, myPropertyPath);

Storyboard myStoryboard = new Storyboard();
myStoryboard.Children.Add(myDoubleAnimation);
BeginStoryboard myBeginStoryboard = new BeginStoryboard();
myBeginStoryboard.Storyboard = myStoryboard;
EventTrigger myMouseEnterTrigger = new EventTrigger();
myMouseEnterTrigger.RoutedEvent = Rectangle.MouseEnterEvent;
myMouseEnterTrigger.Actions.Add(myBeginStoryboard);
rectangle02.Triggers.Add(myMouseEnterTrigger);

Косвенная ссылка с использованием элемента Freezable в качестве начальной точки

В предыдущих разделах описаны способы косвенной ссылки на Freezable с помощью запуска с FrameworkElement или FrameworkContentElement и создания цепочки свойств к подсвойству Freezable. Можно также использовать Freezable в качестве отправной точки и косвенной ссылки на одно из его подсвойств Freezable. Одно дополнительное ограничение применяется при использовании Freezable в качестве отправной точки для косвенной ссылки. Начальный Freezable и ни один Freezable между ним и целевым подсвойством не должен быть зафиксирован.

Интерактивное управление Storyboard в языке XAML

Чтобы запустить раскадровку в Extensible Application Markup Language (XAML), используйте действие триггера BeginStoryboard. BeginStoryboard распределяет анимации объектам и анимируемым свойствам, а затем запускает раскадровку. (Дополнительные сведения об этом процессе см. в разделе Общие сведения об анимации и системе управления временем.) Если присвоить BeginStoryboard имя, задав его свойство Name, раскадровка станет управляемой. После запуска раскадровки ей можно будет управлять в интерактивном режиме. Ниже приведен список действий управляемой раскадровки, которые используются с триггерами событий для управления раскадровкой.

  • PauseStoryboard: приостанавливает раскадровку.

  • ResumeStoryboard: возобновляет приостановленную раскадровку.

  • SetStoryboardSpeedRatio: изменяет скорость раскадровки.

  • SkipStoryboardToFill: перемещает раскадровку к концу ее периода заполнения, если он есть.

  • StopStoryboard: останавливает раскадровку.

  • RemoveStoryboard: удаляет раскадровку.

В следующем примере управляемые действия раскадровки используются для интерактивного управления раскадровкой.

<Page
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="Microsoft.SDK.Animation.ControllableStoryboardExample"
  WindowTitle="Fading Rectangle Example">
  <StackPanel Margin="10">

    <Rectangle
      Name="MyRectangle"
      Width="100" 
      Height="100"
      Fill="Blue">
    </Rectangle>

    <Button Name="BeginButton">Begin</Button>
    <Button Name="PauseButton">Pause</Button>
    <Button Name="ResumeButton">Resume</Button>
    <Button Name="SkipToFillButton">Skip To Fill</Button>
    <Button Name="StopButton">Stop</Button>

    <StackPanel.Triggers>
      <EventTrigger RoutedEvent="Button.Click" SourceName="BeginButton">
        <BeginStoryboard Name="MyBeginStoryboard">
          <Storyboard>
            <DoubleAnimation
              Storyboard.TargetName="MyRectangle" 
              Storyboard.TargetProperty="(Rectangle.Opacity)"
              From="1.0" To="0.0" Duration="0:0:5" />
          </Storyboard>
        </BeginStoryboard>
      </EventTrigger>
      <EventTrigger RoutedEvent="Button.Click" SourceName="PauseButton">
        <PauseStoryboard BeginStoryboardName="MyBeginStoryboard" />
      </EventTrigger>
      <EventTrigger RoutedEvent="Button.Click" SourceName="ResumeButton">
        <ResumeStoryboard BeginStoryboardName="MyBeginStoryboard" />
      </EventTrigger>
      <EventTrigger RoutedEvent="Button.Click" SourceName="SkipToFillButton">
        <SkipStoryboardToFill BeginStoryboardName="MyBeginStoryboard" />
      </EventTrigger>
      <EventTrigger RoutedEvent="Button.Click" SourceName="StopButton">
        <StopStoryboard BeginStoryboardName="MyBeginStoryboard" />
      </EventTrigger>
    </StackPanel.Triggers>
  </StackPanel>
</Page>

Интерактивное управление раскадровкой с помощью кода

В предыдущих примерах показано, как выполнять анимацию с помощью действий триггера. В коде можно также управлять раскадровкой с помощью интерактивных методов класса Storyboard. Чтобы сделать Storyboard интерактивным в коде, необходимо использовать соответствующие перегрузки метода Begin раскадровки и указать true, чтобы сделать его управляемым. Дополнительные сведения см. на странице Begin(FrameworkElement, Boolean).

Ниже перечислены методы, которые могут быть использованы для управления Storyboard после его запуска.

Преимущество использования этих методов заключается в том, что не требуется создавать объекты Trigger или TriggerAction. Просто требуется ссылка на управляемый Storyboard, которым требуется управлять.

Примечание. Все интерактивные действия, выполняемые на Clock и, соответственно, на Storyboard, будут выполнены на следующем делении ядра времени, который произойдет незадолго перед следующей визуализацией. Например, при использовании метода Seek для перехода к другой точке в анимации, значение свойства не измениться мгновенно: оно изменится на следующем делении ядра времени.

В следующем примере демонстрируется применение и управление анимацией с помощью интерактивных методов класса Storyboard.


Imports Microsoft.VisualBasic
Imports System
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Shapes
Imports System.Windows.Media
Imports System.Windows.Media.Animation

Namespace SDKSample

    Public Class ControllableStoryboardExample
        Inherits Page
        Private myStoryboard As Storyboard

        Public Sub New()

            ' Create a name scope for the page.

            NameScope.SetNameScope(Me, New NameScope())

            Me.WindowTitle = "Controllable Storyboard Example"
            Dim myStackPanel As New StackPanel()
            myStackPanel.Margin = New Thickness(10)

            ' Create a rectangle.
            Dim myRectangle As New Rectangle()
            myRectangle.Name = "myRectangle"

            ' Assign the rectangle a name by 
            ' registering it with the page, so that
            ' it can be targeted by storyboard
            ' animations.
            Me.RegisterName(myRectangle.Name, myRectangle)
            myRectangle.Width = 100
            myRectangle.Height = 100
            myRectangle.Fill = Brushes.Blue
            myStackPanel.Children.Add(myRectangle)

            '
            ' Create an animation and a storyboard to animate the
            ' rectangle.
            '
            Dim myDoubleAnimation As New DoubleAnimation()
            myDoubleAnimation.From = 1.0
            myDoubleAnimation.To = 0.0
            myDoubleAnimation.Duration = New Duration(TimeSpan.FromMilliseconds(5000))
            myDoubleAnimation.AutoReverse = True

            ' Create the storyboard.
            myStoryboard = New Storyboard()
            myStoryboard.Children.Add(myDoubleAnimation)
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name)
            Storyboard.SetTargetProperty(myDoubleAnimation, New PropertyPath(Rectangle.OpacityProperty))

            '
            ' Create some buttons to control the storyboard
            ' and a panel to contain them.
            '
            Dim buttonPanel As New StackPanel()
            buttonPanel.Orientation = Orientation.Horizontal
            Dim beginButton As New Button()
            beginButton.Content = "Begin"
            AddHandler beginButton.Click, AddressOf beginButton_Clicked
            buttonPanel.Children.Add(beginButton)
            Dim pauseButton As New Button()
            pauseButton.Content = "Pause"
            AddHandler pauseButton.Click, AddressOf pauseButton_Clicked
            buttonPanel.Children.Add(pauseButton)
            Dim resumeButton As New Button()
            resumeButton.Content = "Resume"
            AddHandler resumeButton.Click, AddressOf resumeButton_Clicked
            buttonPanel.Children.Add(resumeButton)
            Dim skipToFillButton As New Button()
            skipToFillButton.Content = "Skip to Fill"
            AddHandler skipToFillButton.Click, AddressOf skipToFillButton_Clicked
            buttonPanel.Children.Add(skipToFillButton)
            Dim setSpeedRatioButton As New Button()
            setSpeedRatioButton.Content = "Triple Speed"
            AddHandler setSpeedRatioButton.Click, AddressOf setSpeedRatioButton_Clicked
            buttonPanel.Children.Add(setSpeedRatioButton)
            Dim stopButton As New Button()
            stopButton.Content = "Stop"
            AddHandler stopButton.Click, AddressOf stopButton_Clicked
            buttonPanel.Children.Add(stopButton)
            myStackPanel.Children.Add(buttonPanel)
            Me.Content = myStackPanel


        End Sub

        ' Begins the storyboard.
        Private Sub beginButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            ' Specifying "true" as the second Begin parameter
            ' makes this storyboard controllable.
            myStoryboard.Begin(Me, True)

        End Sub

        ' Pauses the storyboard.
        Private Sub pauseButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            myStoryboard.Pause(Me)

        End Sub

        ' Resumes the storyboard.
        Private Sub resumeButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            myStoryboard.Resume(Me)

        End Sub

        ' Advances the storyboard to its fill period.
        Private Sub skipToFillButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            myStoryboard.SkipToFill(Me)

        End Sub

        ' Updates the storyboard's speed.
        Private Sub setSpeedRatioButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            ' Makes the storyboard progress three times as fast as normal.
            myStoryboard.SetSpeedRatio(Me, 3)

        End Sub

        ' Stops the storyboard.
        Private Sub stopButton_Clicked(ByVal sender As Object, ByVal args As RoutedEventArgs)
            myStoryboard.Stop(Me)

        End Sub

    End Class

End Namespace
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace SDKSample
{

    public class ControllableStoryboardExample : Page
    {
        private Storyboard myStoryboard;

        public ControllableStoryboardExample()
        {

            // Create a name scope for the page.

            NameScope.SetNameScope(this, new NameScope()); 

            this.WindowTitle = "Controllable Storyboard Example";
            StackPanel myStackPanel = new StackPanel();
            myStackPanel.Margin = new Thickness(10);

            // Create a rectangle.
            Rectangle myRectangle = new Rectangle();
            myRectangle.Name = "myRectangle";

            // Assign the rectangle a name by 
            // registering it with the page, so that
            // it can be targeted by storyboard
            // animations.
            this.RegisterName(myRectangle.Name, myRectangle);
            myRectangle.Width = 100;
            myRectangle.Height = 100;
            myRectangle.Fill = Brushes.Blue;
            myStackPanel.Children.Add(myRectangle);

            //
            // Create an animation and a storyboard to animate the
            // rectangle.
            //
            DoubleAnimation myDoubleAnimation = new DoubleAnimation();
            myDoubleAnimation.From = 1.0;
            myDoubleAnimation.To = 0.0;
            myDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(5000));
            myDoubleAnimation.AutoReverse = true;

            // Create the storyboard.
            myStoryboard = new Storyboard();
            myStoryboard.Children.Add(myDoubleAnimation);
            Storyboard.SetTargetName(myDoubleAnimation, myRectangle.Name);
            Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath(Rectangle.OpacityProperty));

            //
            // Create some buttons to control the storyboard
            // and a panel to contain them.
            //
            StackPanel buttonPanel = new StackPanel();
            buttonPanel.Orientation = Orientation.Horizontal;
            Button beginButton = new Button();
            beginButton.Content = "Begin";
            beginButton.Click += new RoutedEventHandler(beginButton_Clicked);
            buttonPanel.Children.Add(beginButton);
            Button pauseButton = new Button();
            pauseButton.Content = "Pause";
            pauseButton.Click += new RoutedEventHandler(pauseButton_Clicked);
            buttonPanel.Children.Add(pauseButton);
            Button resumeButton = new Button();
            resumeButton.Content = "Resume";
            resumeButton.Click += new RoutedEventHandler(resumeButton_Clicked);
            buttonPanel.Children.Add(resumeButton);
            Button skipToFillButton = new Button();
            skipToFillButton.Content = "Skip to Fill";
            skipToFillButton.Click += new RoutedEventHandler(skipToFillButton_Clicked);
            buttonPanel.Children.Add(skipToFillButton);
            Button setSpeedRatioButton = new Button();
            setSpeedRatioButton.Content = "Triple Speed";
            setSpeedRatioButton.Click += new RoutedEventHandler(setSpeedRatioButton_Clicked);
            buttonPanel.Children.Add(setSpeedRatioButton);
            Button stopButton = new Button();
            stopButton.Content = "Stop";
            stopButton.Click += new RoutedEventHandler(stopButton_Clicked);
            buttonPanel.Children.Add(stopButton);
            myStackPanel.Children.Add(buttonPanel);
            this.Content = myStackPanel;        


        }

        // Begins the storyboard.
        private void beginButton_Clicked(object sender, RoutedEventArgs args)
        {
            // Specifying "true" as the second Begin parameter
            // makes this storyboard controllable.
            myStoryboard.Begin(this, true);

        }

        // Pauses the storyboard.
        private void pauseButton_Clicked(object sender, RoutedEventArgs args)
        {
            myStoryboard.Pause(this);

        }

        // Resumes the storyboard.
        private void resumeButton_Clicked(object sender, RoutedEventArgs args)
        {
            myStoryboard.Resume(this);

        }

        // Advances the storyboard to its fill period.
        private void skipToFillButton_Clicked(object sender, RoutedEventArgs args)
        {
            myStoryboard.SkipToFill(this);

        }

        // Updates the storyboard's speed.
        private void setSpeedRatioButton_Clicked(object sender, RoutedEventArgs args)
        {
            // Makes the storyboard progress three times as fast as normal.
            myStoryboard.SetSpeedRatio(this, 3);

        }

        // Stops the storyboard.
        private void stopButton_Clicked(object sender, RoutedEventArgs args)
        {
            myStoryboard.Stop(this);

        }         

    }

}

Анимация с использованием стилей

Можно использовать объекты Storyboard для определения анимации в Style. Анимация с Storyboard в Style аналогична использованию Storyboard в другом месте, но существуют три исключения:

  • TargetName не задается; Storyboard всегда указывает элемент, к которому применяется Style. Для ссылки на объекты Freezable необходимо использовать косвенные ссылки. Дополнительные сведения о косвенных ссылках см. в разделе Косвенные ссылки.

  • Нельзя указать SourceName для EventTrigger или Trigger.

  • Нельзя использовать ссылки на динамические ресурсы или выражения привязки данных для задания значений свойств Storyboard или свойств анимации. Это объясняется тем, что все внутри Style должно быть потокобезопасным, и система расчета времени должна сделать объекты Freeze Storyboard потокобезопасными. Storyboard не может быть зафиксирован, если он или его дочерние шкалы времени содержат ссылки на динамические ресурсы или выражения связывания данных. Дополнительные сведения о фиксации и других возможностях Freezable см. в разделе Общие сведения об объектах класса Freezable.

  • В XAML нельзя объявить обработчик событий для Storyboard или событий анимации.

Пример, показывающий, как определить раскадровку в стиле, см. в разделе Практическое руководство. Применение анимации в стиле.

Анимация в ControlTemplate

Можно использовать объекты Storyboard для определения анимации в ControlTemplate. Анимация с помощью Storyboard в ControlTemplate в аналогична использованию Storyboard в другом месте, но существуют два исключения.

  • TargetName может обращаться только к дочерним объектам ControlTemplate. Если TargetName не указан, анимация обращается к элементам, к которым применяется ControlTemplate.

  • SourceName для EventTrigger или Trigger может обращаться только к дочерним объектам ControlTemplate.

  • Нельзя использовать ссылки на динамические ресурсы или выражения привязки данных для задания значений свойств Storyboard или свойств анимации. Это объясняется тем, что все внутри ControlTemplate должно быть потокобезопасным, и система расчета времени должна сделать объекты Freeze Storyboard потокобезопасными. Storyboard не может быть зафиксирован, если он или его дочерние шкалы времени содержат ссылки на динамические ресурсы или выражения связывания данных. Дополнительные сведения о фиксации и других возможностях Freezable см. в разделе Общие сведения об объектах класса Freezable.

  • В XAML нельзя объявить обработчик событий для Storyboard или событий анимации.

Пример, показывающий, как определить раскадровку в ControlTemplate, см. в разделе Практическое руководство. Анимация в элементе управления ControlTemplate.

Анимация при изменении значения свойства

В стилях и шаблонах элементов управления можно использовать объекты-триггеры для запуска раскадровки при изменении свойств. Примеры см. в разделах Практическое руководство. Запуск анимации при изменении значения свойства и Практическое руководство. Анимация в элементе управления ControlTemplate.

Запуск анимации с помощью свойства объектов Trigger выполняется сложнее, чем анимация EventTrigger, или анимация с помощью методов Storyboard. Она «перекликается» с анимацией, определенной другими объектами Trigger, но состоит из EventTrigger и анимации, запускаемой методами.

См. также

Основные понятия

Общие сведения об эффектах анимации

Общие сведения о методах анимации свойств

Общие сведения об объектах класса Freezable