以编程方式使用动画
有时您可能要动态(即时)更改动画的属性。例如,您可能要调整应用到对象的动画行为,这取决于对象当前在布局中的位置、对象包含何种内容等等。可以通过使用程序代码(例如 C# 或 Visual Basic)动态操作动画。
本主题包括下列各节。
访问动画对象以更改其属性的最直接方法是:命名该动画对象,然后在代码中通过该名称引用它。下面的示例包含一个 Ellipse,当您在屏幕上单击时它将显示动画效果。为了实现此动画,在单击 Canvas 时,事件处理程序更改 PointAnimation 对象的 To 属性,然后启动动画。
<Canvas MouseLeftButtonDown="Handle_MouseDown" Background="Gray" Width="600" Height="500"> <Canvas.Resources> <Storyboard x: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. --> <PointAnimation x:Name="myPointAnimation" Storyboard.TargetProperty="Center" Storyboard.TargetName="MyAnimatedEllipseGeometry" Duration="0:0:2"/> </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>
private void 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. } }
下面的示例与上一个示例类似,即在用户单击屏幕的地方出现椭圆,只是此示例中使用了关键帧。对关键帧的集合进行迭代并为关键帧动态设置值,以便使椭圆动画出现在合适的位置。
<Canvas MouseLeftButtonDown="Handle_MouseDown" 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"> <DiscretePointKeyFrame KeyTime="0:0:0" /> <LinearPointKeyFrame KeyTime="0:0:0.5" /> <SplinePointKeyFrame KeySpline="0.6,0.0 0.9,0.00" KeyTime="0:0:2" /> </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>
// Global variables that keep track of the end point // of the last animation. double lastX = 200; double lastY = 100; private void 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)); } else if (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)); } else if (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 更改为所单击的矩形。
<StackPanel Orientation="Horizontal"> <StackPanel.Resources> <Storyboard x:Name="myStoryboard"> <DoubleAnimation x:Name="myDoubleAnimation" Storyboard.TargetProperty="Opacity" From="1.0" To="0.0" Duration="0:0:2" AutoReverse="True" /> </Storyboard> </StackPanel.Resources> <Rectangle x:Name="MyAnimatedRectangle1" Margin="3" Width="100" Height="100" Fill="Blue" MouseLeftButtonDown="Start_Animation" /> <Rectangle x:Name="MyAnimatedRectangle2" Margin="3" Width="100" Height="100" Fill="Blue" MouseLeftButtonDown="Start_Animation" /> <Rectangle x:Name="MyAnimatedRectangle3" Margin="3" Width="100" Height="100" Fill="Blue" MouseLeftButtonDown="Start_Animation" /> <Rectangle x:Name="MyAnimatedRectangle4" Margin="3" Width="100" Height="100" Fill="Blue" MouseLeftButtonDown="Start_Animation" /> </StackPanel>
private void 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 并使用它来创建动画。
<StackPanel Orientation="Horizontal"> <StackPanel.Resources> <Storyboard x:Name="myStoryboard1" Completed="Storyboard_Completed"> <DoubleAnimation x:Name="myDoubleAnimation1" Storyboard.TargetProperty="Opacity" From="1.0" To="0.0" Duration="0:0:2" AutoReverse="True" /> </Storyboard> <Storyboard x:Name="myStoryboard2" Completed="Storyboard_Completed"> <DoubleAnimation x:Name="myDoubleAnimation2" Storyboard.TargetProperty="Opacity" From="1.0" To="0.0" Duration="0:0:2" AutoReverse="True" /> </Storyboard> <Storyboard x:Name="myStoryboard3" Completed="Storyboard_Completed"> <DoubleAnimation x:Name="myDoubleAnimation3" Storyboard.TargetProperty="Opacity" From="1.0" To="0.0" Duration="0:0:2" AutoReverse="True" /> </Storyboard> </StackPanel.Resources> <Rectangle x:Name="MyAnimatedRectangle1" Margin="3" Width="100" Height="100" Fill="Blue" MouseLeftButtonDown="Start_Animation" /> <Rectangle x:Name="MyAnimatedRectangle2" Margin="3" Width="100" Height="100" Fill="Blue" MouseLeftButtonDown="Start_Animation" /> <Rectangle x:Name="MyAnimatedRectangle3" Margin="3" Width="100" Height="100" Fill="Blue" MouseLeftButtonDown="Start_Animation" /> <Rectangle x:Name="MyAnimatedRectangle4" Margin="3" Width="100" Height="100" Fill="Blue" MouseLeftButtonDown="Start_Animation" /> </StackPanel>
bool storyboard1Active = false; bool storyboard2Active = false; bool storyboard3Active = false; private void 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; } else if (!storyboard2Active) { myStoryboard2.Stop(); myDoubleAnimation2.SetValue(Storyboard.TargetNameProperty, myRect.Name); myStoryboard2.Begin(); storyboard2Active = true; } else if (!storyboard3Active) { myStoryboard3.Stop(); myDoubleAnimation3.SetValue(Storyboard.TargetNameProperty, myRect.Name); myStoryboard3.Begin(); storyboard3Active = true; } } private void 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 方法)。这将导致动画失败,且无任何提示。 |

浙公网安备 33010602011771号