Channels ▼
RSS

.NET

Silverlight Animation


An Interactive Animation Example

In the previous example, you used animation to alter an element when it first appears. However, in most applications, animations will be triggered by another event, such as a mouse movement or a mouse click.

The following example demonstrates a slightly more realistic use of animation, which is shown in Figure 1. It begins with a content region that's filled with irregularly shaped rectangles. When you click a rectangle, it begins to fall toward the bottom of the Canvas, and simultaneously begins to change color. When you click another rectangle, the first animation stops and that rectangle begins to fall.

[Click image to view at full size]
Figure 1: Falling rectangles

This animation example is simple, but it demonstrates several of the subtle concepts in Silverlight animation.

The markup for this example defines a single storyboard. This storyboard isn't placed in a Triggers collection, because initially it isn't wired up to any specific rectangle. Instead, it's placed in the Canvas.Resources collection so it can be retrieved by your code when needed.

<Canvas ... >
  <Canvas.Resources>
    <Storyboard x:Name="fallingSquareStoryboard">
      <DoubleAnimation
          Storyboard.TargetProperty="(Canvas.Top)"
         To="400" Duration="0:0:2" />
    <ColorAnimation Storyboard.TargetProperty="Rectangle.Fill.Color"
        To="Blue" Duration="0:0:2" />
      </Storyboard>
   </Canvas.Resources>
</Canvas>

This storyboard wraps two animations: a DoubleAnimation that moves the rectangle, and a ColorAnimation that changes its color. The ColorAnimation uses linear interpolation, which means it will progressively blend the color from its initial value (in this example, red) to its final value (blue). You'll also notice that the Canvas doesn't contain any other elements. That's because this example uses a more flexible approach -- it generates the rectangles dynamically. When the Canvas is loaded, it creates 12 rectangles of random size, at random locations. It wires the MouseLeftButtonDown event of each one to the same event handler.

public void Page_Loaded(object o, EventArgs e)
{
   // Required to initialize variables
   InitializeComponent();
   // Generate some rectangles.
   Random rand = new Random();
   for (int i = 0; i < 12; i++)
   {
      Rectangle rect = new Rectangle();
      rect.Fill = new SolidColorBrush(Colors.Red);
      // Size and place it randomly.
     rect.Width = rand.Next(10, 40);
     rect.Height = rand.Next(10, 40);
     rect.SetValue<double>(Canvas.TopProperty,
        rand.Next((int)this.Height / 2));
     rect.SetValue<double>(Canvas.LeftProperty,
        rand.Next((int)this.Width));
     // Handle clicks.
    rect.MouseLeftButtonDown += rect_Click;
    // Give it a unique name, which is required for animation.
    rect.SetValue<string>(Rectangle.NameProperty, "rect" + i.ToString());
    // Add it to the Canvas.
    this.Children.Add(rect);
    }
}

When a rectangle is clicked, there are two steps that need to be performed. The animation for the current rectangle needs to be halted (by calling the Storyboard.Stop() method), and the existing storyboard needs to be attached to the new rectangle.

However, there's a trick here. Animations don't actually change the underlying value of a property, they simply override it temporarily. When the end of an animation is reached, the property is held indefinitely at its final value (unless you've set the FillBehavior property of the animation class to FillBehavior.Stop). But in this example, the animation needs to be repeatedly stopped. If you don't take any extra steps, each time you stop the animation of a falling rectangle, its position will be reset to its original value, meaning it will "jump" back up to the top of the Canvas.

The solution is to retrieve the current value of the Canvas.Top property for the rectangle, then stop the animation, and then set the animated value. This last step moves the rectangle to its most recent animated position. The result is that every time you click a new rectangle, the rectangle that was falling previously halts in its tracks, but remains in the same position.

Here's the code that implements this design:

// Keep track of the rectangle that's being animated.
private Rectangle currentlyFallingRectangle;
private void rect_Click(object o, EventArgs e)
{
    // Retrieve the Storyboard.
    Storyboard sb = (Storyboard)this.FindName("fallingSquareStoryboard");
    if (currentlyFallingRectangle != null)
   {
       // Stop the current animation and move the rectangle
       // to its current position.
      double top =
          (double)currentlyFallingRectangle.GetValue(Canvas.TopProperty);
      sb.Stop();
          currentlyFallingRectangle.SetValue<double>(Canvas.TopProperty, top);
   }
  // Get the rectangle that was clicked.
  currentlyFallingRectangle = (Rectangle)o;
  // Start the animation for the new rectangle.
  sb.SetValue<string>(Storyboard.TargetNameProperty,
  currentlyFallingRectangle.Name);
  sb.Begin();
}

Although the Canvas.Top property is set manually after the animation is stopped, the color is not. As a result, the rectangle reverts to its initial blue color as soon as another rectangle starts falling. There's another interesting quirk in this example. The animation always uses the same duration (2 seconds). However, the square you click may be close to the bottom or far from the bottom. As a result, squares closer to the bottom will fall more slowly, and squares farther from the bottom will fall faster.

In this example, there's only one storyboard at work at a time. It's reasonable to ask if you could create a similar example where every rectangle you click continues falling. This is possible, but a different design is required.

In the current build of Silverlight 1.1, Storyboard objects can't be fully configured programmatically. Thus, you need to have the storyboard and animations you need defined in your XAML. This is obviously a challenge if you're creating elements dynamically, and don't know how many storyboards you'll use. The solution is to create a custom control that has its own animation behavior. To implement this design in the previous example, you'd create a custom rectangle that has its own XAML template. This XAML would specify the animation that should be used for that rectangle. Thus, every time you create an instance of this custom control, it comes pre-wired with the animation support. Unfortunately, this more modular design takes a fair bit more code, and it's out of the scope of this chapter. However, if you're interested in learning more, check out the Silverlight Balloons example at http://tinyurl.com/398qf4. It illustrates this principle neatly with an endless sequence of rising balloons (each of which is an instance of a custom Balloon control).


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video