Handling Dynamic Storyboards and Animations in Silverlight 2

Animations are a key part of Silverlight 2 that allow your applications to stand out from all of the boring and dull applications floating around on the Web.  In previous articles I’ve written about storyboards and animations but as a quick review, Silverlight relies upon a Storyboard element to define several different types of animations such as DoubleAnimation (which animates object properties of type double) and ColorAnimation (which animates color properties).  In this article I’ll show you how Silverlight can be used to create animations programmatically and how you can interact with animations defined declaratively in a XAML file using C#.

For this example I’ll create a simple “lightbox” style container that can display pictures from Flickr.  As the picture is being shown the box will grow from a height/width of 0 to a larger size that’s determined by the size of the browser.  Three different animations will be performed by the image display container:

  • Animate the Width from 0 to the width of the browser / 1.5
  • Animate the Height from 0 to the height of the browser / 1.5
  • Animate the Opacity from 0 to 1

If you know the To and From values of the animations upfront then it’s easiest to define them in the XAML file.  In cases where there’s an unknown animation property value, you can create the storyboard and animation objects programmatically or update properties of an existing Storyboard defined in XAML.  In this case a Border control will be animated:

<Border x:Name="LightBoxControl" BorderBrush="Black" BorderThickness="3" 
        CornerRadius="10"  
        Height="20" Width="20" Opacity="0" Margin="5" 
        MouseLeftButtonDown="Image_MouseLeftButtonDown">
    <Border.Background>
        <LinearGradientBrush EndPoint="0.893,0.116" StartPoint="0.403,0.694">
            <GradientStop Color="#FFB9B9B9" Offset="1"/>
            <GradientStop Color="#FF4F4F4F" Offset="0"/>
            <GradientStop Color="#FF666666" Offset="0.496"/>
        </LinearGradientBrush>
    </Border.Background>
    <StackPanel x:Name="spImage">
        <Border HorizontalAlignment="Right" VerticalAlignment="Top" BorderBrush="Black" 
                BorderThickness="1" CornerRadius="8" Background="LightGray" Width="60" Height="25">
            <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="12" 
                       Text="Close" />
        </Border>
        <Rectangle x:Name="LightBoxImage" HorizontalAlignment="Center" 
                   Fill="Black" RadiusX="10" RadiusY="10" 
                   Stroke="LightGray" StrokeThickness="2" Margin="5">
        </Rectangle>
    </StackPanel>
</Border>

Here’s an example of programmatically creating a Storyboard and defining the 3 animations mentioned earlier to change the Width, Height and Opacity properties of the Border control:

double? width = (double?)System.Windows.Browser.HtmlPage.Document.Body.GetProperty("offsetWidth") / 1.5;
double? height = (double?)System.Windows.Browser.HtmlPage.Document.Body.GetProperty("offsetHeight") / 1.5;
//Programmatic way to create a storyboard and animations
Storyboard sb = new Storyboard();

//Create animation for Border control's Width
DoubleAnimation wa = new DoubleAnimation();
wa.Duration = new Duration(TimeSpan.FromSeconds(0.5))
wa.From = 0;
wa.To = width;
Storyboard.SetTarget(wa, LightBoxControl);
Storyboard.SetTargetProperty(wa, new PropertyPath(Border.WidthProperty));
sb.Children.Add(wa);

//Create animation for Border control's Height
DoubleAnimation ha = new DoubleAnimation();
ha.Duration = new Duration(TimeSpan.FromSeconds(0.5));
ha.From = 0;
ha.To = height;
Storyboard.SetTarget(ha, LightBoxControl);
Storyboard.SetTargetProperty(ha, new PropertyPath(Border.HeightProperty));
sb.Children.Add(ha);

//Create animation for Border control's Opacity
DoubleAnimation oa = new DoubleAnimation();
oa.Duration = new Duration(TimeSpan.FromSeconds(0.5));
oa.From = 0;
oa.To = 1;
Storyboard.SetTarget(oa, LightBoxControl);
Storyboard.SetTargetProperty(oa, new PropertyPath(Border.OpacityProperty));
sb.Children.Add(oa);

this.spImage.Width = width.Value - 30;
this.spImage.Height = height.Value - 30;
this.LightBoxImage.Width = width.Value - 75;
this.LightBoxImage.Height = height.Value - 75;
sb.Begin();

In this example the final height and width of the target Border control are determined by grabbing the offsetWidth and offsetHeight values using the HtmlPage class.  Once those values are determined a Storyboard object is created along with the three animations all of type DoubleAnimation.  Each animation takes 1/2 second to complete and animates the Border control’s Height, Width and Opacity properties respectively. 

Looking through the code for each animation object you’ll notice that it is associated with the target object and target object property using the Storyboard.SetTarget() and Storyboard.SetTargetProperty() static methods respectively.  This may seem strange at first glance but makes sense once you understand the concept of attached properties.  Each animation object is added to the parent Storyboard object using the Children.Add() method and the Storyboard is started by calling the Begin() method.

Interacting with Storyboards and Animations Defined in XAML

The programmatic approach to creating animations works great when there’s a lot of dynamic data being fed into animation objects.  However, in this example only the Height and Width properties are being changed on the Border control.  That’s a lot of code to write to change two properties.  Rather than defining everything programmatically you can instead define the storyboard and associated animations declaratively and fill in the dynamic pieces at runtime.  Here’s an example of defining the 3 animations shown earlier in XAML:

<Storyboard x:Name="sbShow">
    <DoubleAnimation x:Name="daWidth" Storyboard.TargetName="LightBoxControl"
                     Storyboard.TargetProperty="Width"
                     From="0" Duration="00:00:0.5" />
    <DoubleAnimation x:Name="daHeight" Storyboard.TargetName="LightBoxControl"
                     Storyboard.TargetProperty="Height"
                     From="0" Duration="00:00:0.5" />
    <DoubleAnimation Storyboard.TargetName="LightBoxControl"
                     Storyboard.TargetProperty="Opacity"
                     From="0" To="1" Duration="00:00:0.5" />
</Storyboard>

Before the storyboard starts to play the To property of the daWidth and daHeight objects can then be assigned values as shown next:

double? width = (double?)System.Windows.Browser.HtmlPage.Document.Body.GetProperty("offsetWidth") / 1.5;
double? height = (double?)System.Windows.Browser.HtmlPage.Document.Body.GetProperty("offsetHeight") / 1.5;


//Define how big the Border control should be animated to by setting the To property value

this
.daWidth.To = width; this.daHeight.To = height;
this.spImage.Width = width.Value - 30; this.spImage.Height = height.Value - 30; this.LightBoxImage.Width = width.Value - 75; this.LightBoxImage.Height = height.Value - 75;
this.sbShow.Begin();

And that’s all there is to it!  Silverlight makes it easy to work with dynamic animations whether programmatically or through modifying storyboards defined in XAML.  Download a working version of the Flickr lightbox application here if you’d like to see the animations in action (as well as some others).

comments powered by Disqus

2 Comments

  • Hi,

    I've downloaded the code, changed the _APIKey, and than ran it. I'm getting a AG_E_NETWORK_ERROR message when running it. Could you lend a hand as to why this is, please?

  • Hi Justin,

    That's not a fun error but is typically due to the inability to access the image, accessing an unsupported image type (such as .gif)...wouldn't be the problem here, or some other problem. My guess is that with your key the images aren't being returned properly but it's a difficult one to debug remotely. If it works with my key in there then something is not setup correctly when using your key. That's just my guess though.

Comments have been disabled for this content.