分镜头脚本概述

本主题演示如何使用 Storyboard 对象来组织和应用动画。 它描述了如何通过接口操控 Storyboard 对象,并描述了间接属性目标的语法。

先决条件

若要了解本主题,应熟悉不同的动画类型及其基本功能。 有关动画简介,请参阅 动画概述。 还应了解如何使用附加属性。 有关附加属性的详细信息,请参阅 “附加属性概述”。

什么是分镜头脚本

动画不是唯一有用的时间线类型。 提供其他时间线类,以帮助你组织日程表集,并将时间线应用于属性。 容器时间线派生自 TimelineGroup 类,并包括 ParallelTimelineStoryboard

Storyboard 是一种容器时间线,提供其包含的时间线的目标信息。 情节提要可以包含任何类型的 Timeline,包括其他容器时间线和动画。 Storyboard 通过对象,可以将影响各种对象和属性的日程表合并到单个时间线树中,从而轻松组织和控制复杂的计时行为。 例如,假设你想要一个执行这三项作的按钮。

  • 用户选择按钮时,其会增长并更改颜色。

  • 收缩后,单击时恢复到原始大小。

  • 当禁用时,收缩并淡化到 50% 的不透明度。

在这种情况下,你有多个应用于同一对象的动画集,并且你想要在不同时间播放,具体取决于按钮的状态。 Storyboard 使用对象可以组织动画并将其应用于一个或多个对象。

在何处可以使用故事板

A Storyboard 可用于对可动画类的依赖属性进行动画处理(有关使类变得可动画化的详细信息,请参阅 动画概述)。 但是,由于情节提要是框架级功能,因此该对象必须属于 NameScope 某个或一个 FrameworkElementFrameworkContentElement

例如,您可以使用一个 Storyboard 来执行以下操作:

但是,您不能使用Storyboard来动画SolidColorBrush,如果它没有在FrameworkElementFrameworkContentElement中注册其名称,或者没有用于设置FrameworkElementFrameworkContentElement的属性。

如何使用故事板应用动画

要使用Storyboard来组织和应用动画,请将动画添加为Storyboard的子时间线。 该 Storyboard 类提供 Storyboard.TargetNameStoryboard.TargetProperty 附加属性。 在动画上设置这些属性以指定其目标对象和属性。

若要将动画应用于目标,请通过触发操作或方法开始 Storyboard。 在 XAML 中,可以将 BeginStoryboard 对象与 EventTriggerTriggerDataTrigger 一起使用。 在代码中,也可以使用该方法 Begin

下表显示了支持每个 Storyboard 开始技术的不同位置:每个实例、样式、控件模板和数据模板。 “Per-Instance”是指一种将动画或故事板直接应用于对象实例的技术,而不是应用于样式、控件模板或数据模板中。

故事板已经开始使用… 每个实例 风格 控件模板 数据模板 示例:
BeginStoryboardEventTrigger 是的 是的 是的 是的 使用故事板为属性添加动画效果
BeginStoryboard 和属性 Trigger 是的 是的 是的 当属性值更改时触发动画
BeginStoryboard 和属性 MultiTrigger 是的 是的 是的 MultiTrigger 类示例
BeginStoryboard 和 a DataTrigger 是的 是的 是的 如何:在数据更改时触发动画
BeginStoryboard 和 a MultiDataTrigger 是的 是的 是的 MultiDataTrigger 类示例
Begin 方法 是的 使用故事板为属性添加动画效果

下面的示例使用 Storyboard 来为 Rectangle 元素的 Width 和用于绘制该 RectangleSolidColorBrushColor 制作动画。

<!-- This example shows how to animate with a storyboard.-->
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://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>
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;
        }
    }
}

以下部分更详细地介绍了TargetNameTargetProperty附加属性。

定位框架元素、框架内容元素和可冻结对象

上一部分提到,要让动画找到其目标,它必须知道目标的名称和要进行动画处理的属性。 指定要进行动画处理的属性很简单:只需设置TargetProperty为要进行动画处理的属性名称即可。 通过对动画设置 Storyboard.TargetName 属性来指定要对其属性进行动画处理的对象的名称。

谨慎

虽然可以使用Target属性直接绑定到对象来替代TargetName属性,但它不是可序列化的。 不能保证 Target 可以在 XAML 中正确引用对象。

TargetName若要使属性正常工作,目标对象必须具有名称。 在 XAML 中给FrameworkElementFrameworkContentElement分配名称与给Freezable对象分配名称是不同的。

