演示图板概述

更新:2007 年 11 月

本主题演示如何使用 Storyboard 对象来组织和应用动画,还描述了如何交互操作 Storyboard 对象,并介绍了间接属性目标语法。

先决条件

要了解本主题,您应当熟悉不同的动画类型及其基本功能。有关动画的简介,请参见动画概述。您还应当了解如何使用附加属性。有关附加属性的更多信息,请参见附加属性概述

什么是 Storyboard?

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

Storyboard 是一种为其所包含的时间线提供目标信息的容器时间线。Storyboard 可以包含任意类型的 Timeline,包括其他容器时间线和动画。可以使用 Storyboard 对象将影响各种对象和属性的时间线组合成一个时间线树,以便于组织和控制复杂计时行为。例如,假设您需要一个执行以下三个操作的按钮。

  • 当用户选择该按钮时,该按钮增大并更改颜色。

  • 当单击该按钮时,该按钮缩小并恢复其原始大小。

  • 当该按钮变成禁用时,缩小且不透明度缩减到 50%。

在此例中,您可以向同一对象应用多组动画,并且可以播放多次,具体取决于按钮的状态。可以使用 Storyboard 对象来组织动画并将它们成组地应用于一个或多个对象。

在什么情况下使用 Storyboard?

可以使用 Storyboard 对可动画处理的类的依赖项属性进行动画处理(有关如何使类成为可动画处理的类的更多信息,请参见动画概述)。不过,由于图板演示是框架级别的功能,该对象必须属于 FrameworkElementFrameworkContentElementNameScope

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

但是,不能使用 Storyboard 来对未向 FrameworkElementFrameworkContentElement 注册其名称或未用于设置 FrameworkElementFrameworkContentElement 的属性的 SolidColorBrush 进行动画处理。

如何使用 Storyboard 应用动画

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

若要将动画应用于其目标,您可以使用触发器操作或方法来开始 Storyboard。在 XAML 中,将 BeginStoryboard 对象与 EventTriggerTriggerDataTrigger 一起使用。在代码中,还可以使用 Begin 方法。

下表列出了支持每个 Storyboard 开始技术的不同方面:基于实例、样式、控件模板和数据模板。“基于实例”指的是直接将动画或演示图板应用于对象实例(而不是在样式、控件模板或数据模板中应用)的技术。

开始演示图板时使用…

基于实例

样式

控件模板

数据模板

示例

BeginStoryboardEventTrigger

Yes

Yes

Yes

Yes

如何:使用演示图板对属性进行动画处理

BeginStoryboard 和属性 Trigger

No

Yes

Yes

Yes

如何:在属性值更改时触发动画

BeginStoryboardDataTrigger

No

Yes

Yes

Yes

如何:在数据发生更改时触发动画

Begin 方法

Yes

No

No

No

如何:使用演示图板对属性进行动画处理

下面的示例使用 StoryboardRectangle 元素的 Width 和用于绘制该 RectangleSolidColorBrushColor 进行动画处理。

<!-- 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>
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 附加属性。

以框架元素、框架内容元素和 Freezable 元素为目标

上面一节提到对于动画,要查找其目标,必须知道目标的名称和要进行动画处理的属性。指定要进行动画处理的属性非常直接:只需用要进行动画处理的属性的名称来设置 Storyboard.TargetProperty 即可。 通过在动画上设置 Storyboard.TargetName 属性来指定具有要进行动画处理的属性的对象的名称。

为了使 TargetName 属性起作用,目标对象必须有名称。在 XAML 中为 FrameworkElementFrameworkContentElement 分配名称不同于为 Freezable 对象分配名称。

框架元素是从 FrameworkElement 类继承的类。框架元素的示例包括 WindowDockPanelButtonRectangle。从本质上而言,所有窗口、面板和控件都是元素。框架内容元素是从 FrameworkContentElement 类继承的类。框架内容元素的示例包括 FlowDocumentParagraph。如果您不确定某类型是否是框架元素或框架内容元素,请查看它是否具有 Name 属性。如果它具有 Name 属性,则可能是框架元素或框架内容元素。若要进一步确定,请检查其类型页面的“继承层次结构”部分。

