WPF enables users to apply animations to objects by applying animations on properties. For a property to have animation capabilities, it should have the following requirements.
The property should be a dependency property.
It must belong to a class that inherits from the DependencyObject class and implements the IAnimatable interface.
There must be a compatible Animation type available for the property. For e.g. the Double type property can be animated using the DoubleAnimation. Similarly, the property to be animated should contain the corresponding animation type.
Note that in WPF all animations (element.BeginAnimation method) are executed asynchronously. So, if you call a bunch of BeginAnimation methods one after another in a for loop, they all will overlap and not really execute one after another.
There is really no way to chain animations in WPF. You will have to basically listen to an animation’s Completed event and then trigger the next animation. To make such code efficient and scalable I came up with this recursion with in-line event handler based approach:
Let us say you want to increase the width of these 2 rectangles one after the other via animation:
[C#]
privatevoidWindow_Loaded(object sender, RoutedEventArgs e)
{
DoubleAnimation da = new DoubleAnimation(100, new Duration(TimeSpan.FromSeconds(3)));
List list = new List();
list.Add(newobject[] { rect1, Rectangle.WidthProperty, da});
list.Add(newobject[] { rect2, Rectangle.WidthProperty, da });
this.PerformAnimations(0, list);
}
privatevoidPerformAnimations(int index, List lstDefinitions)
{
object[] definition = lstDefinitions[index] asobject[];
AnimationTimeline animation = definition[2] as AnimationTimeline;
animation.Completed += delegate
{
// Start the other animation after the end of the previous animation.
index++;
if (lstDefinitions.Count > index)
this.PerformAnimations(index, lstDefinitions);
};
((UIElement)definition[0]).BeginAnimation((DependencyProperty)definition[1], (AnimationTimeline)definition[2]);
}
The idea is to maintain a list of ‘animation definitiions’ and execute animations from this list recursively.
By default, the value of the property on which the animation is applied is overwritten to the ’To’ value of the animation at the end of the animation. But, instead you can reset the property’s value to it’s original value by setting the ‘FillBehavior’ property of the animation to ‘Stop’ (by default it’s set to the HoldEnd enum).
The following example uses controllable storyboard actions to interactively control a storyboard.
[XAML]
<Pagexmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'WindowTitle='Controlling a Storyboard' ><StackPanelMargin='20' ><!-- This rectangle is animated. --><RectangleName='myRectangle'Width='100'Height='20'Margin='12,0,0,5'Fill='#AA3333FF'HorizontalAlignment='Left' /><!-- This StackPanel contains all the Buttons. --><StackPanelOrientation='Horizontal'Margin='0,30,0,0'><ButtonName='BeginButton'>Begin</Button><ButtonName='PauseButton'>Pause</Button><ButtonName='ResumeButton'>Resume</Button><ButtonName='SeekButton'>Seek</Button><ButtonName='SkipToFillButton'>Skip To Fill</Button><ButtonName='SetSpeedRatioButton'>Triple Speed</Button><ButtonName='StopButton'>Stop</Button><StackPanel.Triggers><!-- Begin the Storyboard --><EventTriggerRoutedEvent='Button.Click'SourceName='BeginButton'><BeginStoryboardName='MyBeginStoryboard'><Storyboard ><DoubleAnimationStoryboard.TargetName='myRectangle'Storyboard.TargetProperty='Width'Duration='0:0:5'From='100'To='500' /></Storyboard></BeginStoryboard></EventTrigger><!-- Pause the Storyboard --><EventTriggerRoutedEvent='Button.Click'SourceName='PauseButton'><PauseStoryboardBeginStoryboardName='MyBeginStoryboard' /></EventTrigger><!-- Resume the Storyboard --><EventTriggerRoutedEvent='Button.Click'SourceName='ResumeButton'><ResumeStoryboardBeginStoryboardName='MyBeginStoryboard' /></EventTrigger><!-- Seek one second into the storyboard’s active period. --><EventTriggerRoutedEvent='Button.Click'SourceName='SeekButton'><SeekStoryboardBeginStoryboardName='MyBeginStoryboard'Offset='0:0:1'Origin='BeginTime' /></EventTrigger><!-- Skip to Fill --><EventTriggerRoutedEvent='Button.Click'SourceName='SkipToFillButton'><SkipStoryboardToFillBeginStoryboardName='MyBeginStoryboard' /></EventTrigger><!-- Stop the Storyboard --><EventTriggerRoutedEvent='Button.Click'SourceName='StopButton'><StopStoryboardBeginStoryboardName='MyBeginStoryboard' /></EventTrigger><!-- Triple the speed of the Storyboard --><EventTriggerRoutedEvent='Button.Click'SourceName='SetSpeedRatioButton'><SetStoryboardSpeedRatioSpeedRatio='3'BeginStoryboardName='MyBeginStoryboard' /></EventTrigger></StackPanel.Triggers></StackPanel></StackPanel></Page>
Animations can be applied to the objects in WPF application when the data in the control changes using the DataTriggers. The following lines of code is used to apply animations using a DataTrigger
The scenario is such that the animations in the page will continue to play until it is garbage collected, even when the page is navigated away to another page holding the memory and system resources for animation. There will be severe drop in performance when more animations are running in a page. To overcome this issue, ‘Unloaded’ event of the page can be used to remove the animations from the page such that the animations don’t consume memory and system resources.
Animations can be applied without using the StoryBoard. BeginAnimation() method can be used to apply animations instead of StoryBoard. This method can be used when simple animations are applied to a property of a control.
The following code snippet animates the width of the TextBlock using the ’BeginAnimation’ method.
[C#]
DoubleAnimation Dblanimation = new DoubleAnimation();
Dblanimation.From = 25;
Dblanimation.To = 50;
Dblanimation.Duration = new Duration(TimeSpan.FromSeconds(3));
tb.BeginAnimation(TextBlock.WidthProperty, Dblanimation);