Dan Wahlin (Microsoft Most Valuable Professional for ASP.NET and XML Web Services) is a .NET development instructor and architecture consultant at Interface Technical Training. Dan founded the XML for ASP.NET Developers site, which focuses on using ASP.NET, XML, Silverlight, AJAX, and Web Services on .NET. Dan has co-authored/authored several different books on .NET, including ASP.NET 2.0 MVP Hacks, Professional ASP.NET AJAX, XML for ASP.NET Developers and is currently working on a new book on Silverlight 2. Dan blogs at http://weblogs.asp.net/dwahlin.
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 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 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 three 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 three 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).