若要在 XAML 中以框架元素或框架内容元素为目标,请设置其 Name 属性。在代码中,还需要使用 RegisterName 方法向创建了 NameScope 的元素注册该元素名称。

下面的示例摘自上一个示例,它为一个 Rectangle(一种 FrameworkElement)分配名称 MyRectangle。

<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 中以 Freezable 作为动画的目标,可以使用 x:Name 属性 为其分配名称。在代码中,可以使用 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 名称范围。如果忽略 TargetName 属性,动画则以定义该属性时所在的元素为目标(如果存在样式,则以带样式的元素为目标)。

有时,不能为 Freezable 对象分配名称。例如,如果 Freezable 声明为资源,或者用于设置样式中的属性值,则不能为该对象分配名称。由于它没有名称,因此不能直接以它为目标 - 但是可以间接以它为目标。下面几节描述如何使用间接目标。

间接目标

有时动画不能直接以 Freezable 为目标,例如当 Freezable 声明为资源或者用于设置样式中的属性值时。在这些情况下,即使不能直接将 Freezable 对象作为目标,您仍可以对其进行动画处理。您可以为 TargetName 属性分配 Freezable“所属”的元素的名称,而不是使用 Freezable 的名称来设置该属性。例如,SolidColorBrush 用于设置矩形元素的 Fill 属于该矩形。若要对该画笔进行动画处理,您需要使用一个属性链来设置动画的 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,因为实际上并不会对原始对象进行动画处理。有关克隆的更多信息,请参见 Freezable 对象概述

另请注意,当使用间接属性目标时,可能会以不存在的对象为目标。例如,您可能假设使用 SolidColorBrush 设置了特定按钮的 Background 并尝试对其颜色进行动画处理,而实际上该按钮的 Background 是使用 LinearGradientBrush 设置的。在这种情况下,不会引发异常;由于 LinearGradientBrush 不对 Color 属性的更改做出反应,因此动画不会具有可视效果。

下面几节更详细地描述了间接属性目标语法。

在 XAML 中间接以 Freezable 的属性为目标

若要在 XAML 中以 Freezable 的属性为目标,请使用以下语法。

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>

有时,您需要以集合或数组中包含的 Freezable 为目标。

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

ElementPropertyName.Children[CollectionIndex].FreezablePropertyName

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

例如,假设矩形具有应用于其 RenderTransform 属性的 TransformGroup 资源,您希望对它包含的变换之一进行动画处理。

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

下面的代码演示如何对上例中演示的 RotateTransformAngle 属性进行动画处理。

<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 时,请指定 PathPathParameters

若要创建 PathParameters,需要创建 DependencyProperty 类型的数组,其中包含依赖项属性标识符字段的列表。第一个标识符字段用于使用 Freezable 设置的 FrameworkElementFrameworkContentElement 的属性。下一个标识符字段表示目标 Freezable 的属性。将其看作一个属性链,该属性链将 Freezable 连接到 FrameworkElement 对象。

下面的示例是一个依赖项属性链,这些属性以用于设置矩形元素的 FillSolidColorBrushColor 为目标。

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

您还需要指定 PathPath 是一个 String,用于告知 Path 如何解释其 PathParameters。它使用以下语法。

(OwnerPropertyArrayIndex).(FreezablePropertyArrayIndex)

位置

下面的示例演示上例中定义的 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);

有时,您需要以集合或数组中包含的 Freezable 为目标。例如,假设矩形具有应用于其 RenderTransform 属性的 TransformGroup 资源,您希望对它包含的变换之一进行动画处理。

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

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

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

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

若要以 RotateTransformTransformGroup 中的第二个变换)的 Angle 属性为目标,请使用以下 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);

下面的示例演示用于对 TransformGroup 中包含的 RotateTransformAngle 进行动画处理的完整代码。

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 为目标并将其作为开始点