框架元素是那些从 FrameworkElement 类继承而来的类。 框架元素的示例包括WindowDockPanelButtonRectangle。 本质上,所有窗口、面板和控件都是元素。 继承自 FrameworkContentElement 类的那些类被称为框架内容元素。 框架内容元素的示例包括 FlowDocumentParagraph。 如果不确定类型是框架元素还是框架内容元素,请检查它是否具有 Name 属性。 如果这样做,它可能是框架元素或框架内容元素。 若要确定,请检查其类型页的“继承层次结构”部分。

要在 XAML 中用于定位框架元素或框架内容元素,请指定其 Name 属性。 在代码中,您还需要使用 RegisterName 方法将元素名称注册到您已为其创建 NameScope 的目标元素。

以下的示例取自前面的示例,它为名称 MyRectangle 指定了一个Rectangle,其类型为 FrameworkElement

<Rectangle Name="MyRectangle"
  Width="100"
  Height="100">
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));

Freezable 类型是那些从 Freezable 类继承的类。 Freezable包括SolidColorBrushRotateTransformGradientStop.

若要在 XAML 中设置动画的目标,请使用 x:Name 指令Freezable 分配名称。 在代码中 RegisterName ,使用该方法向为其创建了它的 NameScope元素注册其名称。

以下示例将名称分配给对象 Freezable

<SolidColorBrush x:Name="MySolidColorBrush" Color="Blue" />
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 对象使用名称范围解析 TargetName 属性。 有关 WPF 名称范围的详细信息,请参阅 WPF XAML 名称范围。 如果省略该TargetName属性,则动画将定位于定义它的元素,或者,如果是样式的情况,则以样式元素为目标。

有时无法将名称分配给 Freezable 对象。 例如,如果将 a Freezable 声明为资源或用于在样式中设置属性值,则无法为其指定名称。 因为它没有名称,因此不能直接锁定它,但是可以间接锁定。 以下部分介绍如何使用间接定位。

间接目标

有时候,Freezable 动画无法被直接定位,例如,当 Freezable 被声明为资源或用于在样式中设置属性值的时候。 在这些情况下,即使不能直接定位它,你仍然可以对 Freezable 对象进行动画处理。 而不是将TargetName属性设置为Freezable的名称,您需要为其指定该元素所属的元素名称Freezable。例如,用于设置矩形元素FillSolidColorBrush属于该矩形元素。 若要为画笔设置动画,您可以使用一系列属性来设置动画的 TargetProperty ,这些属性从框架元素或框架内容元素的属性开始,Freezable 用于设置,然后以需要动画处理的属性 Freezable 结束。

<ColorAnimation 
  Storyboard.TargetName="Rectangle01"
  Storyboard.TargetProperty="Fill.Color"
  From="Blue" To="AliceBlue" Duration="0:0:1" />
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,因为原始对象实际上并未进行动画处理。 有关克隆的详细信息,请参阅 冻结对象概述

另请注意,使用间接属性目标时,可以面向不存在的对象。 例如,你可能会假设使用了 SolidColorBrush 来设置特定按钮的 Background 并尝试为其颜色制作动画,而实际上使用的是 LinearGradientBrush 来设置该按钮的背景。 在这些情况下,不会引发异常;动画无法产生可见效果,因为 LinearGradientBrush 对属性的更改 Color 没有反应。

以下部分更详细地介绍了间接属性目标语法。

在 XAML 中间接定位可冻结的属性

在 XAML 中针对冻结对象的属性,请使用以下语法。

属性语法
ElementPropertyName.FreezablePropertyName

位置

以下代码演示如何对用于设置矩形元素FillSolidColorBrushColor进行动画处理。

<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>

有时需要以集合或数组中包含的可冻结对象为目标。

若要以集合中包含的可冻结对象为目标,请使用以下路径语法。

路径语法
ElementPropertyName.Children[CollectionIndex].FreezablePropertyName

其中 CollectionIndex 是其数组或集合中对象的索引。

例如,假设一个矩形有一个 TransformGroup 应用于其 RenderTransform 属性的资源,并且你想要对其包含的某个转换进行动画处理。

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

以下代码演示如何对上一示例中所示的Angle属性进行动画处理。

<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>  

在代码中间接定位可冻结的属性

在代码中,创建对象 PropertyPath 。 创建PropertyPath时,请指定PathPathParameters

