WPF FAQ - Animation

What are the requirements for a property to be animated?

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.

Permalink
I have a sequence of animations I want to execute one after another, how do I do that?

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:

[XAML]
<Canvas>  
        <Rectangle x:Name='rect1' Canvas.Top='0' Width='30' Height='30' Fill='Aqua'></Rectangle>  
        <Rectangle x:Name='rect2' Canvas.Top='50' Width='30' Height='30'  Fill='Azure'></Rectangle>  
</Canvas>

The code-behind for this would be:

[C#]
private void Window_Loaded(object sender, RoutedEventArgs e)   
        {   
  
            DoubleAnimation da = new DoubleAnimation(100, new Duration(TimeSpan.FromSeconds(3)));   
            List<icollection> list = new List<icollection>();   
            list.Add(new object[] { rect1, Rectangle.WidthProperty, da});   
            list.Add(new object[] { rect2, Rectangle.WidthProperty, da });   
  
            this.PerformAnimations(0, list);   
        }   
  
        private void PerformAnimations(int index, List<icollection> lstDefinitions)   
        {   
            object[] definition = lstDefinitions[index] as object[];   
            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]);   
        }
</icollection></icollection></icollection>

The idea is to maintain a list of ‘animation definitiions’ and execute animations from this list recursively.

Permalink
At the end of animation, how do I cause the value of a property to revert back to it’s original value?

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).

Permalink
How do I control a Storyboard animation once it starts?

In code behind:

The following article from MSDN describes how to Pause, Resume, Stop, etc. on a running animation:

http://msdn.microsoft.com/en-us/library/ms741997.aspx

In XAML:

The following example uses controllable storyboard actions to interactively control a storyboard.

[XAML]

<Page xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
  xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
  WindowTitle='Controlling a Storyboard' >
  <StackPanel Margin='20' >

    <!-- This rectangle is animated. -->
    <Rectangle Name='myRectangle'
      Width='100' Height='20' Margin='12,0,0,5' Fill='#AA3333FF' HorizontalAlignment='Left' />

    <!-- This StackPanel contains all the Buttons. -->
    <StackPanel Orientation='Horizontal' Margin='0,30,0,0'>

      <Button Name='BeginButton'>Begin</Button>
      <Button Name='PauseButton'>Pause</Button>
      <Button Name='ResumeButton'>Resume</Button>
      <Button Name='SeekButton'>Seek</Button>
      <Button Name='SkipToFillButton'>Skip To Fill</Button>
      <Button Name='SetSpeedRatioButton'>Triple Speed</Button>
      <Button Name='StopButton'>Stop</Button>

      <StackPanel.Triggers>

        <!-- Begin the Storyboard -->
        <EventTrigger RoutedEvent='Button.Click' SourceName='BeginButton'>
          <BeginStoryboard Name='MyBeginStoryboard'>
            <Storyboard >
              <DoubleAnimation 
                Storyboard.TargetName='myRectangle' 
                Storyboard.TargetProperty='Width' 
                Duration='0:0:5' From='100' To='500' />
            </Storyboard>
          </BeginStoryboard>
        </EventTrigger>

        <!-- Pause the Storyboard -->
        <EventTrigger RoutedEvent='Button.Click' SourceName='PauseButton'>
          <PauseStoryboard BeginStoryboardName='MyBeginStoryboard' />
        </EventTrigger>

        <!-- Resume the Storyboard -->
        <EventTrigger RoutedEvent='Button.Click' SourceName='ResumeButton'>
          <ResumeStoryboard BeginStoryboardName='MyBeginStoryboard' />
        </EventTrigger>

         <!-- Seek one second into the storyboard’s active period. -->
         <EventTrigger RoutedEvent='Button.Click' SourceName='SeekButton'>
           <SeekStoryboard 
            BeginStoryboardName='MyBeginStoryboard' 
            Offset='0:0:1' Origin='BeginTime' />
        </EventTrigger>   

        <!-- Skip to Fill -->
        <EventTrigger RoutedEvent='Button.Click' SourceName='SkipToFillButton'>
          <SkipStoryboardToFill BeginStoryboardName='MyBeginStoryboard' />
        </EventTrigger>

        <!-- Stop the Storyboard -->
        <EventTrigger RoutedEvent='Button.Click' SourceName='StopButton'>
          <StopStoryboard BeginStoryboardName='MyBeginStoryboard' />
        </EventTrigger>

        <!-- Triple the speed of the Storyboard -->
        <EventTrigger RoutedEvent='Button.Click' SourceName='SetSpeedRatioButton'>
          <SetStoryboardSpeedRatio SpeedRatio='3' BeginStoryboardName='MyBeginStoryboard' />
        </EventTrigger>
      </StackPanel.Triggers>
    </StackPanel>
  </StackPanel>
</Page>
Permalink
How do I trigger an animation when the data in the control is changed?

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

[XAML]

<TextBlock Text='WPF Application' Name='tb1' Foreground='Blue' FontSize='28' TextAlignment='Center' Height='50' VerticalAlignment='Top'>
<TextBlock.Style>
      	<Style>
            <Style.Triggers>
<DataTrigger Binding='{Binding ElementName=cb1,Path=IsChecked}' Value='true'>
                  <DataTrigger.EnterActions>
                  <BeginStoryboard>
                  <Storyboard>
<DoubleAnimation Storyboard.TargetProperty='Opacity' From='0' To='1' Duration='0:0:1' AutoReverse='True' RepeatBehavior='Forever' />
                  </Storyboard>
                  </BeginStoryboard>
                  </DataTrigger.EnterActions>
                  <DataTrigger.ExitActions>
                  <BeginStoryboard> 
                  <Storyboard FillBehavior='Stop'>
<DoubleAnimation Storyboard.TargetProperty='Opacity' To='1' Duration='0:0:1' />
                  </Storyboard>
                  </BeginStoryboard>  
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<CheckBox Name='cb1' Content='Show Animation' IsChecked='True' Height='30' HorizontalAlignment='Center' />
Permalink
Performance gets reduced even after navigating away from a page that contains animation. How do we handle this issue ?

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.

Permalink
How do I apply an animation without using a storyboard ?

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);
Permalink