上面几节描述了如何通过从 FrameworkElementFrameworkContentElement 开始为 Freezable 子属性创建属性链,来间接以 Freezable 为目标。您还可以使用 Freezable 作为开始点,并间接以其 Freezable 子属性之一为目标。如果使用 Freezable 作为间接目标的开始点,则存在一个附加限制:起始 Freezable 以及它与子属性间接目标之间的每个 Freezable 都不能冻结。

在 XAML 中以交互方式控制 Storyboard

若要在 可扩展应用程序标记语言 (XAML) 中启动 Storyboard,请使用 BeginStoryboard 触发器操作。BeginStoryboard 会将动画分发到要进行动画处理的对象和属性,然后启动 Storyboard (有关此过程的详细信息,请参见动画和计时系统概述)。如果通过指定 BeginStoryboardName 属性为它提供一个名称,则它将成为可控制的 Storyboard。然后,可以在 Storyboard 启动后以交互方式对它进行控制。下面列出与事件触发器一起用来控制 Storyboard 的可控制 Storyboard 操作。

在下面的示例中,使用可控制的 Storyboard 操作来以交互方式控制 Storyboard。

<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 类的交互方法来控制 Storyboard。若要使 Storyboard 在代码中可交互,必须使用该 Storyboard 的 Begin 方法的适当重载并指定 true 使之可以控制。有关更多信息,请参见 Begin(FrameworkElement, Boolean) 页。

下面的列表显示了在 Storyboard 启动之后可用来对其进行操作的方法:

使用这些方法的优点是您不需要创建 TriggerTriggerAction 对象,只需要一个对要操作的可控制 Storyboard 的引用。

**注意:**对 Clock 采取的所有交互操作(因而也会对 Storyboard 采取这些操作)将在计时引擎下次滴答时执行,也就是在快要进行下次呈现时执行。例如,如果使用 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);

        }         

    }

}

在样式中进行动画处理

可以使用 Storyboard 对象在 Style 中定义动画。在 Style 中使用 Storyboard 进行动画处理与在别处使用 Storyboard 类似,不过,有以下三个例外:

  • 您不必指定 TargetNameStoryboard 始终以 Style 应用到的元素为目标。若要以 Freezable 为目标,必须使用间接目标。有关间接目标的更多信息,请参见间接目标部分。

  • 您不能指定 EventTriggerTriggerSourceName

  • 您不能使用动态资源引用或数据绑定表达式来设置 Storyboard 或动画属性值。这是因为 Style 中的任何内容都必须是线程安全的,计时系统必须 Freeze Storyboard 对象以使其变为线程安全。如果 Storyboard 或其子时间线包含动态资源引用或数据绑定表达式,则不能将其冻结。有关冻结和其他 Freezable 功能的更多信息,请参见 Freezable 对象概述

  • 在 XAML 中,不能为 Storyboard 或动画事件声明事件处理程序。

有关演示如何在样式中定义 Storyboard 的示例,请参见如何:在样式中进行动画处理示例。

在 ControlTemplate 中进行动画处理

可以使用 Storyboard 对象在 ControlTemplate 中定义动画。在 ControlTemplate 中使用 Storyboard 进行动画处理与在别处使用 Storyboard 类似,不过,有以下四个例外:

有关演示如何在 ControlTemplate 中定义 Storyboard 的示例,请参见如何:在 ControlTemplate 中进行动画处理示例。

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

在样式和控件模板中,当属性更改时,可以使用 Trigger 对象来启动 Storyboard。有关示例,请参见 如何:在属性值更改时触发动画如何:在 ControlTemplate 中进行动画处理

属性 Trigger 对象所应用的动画的行为比 EventTrigger 动画或使用 Storyboard 方法启动的动画的行为更复杂一些。 它们在“提交”时使用的是其他 Trigger 对象定义的动画,但是在编写时使用 EventTrigger 和方法触发的动画。

请参见

概念

动画概述

属性动画技术概述

Freezable 对象概述