MCTS Self-Paced Training: Styles and Animation in Windows Presentation Foundation
- 7/9/2008
Using Animations
The term animation brings to mind hand-drawn anthropomorphic animals performing amusing antics in video media, but in WPF, animation has a far simpler meaning. Generally speaking, an animation in WPF refers to an automated property change over a set period of time. You can animate an element’s size, location, color, or virtually any other property or properties associated with an element. You can use the Animation classes to implement these changes.
The Animation classes are a large group of classes designed to implement these automated property changes. There are 42 Animation classes in the System.Windows.Media.Animation namespace, and each one has a specific data type that they are designed to animate. Animation classes fall into three basic groups: Linear animations, key frame–based animations, and path-based animations.
Linear animations, which automate a property change in a linear way, are named in the format <TypeName>Animation, where <TypeName> is the name of the type being animated. DoubleAnimation is an example of a linear animation class, and that is the animation class you are likely to use the most.
Key frame–based animations perform their animation on the basis of several waypoints, called key frames. The flow of a key-frame animation starts at the beginning, and then progresses to each of the key frames before ending. The progression is usually linear. Key-frame animations are named in the format <TypeName>AnimationUsingKeyFrames, where <TypeName> is the name of the Type being animated. An example is StringAnimationUsingKeyFrames.
Path-based animations use a Path object to guide the animation. They are used most often to animate properties that relate to the movement of visual objects along a complex course. Path-based animations are named in the format <TypeName>AnimationUsingPath, where <TypeName> is the name of the type being animated. There are currently only three path-based Animation classes—PointAnimationUsingPath, DoubleAnimationUsingPath, and MatrixAnimationUsingPath.
Important Properties of Animations
Although there are many different Animation classes, they all work in the same fundamental way—they change the value of a designated property over a period of time. As such, they share common properties. Many of these properties also are shared with the Storyboard class, which is used to organize Animation objects, as you will see later in this lesson. Important common properties of the Animation and Storyboard classes are shown in Table 7-4.
Table 7-4 Important Properties of the Animation and Storyboard Classes
Property |
Description |
AccelerationRatio |
Gets or sets a value specifying the percentage of the Duration property of the Animation that is spent accelerating the passage of time from zero to its maximum rate. |
AutoReverse |
Gets or sets a value that indicates whether the Animation plays in reverse after it completes a forward iteration. |
BeginTime |
Gets or sets the time at which the Animation should begin, relative to the time that the Animation is executed. For example, an Animation with a BeginTime set to 0:0:5 exhibits a 5-second delay before beginning. |
DecelerationRatio |
Gets or sets a value specifying the percentage of the duration of the Animation spent decelerating the passage of time from its maximum rate to zero. |
Duration |
Gets or sets the length of time for which the Animation plays. |
FillBehavior |
Gets or sets a value that indicates how the Animation behaves after it has completed. |
RepeatBehavior |
Gets or sets a value that indicates how the Animation repeats. |
SpeedRatio |
Gets or sets the rate at which the Animation progresses relative to its parent. |
In addition, the linear animation classes typically implement a few more important properties, which are described in Table 7-5.
Table 7-5 Important Properties of Linear Animation Classes
Property |
Description |
From |
Gets or sets the starting value of the Animation. If omitted, the Animation uses the current property value. |
To |
Gets or sets the ending value of the Animation. |
By |
Gets or sets the amount by which to increase the value of the target property over the course of the Animation. If both the To and By properties are set, the value of the By property is ignored. |
The following example demonstrates a very simple animation. This animation changes the value of a property that has a Double data type representation from 1 to 200 over the course of 10 seconds:
<DoubleAnimation Duration="0:0:10" From="1" To="200" />
In this example, the Duration property specifies a duration of 10 seconds for the animation, and the From and To properties indicate a starting value of 1 and an ending value of 200.
You might notice that something seems to be missing from this example. What property is this animation animating? The answer is that it is not animating any property—the Animation object carries no intrinsic information about the property that is being animated, but instead it is applied to a property by means of a Storyboard.
Storyboard Objects
The Storyboard is the object that controls and organizes animations in your user interface. The Storyboard class contains a Children collection, which organizes a collection of Timeline objects, which include Animation objects. When created declaratively in XAML, all Animation objects must be enclosed within a Storyboard object, as shown here:
<Storyboard> <DoubleAnimation Duration="0:0:10" From="1" To="200" /> </Storyboard>
Using a Storyboard to Control Animations
In XAML, Storyboard objects organize your Animation objects. The most important feature of the Storyboard object is that it contains properties that allow you to specify the target element and target property of the child Animation objects, as shown in bold in this example:
<Storyboard TargetName="Button1" TargetProperty="Height"> <DoubleAnimation Duration="0:0:10" From="1" To="200" /> </Storyboard>
This example is now usable. It defines a timeline where over the course of 10 seconds, the Height property of Button1 goes from a value of 1 to a value of 200.
The TargetName and TargetProperty properties are attached properties, so instead of defining them in the Storyboard itself, you can define them in the child Animation objects, as shown in bold here:
<Storyboard> <DoubleAnimation Duration="0:0:10" From="1" To="200" Storyboard.TargetName="Button1" Storyboard.TargetProperty="Height" /> </Storyboard>
Because a Storyboard can hold more than one Animation at a time, this configuration allows you to set separate target elements and properties for each animation. Thus, it is more common to use the attached properties.
Simultaneous Animations
The Storyboard can contain multiple child Animation objects. When the Storyboard is activated, all child animations are started at the same time and run simultaneously. The following example demonstrates two simultaneous Animations that cause both the Height and Width of a Button element to grow over 10 seconds:
<Storyboard> <DoubleAnimation Duration="0:0:10" From="1" To="200" Storyboard.TargetName="Button1" Storyboard.TargetProperty="Height" /> <DoubleAnimation Duration="0:0:10" From="1" To="100" Storyboard.TargetName="Button1" Storyboard.TargetProperty="Widtht" /> </Storyboard>
Using Animations with Triggers
You now have learned most of the story about using Animation objects. The Animation object defines a property change over time, and the Storyboard object contains Animation objects and determines what element and property the Animation objects affect. But there is still one piece that is missing: How do you start and stop an Animation?
All declaratively created Animation objects must be housed within a Trigger object. This can be either as a part of a Style, or in the Triggers collection of an Element, which accepts only EventTrigger objects.
Trigger objects define collections of Action objects, which control when an Animation is started and stopped. The following example demonstrates an EventTrigger object with an inline Animation:
<EventTrigger RoutedEvent="Button.Click"> <EventTrigger.Actions> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:5" Storyboard.TargetProperty="Height" To="200" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger>
As you can see in the preceding example, the Storyboard object is enclosed in a BeginStoryboard tag, which itself is enclosed in the EventTrigger.Actions tag. BeginStoryboard is an Action—it indicates that the contained Storyboard should be started. The EventTrigger class defines a collection of Actions that should be initiated when the Trigger is activated, and in this example, BeginStoryboard is the action that is initiated. Thus, when the Button indicated in this trigger is clicked, the described Animation runs.
Using Actions to Control Playback
There are several Action classes that can be used to manage animation playback. These classes are summarized in Table 7-6.
Table 7-6 Animation-Related Action Classes
Action |
Description |
BeginStoryboard |
Begins the child Storyboard object. |
PauseStoryboard |
Pauses the playback of an indicated Storyboard at the current playback position. |
ResumeStoryboard |
Resumes playback of an indicated Storyboard. |
SeekStoryboard |
Fast-forwards to a specified position in a target Storyboard. |
SetStoryboardSpeedRatio |
Sets the SpeedRatio of the specified Storyboard. |
SkipStoryboardToFill |
Moves the specified Storyboard to the end of its timeline. |
StopStoryboard |
Stops playback of the specified Storyboard and returns the animation to the starting position. |
PauseStoryboard, ResumeStoryboard, SkipStoryboardToFill, and StopStoryboard are all fairly self-explanatory. They cause the indicated Storyboard to pause, resume, stop, or skip to the end, as indicated by the Action name. The one property that all these Action classes have in common is the BeginStoryboardName property. This property indicates the name of the BeginStoryboard object that the action is to affect. The following example demonstrates a StopStoryboard action that stops the BeginStoryBoard object named stb1:
<Style.Triggers> <EventTrigger RoutedEvent="Button.MouseEnter"> <EventTrigger.Actions> <BeginStoryboard Name="stb1"> <Storyboard> <DoubleAnimation Duration="0:0:5" Storyboard.TargetProperty="Height" To="200" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> <EventTrigger RoutedEvent="Button.MouseLeave"> <EventTrigger.Actions> <StopStoryboard BeginStoryboardName="stb1" /> </EventTrigger.Actions> </EventTrigger> </Style.Triggers>
All Actions that affect a particular Storyboard object must be defined in the same Triggers collection. The previous example shows both of these triggers being defined in the Button.Triggers collection. If you were to define these triggers in separate Triggers collections, storyboard actions would not function.
The SetStoryboardSpeedRatio action sets the speed ratio for the entire Storyboard and all Animation objects in that Storyboard. In addition to BeginStoryboardName, you must set the SpeedRatio property of this Action as well. The following example demonstrates a SetStoryboardSpeedRatio action that speeds the referenced Storyboard by a factor of 2:
<Style.Triggers> <EventTrigger RoutedEvent="Button.MouseEnter"> <EventTrigger.Actions> <BeginStoryboard Name="stb1"> <Storyboard> <DoubleAnimation Duration="0:0:5" Storyboard.TargetProperty="Height" To="200" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> <EventTrigger RoutedEvent="Button.MouseLeave"> <EventTrigger.Actions> <SetStoryboardSpeedRatio BeginStoryboardName="stb1" SpeedRatio="2" /> </EventTrigger.Actions> </EventTrigger> </Style.Triggers>
The SeekStoryboard action requires two additional properties to be set. The Origin property can be either a value of BeginTime or of Duration and specifies how the Offset property is applied. An Origin value of BeginTime specifies that the Offset is relative to the beginning of the Storyboard. An Origin value of Duration specifies that the Offset is relative to the Duration property of the Storyboard. The Offset property determines the amount of the offset to jump to in the animation. The following example shows a Seek-Storyboard action that skips the referenced timeline to 5 seconds ahead from its current point in the timeline.
<Style.Triggers> <EventTrigger RoutedEvent="Button.MouseEnter"> <EventTrigger.Actions> <BeginStoryboard Name="stb1"> <Storyboard> <DoubleAnimation Duration="0:0:10" Storyboard.TargetProperty="Height" To="200" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> <EventTrigger RoutedEvent="Button.MouseLeave"> <EventTrigger.Actions> <SeekStoryboard BeginStoryboardName="stb1" Origin="BeginTime" Offset="0:0:5" /> </EventTrigger.Actions> </EventTrigger> </Style.Triggers>
Using Property Triggers with Animations
In the examples shown in this section, you have seen Actions being hosted primarily in EventTrigger objects. You can also host Action objects in other kinds of Triggers. Trigger, MultiTrigger, DataTrigger, and MultiDataTrigger objects host two Action collections: EnterActions and ExitActions collections.
The EnterActions collection hosts a set of Actions that are executed when the Trigger is activated. Conversely, the ExitActions collection hosts a set of Actions that are executed when the Trigger is deactivated. The following demonstrates a Trigger that begins a Storyboard when activated and stops that Storyboard when deactivated:
<Trigger Property="IsMouseOver" Value="True"> <Trigger.EnterActions> <BeginStoryboard Name="stb1"> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="FontSize" To="20" Duration="0:0:.5" /> </Storyboard> </BeginStoryboard> </Trigger.EnterActions> <Trigger.ExitActions> <StopStoryboard BeginStoryboardName="stb1" /> </Trigger.ExitActions> </Trigger>
Managing the Playback Timeline
Both the Animation class and the Storyboard class contain several properties that allow you to manage the playback timeline with a fine level of control. Each of these properties is discussed in this section. When a property is set on an Animation, the setting affects only that animation. Setting a property on a Storyboard, however, affects all Animation objects it contains.
AccelerationRatio and DecelerationRatio
The AccelerationRatio and DecelerationRatio properties allow you to designate a part of the timeline for acceleration and deceleration of the animation speed, rather than starting and playing at a constant speed. This is used sometimes to give an animation a more “natural” appearance. These properties are expressed in fractions of 1 and represent a percentage value of the total timeline. Thus, an AccelerationRatio with a value of .2 indicates that 20 percent of the timeline should be spent accelerating to the top speed. So the AccelerationRatio and DecelerationRatio properties should be equal to or less than 1 when added together. This example shows an Animation with an AccelerationRatio of .2:
<DoubleAnimation Duration="0:0:5" AccelerationRatio="0.2" Storyboard.TargetProperty="Height" To="200" />
AutoReverse
As the name implies, the AutoReverse property determines whether the animation automatically plays out in reverse after the end is reached. A value of True indicates that the Animation will play in reverse after the end is reached. False is the default value. The following example demonstrates this property:
<DoubleAnimation Duration="0:0:5" AutoReverse="True" Storyboard.TargetProperty="Height" To="200" />
FillBehavior
The FillBehavior property determines how the Animation behaves after it has completed. A value of HoldEnd indicates that the Animation holds the final value after it has completed, whereas a value of Stop indicates that the Animation stops and returns to the beginning of the timeline when completed. An example is shown here:
<DoubleAnimation Duration="0:0:5" FillBehavior="Stop" Storyboard.TargetProperty="Height" To="200" />
The default value for FillBehavior is HoldEnd.
RepeatBehavior
The RepeatBehavior property determines if and how an animation repeats. The Repeat-Behavior property can be set in three ways. First, it can be set to Forever, which indicates that an Animation repeats for the duration of the application. Second, it can be set to a number followed by the letter x (for example, 2x), which indicates the number of times to repeat the animation. Third, it can be set to a Duration, which indicates the amount of time that an Animation plays, irrespective of the number of iterations. The following three examples demonstrate these settings. The first demonstrates an Animation that repeats forever, the second an Animation that repeats three times, and the third an Animation that repeats for 1 minute:
<DoubleAnimation Duration="0:0:5" RepeatBehavior="Forever" Storyboard.TargetProperty="Height" To="200" /> <DoubleAnimation Duration="0:0:5" RepeatBehavior="3x" Storyboard.TargetProperty="Height" To="200" /> <DoubleAnimation Duration="0:0:5" RepeatBehavior="0:1:0" Storyboard.TargetProperty="Height" To="200" />
SpeedRatio
The SpeedRatio property allows you to speed up or slow down the base timeline. The SpeedRatio value represents the coefficient for the speed of the Animation. Thus, an Animation with a SpeedRatio value of 0.5 takes twice the standard time to complete, whereas a value of 2 causes the Animation to complete twice as fast. An example is shown here:
<DoubleAnimation Duration="0:0:5" SpeedRatio="0.5" Storyboard.TargetProperty="Height" To="200" />
Animating Non-Double Types
Most of the examples that you have seen in this lesson have dealt with the DoubleAnimation class, but in fact a class exists for every animatable data type. For example, the ColorAnimation class allows you to animate a color change, as shown here:
<Button Height="23" Width="100" Name="Button1"> <Button.Background> <SolidColorBrush x:Name="myBrush" /> </Button.Background> <Button.Triggers> <EventTrigger RoutedEvent="Button.Click"> <BeginStoryboard> <Storyboard> <ColorAnimation Storyboard.TargetName="myBrush" Storyboard.TargetProperty="Color" From="Red" To="LimeGreen" Duration="0:0:5" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Button.Triggers> </Button>
In this example, when the button is clicked, the background color of the button gradually changes from red to lime green over the course of 5 seconds.
Animation with Key Frames
Up until now, all the animations you have seen have used linear interpolation—that is, the animated property changes take place over a linear timeline at a linear rate. You also can create nonlinear animations by using key frames.
Key frames are waypoints in an animation. Instead of allowing the Animation to progress linearly from beginning to end, key frames divide the animation up into short segments. The animation progresses from the beginning to the first key frame, then the next, and through the KeyFrames collection until the end of the animation is reached. Each key frame defines its own Value and KeyTime properties, which indicate the value that the Animation will represent when it reaches the key frame and the time in the Animation at which that frame will be reached.
Every data type that supports a linear Animation type also supports a key-frame Animation type, and some types that do not have linear animation types have key-frame Animation types. The key-frame Animation types are named <TargetType>AnimationUsingKeyFrames, where <TargetType> represents the name of the Type animated by the Animation. Key-frame Animation types do not support the From, To, and By properties; rather, the course of the Animation is defined by the collection of key frames.
There are three different kinds of key frames. The first is linear key frames, which are named Linear<TargetType>KeyFrame. These key frames provide points in an Animation that are interpolated between in a linear fashion. The following example demonstrates the use of linear key frames:
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Height"> <LinearDoubleKeyFrame Value="10" KeyTime="0:0:1" /> <LinearDoubleKeyFrame Value="100" KeyTime="0:0:2" /> <LinearDoubleKeyFrame Value="30" KeyTime="0:0:4"/> </DoubleAnimationUsingKeyFrames>
In the preceding example, the Height property goes from its starting value to a value of 10 in the first second, then to a value of 100 in the next second, and finally returns to a value of 30 in the last 2 seconds. The progression between each segment is interpolated linearly. In this example, it is similar to having several successive linear Animation objects.
Discrete Key Frames
Some animatable data types do not support gradual transitions under any circumstances. For example, the String type can only accept discrete changes. You can use discrete key frame objects to make discrete changes in the value of an animated property. Discrete key frame classes are named Discrete<TargetType>KeyFrame, where <TargetType> is the Type being animated. Like linear key frames, discrete key frames use a Value and a KeyTime property to set the parameters of the key frame. The following example demonstrates an animation of a String using discrete key frames:
<StringAnimationUsingKeyFrames Storyboard.TargetProperty="Content"> <DiscreteStringKeyFrame Value="Soup" KeyTime="0:0:0" /> <DiscreteStringKeyFrame Value="Sous" KeyTime="0:0:1" /> <DiscreteStringKeyFrame Value="Sots" KeyTime="0:0:2" /> <DiscreteStringKeyFrame Value="Nots" KeyTime="0:0:3" /> <DiscreteStringKeyFrame Value="Nuts" KeyTime="0:0:4" /> </StringAnimationUsingKeyFrames>
Spline Key Frames
Spline key frames allow you to define a Bézier curve that expresses the relationship between animation speed and animation time, thus allowing you to create animations that accelerate and decelerate in complex ways. While the mathematics of Bézier curves is beyond the scope of this lesson, a Bézier curve is simply a curve between two points whose shape is influenced by two control points. Using spline key frames, the start and end points of the curve are always (0,0) and (1,1) respectively, so you must define the two control points. The KeySpline property accepts two points to define the Bézier curve, as seen here:
<SplineDoubleKeyFrame Value="300" KeyTime="0:0:6" KeySpline="0.1,0.8 0.6,0.6" />
Spline key frames are difficult to create with the intended effect without complex design tools, and are most commonly used when specialized animation design tools are available.
Using Multiple Types of Key Frames in an Animation
You can use multiple types of key frames in a single animation—you can freely intermix LinearKeyFrame, DiscreteKeyFrame, and SplineKeyFrame objects in the KeyFrames collection. The only restriction is that all key frames you use must be appropriate to the Type that is being animated. String animations, for example, can use only DiscreteStringKeyFrame objects.
Creating and Starting Animations in Code
All the Animation objects that you have seen so far in this lesson were created declaratively in XAML. However, you can create and execute Animation objects just as easily in code as well.
The process of creating an Animation should seem familiar to you; as with other .NET objects, you create a new instance of your Animation and set the relevant properties, as seen in this example:
' VB Dim aAnimation As New System.Windows.Media.Animation.DoubleAnimation() aAnimation.From = 20 aAnimation.To = 300 aAnimation.Duration = New Duration(New TimeSpan(0, 0, 5)) aAnimation.FillBehavior = Animation.FillBehavior.Stop // C# System.Windows.Media.Animation.DoubleAnimation aAnimation = new System.Windows.Media.Animation.DoubleAnimation(); aAnimation.From = 20; aAnimation.To = 300; aAnimation.Duration = new Duration(new TimeSpan(0, 0, 5)); aAnimation.FillBehavior = Animation.FillBehavior.Stop;
After the Animation has been created, however, the obvious question is: How do you start it? When creating Animation objects declaratively, you must use a Storyboard to organize your Animation and an Action to start it. In code, however, you can use a simple method call. All WPF controls expose a method called BeginAnimation, which allows you to specify a dependency property on that control and an Animation object to act on that dependency property. The following code shows an example:
' VB Button1.BeginAnimation(Button.HeightProperty, aAnimation) // C# button1.BeginAnimation(Button.HeightProperty, aAnimation);
Lab: Improving Readability with Animations
In this lab, you improve upon your solution to the lab in Lesson 1 of this chapter. You remove the triggers that cause the FontSize to expand and instead use an Animation to make it look more natural. In addition, you create Animation objects to increase the size of the control when the mouse is over it.
Exercise: Animating High-Contrast Styles
Open the completed solution from the lab from Lesson 1 of this chapter.
In each of the Styles, remove the FontSize Setter that is defined in the Trigger and replace it with a Trigger.EnterActions and Trigger.ExitActions section, as shown here:
<Trigger.EnterActions> </Trigger.EnterActions> <Trigger.ExitActions> </Trigger.ExitActions>
In each Trigger.EnterActions section, add a BeginStoryboard action, as shown here:
<BeginStoryboard Name="Storyboard1"> </BeginStoryboard>
Add the following Storyboard and Animation objects to the BeginStoryboard object in the style for the TextBox. Note that the values for the ThicknessAnimation object are crafted specifically for the completed version of the Lesson 1 lab on the CD. If you created your own solution, you need to recalculate these values:
<Storyboard Duration="0:0:1"> <DoubleAnimation Storyboard.TargetProperty="FontSize" To="20" /> <ThicknessAnimation Storyboard.TargetProperty="Margin" To="26,118,45,104" /> <DoubleAnimation Storyboard.TargetProperty="Width" To="210"/> <DoubleAnimation Storyboard.TargetProperty="Height" To="40"/> </Storyboard>
Add a similar Storyboard to the style for the Label, as shown here:
<Storyboard Duration="0:0:1"> <DoubleAnimation Storyboard.TargetProperty="FontSize" To="20" /> <ThicknessAnimation Storyboard.TargetProperty="Margin" To="26,62,46,-10" /> <DoubleAnimation Storyboard.TargetProperty="Width" To="210"/> <DoubleAnimation Storyboard.TargetProperty="Height" To="40"/> </Storyboard>
Add a similar Storyboard to the style for the Button, as shown here:
<Storyboard Duration="0:0:1"> <DoubleAnimation Storyboard.TargetProperty="FontSize" To="20" /> <ThicknessAnimation Storyboard.TargetProperty="Margin" To="26,0,46,52" /> <DoubleAnimation Storyboard.TargetProperty="Width" To="210"/> <DoubleAnimation Storyboard.TargetProperty="Height" To="40"/> </Storyboard>
Add the following line to the Trigger.ExitActions section of each Style:
<StopStoryboard BeginStoryboardName="Storyboard1" />
Press F5 to build and run your application. Now the FontSize expansion is animated and the control expands as well.
Lesson Summary
Animation objects drive automated property changes over time. There are three different types of Animation objects—linear animations, key frame–based animations, and path-based animations. Every animatable type has at least one Animation type associated with it, and some types have more than one type of Animation that can be applied.
Storyboard objects organize one or more Animation objects. Storyboard objects determine what objects and properties their contained Animation objects are applied to.
Both Animation and Storyboard objects contain a variety of properties that control Animation playback behavior.
Storyboard objects that are created declaratively are activated by a BeginStory-board action in the Actions collection of a Trigger. Triggers also can define actions that pause, stop, and resume Storyboard objects, as well as performing other Storyboard-related functions.
Key frame animations define a series of waypoints through which the Animation passes. There are three kinds of key frames: linear key frames, discrete key frames, and spline key frames. Some animatable types, such as String, support only discrete key frames.
You can create and apply Animation objects in code. When doing this, you do not need to define a Storyboard object; rather, you call the BeginAnimation method on the element with which you want to associate the Animation.
Lesson Review
You can use the following questions to test your knowledge of the information in Lesson 2, “Animations.” The questions are also available on the companion CD if you prefer to review them in electronic form.
How many times does the Animation shown here repeat (not counting the first iteration)?
<DoubleAnimation Duration="0:0:15" RepeatBehavior="0:1:0" Storyboard.TargetProperty="Height" To="200" />
0
1
2
3
Look at this Animation:
<DoubleAnimation Duration="0:0:5" From="30" By="80" To="200" Storyboard.TargetProperty="Height" />
Assuming that the element whose Height property it animates begins with a Height of 50, what is the value of the element after the animation has completed?
50
110
130
200