有时您可能要动态(即时)更改动画的属性。 例如,您可能要调整应用到对象的动画行为,这取决于对象当前在布局中的位置、对象包含何种内容等等。 可以通过使用程序代码(例如 C# 或 Visual Basic)动态操作动画。
本主题包括下列各节。
访问动画对象以更改其属性的最直接方法是:命名该动画对象,然后在代码中通过该名称引用它。 下面的示例包含一个 Ellipse,当您在屏幕上单击时它将显示动画效果。 为了实现此动画,在单击 Canvas 时,事件处理程序更改 PointAnimation 对象的 To 属性,然后启动动画。
<CanvasMouseLeftButtonDown="Handle_MouseDown"Background="Gray"Width="600"Height="500"><Canvas.Resources><Storyboardx:Name="myStoryboard"><!-- The PointAnimation has a name so it can be accessed from code. The To property is left out of the XAML because the value of To is determined in code. --><PointAnimationx:Name="myPointAnimation"Storyboard.TargetProperty="Center"Storyboard.TargetName="MyAnimatedEllipseGeometry"Duration="0:0:2"/></Storyboard></Canvas.Resources><PathFill="Blue"><Path.Data><!-- Describes an ellipse. --><EllipseGeometryx:Name="MyAnimatedEllipseGeometry"Center="200,100"RadiusX="15"RadiusY="15"/></Path.Data></Path></Canvas>
privatevoid Handle_MouseDown(object sender, MouseButtonEventArgs e) { // Retrieve current mouse coordinates.double newX = e.GetPosition(null).X; double newY = e.GetPosition(null).Y; Point myPoint = new Point(); myPoint.X = newX; myPoint.Y = newY; myPointAnimation.To = myPoint; myStoryboard.Begin(); }
使所有动画具有唯一的名称有时不易做到。 这时您可以使用集合来访问动画或动画的关键帧。 例如,如果要以编程方式访问 DoubleAnimationUsingKeyFrames 对象中的所有关键帧,可以使用与以下代码类似的代码:
<Canvas x:Name="parentCanvas" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Width="600" Height="500" Background="Gray"> <Canvas.Resources> <Storyboard x:Name="myStoryboard"> <PointAnimationUsingKeyFrames x:Name="myPointAnimationUsingKeyFrames" Storyboard.TargetProperty="Center" Storyboard.TargetName="MyAnimatedEllipseGeometry" Duration="0:0:3"> <!-- Set of keyframes --> <DiscretePointKeyFrame KeyTime="0:0:0" /> <LinearPointKeyFrame KeyTime="0:0:0.5" /> <SplinePointKeyFrame KeySpline="0.6,0.0 0.9,0.00" KeyTime="0:0:3" /> </PointAnimationUsingKeyFrames> </Storyboard> </Canvas.Resources> <Path Fill="Blue"> <Path.Data> <!-- Describes an ellipse. --> <EllipseGeometry x:Name="MyAnimatedEllipseGeometry" Center="200,100" RadiusX="15" RadiusY="15" /> </Path.Data> </Path> </Canvas>
public void Handle_MouseDown(object sender, MouseEventArgs e) { int i; for (i = 0; i < myPointAnimationUsingKeyFrames.KeyFrames.Count; i++) { // Do something with each keyframe; for example, set values. } }
下面的示例与上一个示例类似,即在用户单击屏幕的地方出现椭圆,只是此示例中使用了关键帧。 对关键帧的集合进行迭代并为关键帧动态设置值,以便使椭圆动画出现在合适的位置。
<CanvasMouseLeftButtonDown="Handle_MouseDown"Width="600"Height="500"Background="Gray"><Canvas.Resources><Storyboardx:Name="myStoryboard"><PointAnimationUsingKeyFramesx:Name="myPointAnimationUsingKeyFrames"Storyboard.TargetProperty="Center"Storyboard.TargetName="MyAnimatedEllipseGeometry"Duration="0:0:3"><DiscretePointKeyFrameKeyTime="0:0:0"/><LinearPointKeyFrameKeyTime="0:0:0.5"/><SplinePointKeyFrameKeySpline="0.6,0.0 0.9,0.00"KeyTime="0:0:2"/></PointAnimationUsingKeyFrames></Storyboard></Canvas.Resources><PathFill="Blue"><Path.Data><!-- Describes an ellipse. --><EllipseGeometryx:Name="MyAnimatedEllipseGeometry"Center="200,100"RadiusX="15"RadiusY="15"/></Path.Data></Path></Canvas>
// Global variables that keep track of the end point// of the last animation.double lastX = 200; double lastY = 100; privatevoid Handle_MouseDown(object sender, MouseEventArgs e) { // Retrieve current mouse coordinates.double newX = e.GetPosition(null).X; double newY = e.GetPosition(null).Y; int i; for (i = 0; i < myPointAnimationUsingKeyFrames.KeyFrames.Count; i++) { PointKeyFrame keyFrame = myPointAnimationUsingKeyFrames.KeyFrames[i]; if (keyFrame.GetType().Name == "DiscretePointKeyFrame") { keyFrame.SetValue(DiscretePointKeyFrame.ValueProperty, new Point(lastX, lastY)); } elseif (keyFrame.GetType().Name == "LinearPointKeyFrame") { // The LinearKeyFrame has a value that is part way to the // final end point. In addition, this value has to be on// the correct line; therefore, you need to use the line // formula y = mx + b to find the values of x and y.// Calculate the slope.double m = (newY - lastY) / (newX - lastX); // Calculate the y-intercept.double b = newY - (m * newX); // Set X to a third of the way to the end point.double intermediateX = lastX + (newX - lastX) / 3; // Find the value Y from X and the line formula.double intermediateY = (m * intermediateX) + b; // Set the keyframe value to the intermediate x and y value. keyFrame.SetValue(LinearPointKeyFrame.ValueProperty, new Point(intermediateX, intermediateY)); } elseif (keyFrame.GetType().Name == "SplinePointKeyFrame") { keyFrame.SetValue(SplinePointKeyFrame.ValueProperty, new Point(newX, newY)); } } myStoryboard.Stop(); myStoryboard.Begin(); lastX = newX; lastY = newY; }
![]() |
---|
Storyboard 具有 Children 属性,该属性允许您访问指定 Storyboard 中的所有动画对象。 |
动态更改 Storyboard.TargetName 属性最常见的情况是您想将同一动画应用到多个对象。 当具有要应用相似动画的大量对象时,这特别有用。 例如,您可能要显示几行图像并使用动画突出显示鼠标当前所指示的图像。 为每个图像创建单独的 Storyboard 对象非常麻烦。 重用同一 Storyboard 更为合适。
下面的示例涉及很多矩形,当您单击这些矩形时,它们会逐渐消失,接着重新显示。 所有这些矩形使用同一 Storyboard,因为呈现 Opacity 动画效果的DoubleAnimation 将 TargetName 更改为所单击的矩形。
<StackPanelOrientation="Horizontal"><StackPanel.Resources><Storyboardx:Name="myStoryboard"><DoubleAnimationx:Name="myDoubleAnimation"Storyboard.TargetProperty="Opacity"From="1.0"To="0.0"Duration="0:0:2"AutoReverse="True"/></Storyboard></StackPanel.Resources><Rectanglex:Name="MyAnimatedRectangle1"Margin="3"Width="100"Height="100"Fill="Blue"MouseLeftButtonDown="Start_Animation"/><Rectanglex:Name="MyAnimatedRectangle2"Margin="3"Width="100"Height="100"Fill="Blue"MouseLeftButtonDown="Start_Animation"/><Rectanglex:Name="MyAnimatedRectangle3"Margin="3"Width="100"Height="100"Fill="Blue"MouseLeftButtonDown="Start_Animation"/><Rectanglex:Name="MyAnimatedRectangle4"Margin="3"Width="100"Height="100"Fill="Blue"MouseLeftButtonDown="Start_Animation"/></StackPanel>
privatevoid Start_Animation(object sender, MouseEventArgs e) { // If the Storyboard is running and you try to change// properties of its animation objects programmatically, // an error will occur. myStoryboard.Stop(); // Get a reference to the rectangle that was clicked. Rectangle myRect = (Rectangle)sender; // Change the TargetName of the animation to the name of the// rectangle that was clicked. myDoubleAnimation.SetValue(Storyboard.TargetNameProperty, myRect.Name); // Begin the animation. myStoryboard.Begin(); }
在前面的代码中,请注意您在动态更改动画对象的属性前必须停止 Storyboard,否则将出错。 在此示例中,可能不希望只有停止一个矩形的动画才能启动另一个矩形的动画。 可能您想同时运行这两个动画。 但是,您不能使用同一个动画对象同时运行两个独立的动画,因为只有一个 TargetName。 这并不意味着您不得不重新为每个对象创建单独的 Storyboard。 您只需要为并发(同步)运行的每个动画提供一个 Storyboard。 下面的示例与上一个示例类似,只是它包含三个而不是一个Storyboard 对象。 您单击矩形时,事件处理程序查找当前未使用的 Storyboard 并使用它来创建动画。
<StackPanelOrientation="Horizontal"><StackPanel.Resources><Storyboardx:Name="myStoryboard1"Completed="Storyboard_Completed"><DoubleAnimationx:Name="myDoubleAnimation1"Storyboard.TargetProperty="Opacity"From="1.0"To="0.0"Duration="0:0:2"AutoReverse="True"/></Storyboard><Storyboardx:Name="myStoryboard2"Completed="Storyboard_Completed"><DoubleAnimationx:Name="myDoubleAnimation2"Storyboard.TargetProperty="Opacity"From="1.0"To="0.0"Duration="0:0:2"AutoReverse="True"/></Storyboard><Storyboardx:Name="myStoryboard3"Completed="Storyboard_Completed"><DoubleAnimationx:Name="myDoubleAnimation3"Storyboard.TargetProperty="Opacity"From="1.0"To="0.0"Duration="0:0:2"AutoReverse="True"/></Storyboard></StackPanel.Resources><Rectanglex:Name="MyAnimatedRectangle1"Margin="3"Width="100"Height="100"Fill="Blue"MouseLeftButtonDown="Start_Animation"/><Rectanglex:Name="MyAnimatedRectangle2"Margin="3"Width="100"Height="100"Fill="Blue"MouseLeftButtonDown="Start_Animation"/><Rectanglex:Name="MyAnimatedRectangle3"Margin="3"Width="100"Height="100"Fill="Blue"MouseLeftButtonDown="Start_Animation"/><Rectanglex:Name="MyAnimatedRectangle4"Margin="3"Width="100"Height="100"Fill="Blue"MouseLeftButtonDown="Start_Animation"/></StackPanel>
bool storyboard1Active = false; bool storyboard2Active = false; bool storyboard3Active = false; privatevoid Start_Animation(object sender, MouseEventArgs e) { // Get a reference to the rectangle that was clicked. Rectangle myRect = (Rectangle)sender; if (!storyboard1Active) { myStoryboard1.Stop(); myDoubleAnimation1.SetValue(Storyboard.TargetNameProperty, myRect.Name); myStoryboard1.Begin(); storyboard1Active = true; } elseif (!storyboard2Active) { myStoryboard2.Stop(); myDoubleAnimation2.SetValue(Storyboard.TargetNameProperty, myRect.Name); myStoryboard2.Begin(); storyboard2Active = true; } elseif (!storyboard3Active) { myStoryboard3.Stop(); myDoubleAnimation3.SetValue(Storyboard.TargetNameProperty, myRect.Name); myStoryboard3.Begin(); storyboard3Active = true; } } privatevoid Storyboard_Completed(object sender, EventArgs e) { Storyboard myStoryboard = sender as Storyboard; switch (myStoryboard.GetValue(NameProperty).ToString()) { case"myStoryboard1": storyboard1Active = false; break; case"myStoryboard2": storyboard2Active = false; break; case"myStoryboard3": storyboard3Active = false; break; } }
在上面的示例中,同时只能运行三个动画(等于 Storyboard 对象的数目)。 如果您不需要同时运行更多动画,这个示例就可以满足要求了,否则将需要更多的Storyboard 对象。 如果要同时运行很多独立的动画,可能要动态创建 Storyboard 对象。 有关在代码中创建演示图板对象的示例,请参见下一节。
您还可以完全在程序代码中创建动画。 下面的示例演示如何创建一个动画,在其中用动画呈现矩形的 Canvas.Top 和 Canvas.Left 附加属性。
private void Create_And_Run_Animation(object sender, EventArgs e) { // Create a red rectangle that will be the target // of the animation. Rectangle myRectangle = new Rectangle(); myRectangle.Width = 200; myRectangle.Height = 200; Color myColor = Color.FromArgb(255, 255, 0, 0); SolidColorBrush myBrush = new SolidColorBrush(); myBrush.Color = myColor; myRectangle.Fill = myBrush; // Add the rectangle to the tree. LayoutRoot.Children.Add(myRectangle); // Create a duration of 2 seconds. Duration duration = new Duration(TimeSpan.FromSeconds(2)); // Create two DoubleAnimations and set their properties. DoubleAnimation myDoubleAnimation1 = new DoubleAnimation(); DoubleAnimation myDoubleAnimation2 = new DoubleAnimation(); myDoubleAnimation1.Duration = duration; myDoubleAnimation2.Duration = duration; Storyboard sb = new Storyboard(); sb.Duration = duration; sb.Children.Add(myDoubleAnimation1); sb.Children.Add(myDoubleAnimation2); Storyboard.SetTarget(myDoubleAnimation1, myRectangle); Storyboard.SetTarget(myDoubleAnimation2, myRectangle); // Set the attached properties of Canvas.Left and Canvas.Top // to be the target properties of the two respective DoubleAnimations. Storyboard.SetTargetProperty(myDoubleAnimation1, new PropertyPath("(Canvas.Left)")); Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(Canvas.Top)")); myDoubleAnimation1.To = 200; myDoubleAnimation2.To = 200; // Make the Storyboard a resource. LayoutRoot.Resources.Add("unique_id", sb); // Begin the animation. sb.Begin(); }
![]() |
---|
不要试图在页面的构造函数中调用 Storyboard 成员(例如 Begin 方法)。 这将导致动画失败,且无任何提示。 |