若要创建 PathParameters,请创建包含依赖属性标识符字段列表的类型 DependencyProperty 数组。 第一个标识符字段用于FrameworkElementFrameworkContentElement的属性设置由Freezable实现。 下一个标识符字段表示目标的属性 Freezable 。 将其视为将对象连接到FreezableFrameworkElement对象的属性链。

下面是一个依赖属性链的示例,该链用于设置矩形元素的Fill,而目标是SolidColorBrushColor

DependencyProperty[] propertyChain =
    new DependencyProperty[]
        {Rectangle.FillProperty, SolidColorBrush.ColorProperty};

还需要指定一个 Path。 A 是一个说明Path如何解释它的PathParametersPathString。 它使用以下语法。

属性路径语法
( 所有者属性数组索引).(可冻结属性数组索引)

位置

下面的示例展示了在前面示例中定义的 PathParameters 所附带的 Path

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

以下示例结合前面示例中的代码,以动画方式展示用于设置矩形元素的FillSolidColorBrushColor


// 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);

有时需要以集合或数组中包含的可冻结对象为目标。 例如,假设一个矩形有一个 TransformGroup 应用于其 RenderTransform 属性的资源,并且你想要对其包含的某个转换进行动画处理。

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

若要以集合中包含的目标 Freezable 为目标,请使用以下路径语法。

路径语法
( OwnerPropertyArrayIndex).(CollectionChildrenPropertyArrayIndex)[CollectionIndex].(FreezablePropertyArrayIndex)

其中 CollectionIndex 是其数组或集合中对象的索引。

要针对RotateTransformAngle属性,即TransformGroup中的第二个转换,请使用以下PathPathParameters

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);

下面的示例演示了完整代码,用于对TransformGroupRotateTransformAngle进行动画处理。

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);

以可冻结项为起点的间接定位

前面的部分介绍了如何通过从FrameworkElementFrameworkContentElement开始并创建到Freezable子属性的属性链来间接设定目标Freezable。 还可以使用 a Freezable 作为起点,间接定位其 Freezable 子属性之一。 在使用 Freezable 作为间接目标的起点时,有一个额外的限制:起始 Freezable 及其与间接目标子属性之间每个 Freezable 都不得被冻结。

在 XAML 中以交互方式控制动画板

若要在可扩展应用程序标记语言(XAML)中启动情节提要,请使用 BeginStoryboard 触发器动作。 BeginStoryboard 将动画分发到它们所动画的对象和属性,并启动动画板。 (有关此过程的详细信息,请参阅动画和计时系统概述。如果通过指定其Name属性来指定BeginStoryboard名称,则将其设为可控制的情节提要。 然后,可以在故事板启动后以交互方式进行控制。 下面是可用于控制情节提要的事件触发器来控制情节提要的可控制情节提要作的列表。

在以下示例中,可控制的故事板动作用于交互地控制故事板。

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://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的方法:

使用这些方法的优点是无须创建 TriggerTriggerAction 对象,只需有一个可操作的 Storyboard 的引用即可。

注释

ClockStoryboard执行的所有交互操作将会在计时引擎的下一次刻度发生,因此也包括在下次渲染前不久发生。 例如,如果使用 Seek 该方法跳转到动画中的另一个点,则属性值不会立即更改,而是在计时引擎的下一刻刻更改值。

以下示例演示如何使用类的 Storyboard 交互式方法应用和控制动画。

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);
        }
    }
}

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

在样式中设置动画

可以使用 Storyboard 对象在 . Style中定义动画。 在 Style 中使用 Storyboard 来动画化类似于在其他地方使用 Storyboard,但有以下三个例外:

有关如何在样式中定义故事板的示例,请参阅在样式中动画示例

在 ControlTemplate 中设置动画

可以使用 Storyboard 对象在 . ControlTemplate中定义动画。 在 Storyboard 中进行动画处理类似于在其他地方使用 Storyboard,但有以下两个例外:

有关如何在ControlTemplate中定义Storyboard的示例,请参阅ControlTemplate中的动画示例。

当属性值更改时进行动画处理

在样式和控件模板中,可以使用 Trigger 对象在属性更改时启动故事板。 有关示例,请参阅属性值更改时触发动画在ControlTemplate中设置动画

属性Trigger对象应用的动画的行为比EventTrigger动画或使用Storyboard方法开始的动画更为复杂。 它们使用由其他 Trigger 对象定义的动画进行“切换”,但使用 EventTrigger 和方法触发的动画进行组合。

另请参阅