Introduction
In this brief article, we are going to take a look at Silverlight animations, and will try to find a simple way to reuse the same animations for different elements. Creating animations in XAML code is easy, but it’s not that easy to reuse the same animation for different target objects.
In the worst case, you would have to copy the same XAML and change it for each object you want to apply the animation to. You’ll get into a deeper trap if you need to create an animation which should be different depending on the object this animation is applied to.
Based on the above, we’ve decided to come up with a different way to handle animations in Silverlight. We need a simple yet powerful way to reuse animations for different sets of objects. We also need a way to dynamically add animation effects to Silverlight objects.
In this article, we’ll cover some implementation details of how this problem can be solved.
Creating Simple Storyboards
As you probably know, Silverlight animations are handled by the Storyboard
class. Creating a storyboard in XAML is easy, e.g., if you need to create a fade in animation, you would normally use the following piece of XAML code:
<Storyboard x:Name="fadeIn">
<DoubleAnimation
Storyboard.TargetName="fadeInEffect"
Storyboard.TargetProperty="(UIElement.Opacity)"
From="0"
To="1"
SpeedRatio="3">
</DoubleAnimation>
</Storyboard>
Here, we use the DoubleAnimation
element to animate the UIElement.Opactiry
property from a 0 value to 1 with SpeedRatio
set to 3. The Storyboard.TargetName
attribute is used to specify the target object for the animation.
There are several other types of animation elements which can be created inside storyboards, e.g., we can also use the DoubleAnimationUsingKeyFrames
element and create several SplineDoubleKeyFrame
elements inside.
Now, if we want to apply this animation to a different object, or if we want to change some of the animation parameters, then we would need to copy this XAML and change it to suite our needs – and this is not what we want to achieve in the end.
Let’s consider another example of animation created using XAML code: a grow-effect XAML may look like this:
<Storyboard x:Name="grow">
<DoubleAnimation
Storyboard.TargetName="growEffect"
Storyboard.TargetProperty="(UIElement.RenderTransform).
(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"
From="1"
To="1.5"
SpeedRatio="3">
</DoubleAnimation>
<DoubleAnimation
Storyboard.TargetName="growEffect"
Storyboard.TargetProperty="(UIElement.RenderTransform).
(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"
From="1"
To="1.5"
SpeedRatio="3">
</DoubleAnimation>
</Storyboard>
Note that if we want to animate an element’s scale, then our target element should have appropriate transforms inside its RenderTransform
property:
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1">
</ScaleTransform>
</TransformGroup>
</Border.RenderTransform>
Now, we can reference the ScaleTransform
property via this string: (UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)
.
Instead of creating animations purely in XAML, we can easily use procedural code. This might sound like a bad idea, but it’s not. Creating storyboards in procedural code is the most powerful way to create and reuse Silverlight animations.
Let’s consider some procedural code showing how we can create animations dynamically. The following code shows how a Storyboard
object can be created in procedural code:
Storyboard storyboard = new Storyboard();
DoubleAnimation animation = new DoubleAnimation();
animation.From = 0;
animation.To = 1;
animation.SpeedRatio = 3;
Storyboard.SetTarget(animation, target);
Storyboard.SetTargetProperty(animation, "(UIElement.Opacity)");
storyboard.Children.Add(animation);
The code presented above is equivalent to the XAML code we created. However, this procedural code gives us some more flexibility, and allows code reuse. In other words, we can now create parameters for the From
, To
, and SpeedRatio
properties and use any values we need.
Note that we call the SetTarget
and SetTargetProperty
static methods on the Storyboard
class to set the target element and the target property for the double animation element. The last line of code adds a double animation element to the Storyboard
object.
In order to run the animation, we’ll need to add the storyboard to our target framework element. Here is how we can do this:
target.Resources.Add(storyboard);
To start the animation, we just call Begin
on the Storyboard
instance.
Dynamic Effect Class
Let’s create a dynamic effect class which would be useful for creating storyboards dynamically, adding them to target framework elements and removing when animation is completed.
Here is the code of the DynamicEffect
class:
public abstract class DynamicEffect
{
FrameworkElement target;
Storyboard storyboard;
public DynamicEffect()
{
}
protected abstract Storyboard CreateStoryboard(FrameworkElement target);
protected virtual void Add(FrameworkElement target)
{
this.target = target;
storyboard = CreateStoryboard(target);
storyboard.Completed += new EventHandler(OnCompleted);
target.Resources.Add(storyboard);
}
protected virtual void Remove()
{
if (target == null)
return;
if (storyboard == null)
return;
target.Resources.Remove(storyboard);
target = null;
storyboard = null;
}
protected virtual void OnCompleted(object sender, EventArgs e)
{
Remove();
if (Completed != null)
Completed(sender, e);
}
public virtual void Start(FrameworkElement target)
{
Add(target);
storyboard.Begin();
}
public virtual void Stop()
{
if (target == null || storyboard == null)
return;
storyboard.Stop();
Remove();
}
public event EventHandler Completed;
}
The DynamicEffect
class defines the Start
and Stop
public methods to start and stop our effect, and there is also a useful Completed
event defined.
The Start
method will add an effect’s storyboard to the target framework element, and then call Begin
to start animation. In turn, the Stop
method will call Stop
on the effect’s storyboard and then remove it from the target element.
All we need is just to create a descendant effect class and override its CreateStoryboard
virtual method.
Fade In and Fade Out Effects
Here is how we would create the FadeEffect
class:
public class FadeEffect : DynamicEffect
{
double from;
double to;
double speed;
public FadeEffect(double from, double to, double speed)
{
this.from = from;
this.to = to;
this.speed = speed;
}
protected override Storyboard CreateStoryboard(FrameworkElement target)
{
Storyboard result = new Storyboard();
DoubleAnimation animation = new DoubleAnimation();
animation.From = from;
animation.To = to;
animation.SpeedRatio = speed;
Storyboard.SetTarget(animation, target);
Storyboard.SetTargetProperty(animation, "(UIElement.Opacity)");
result.Children.Add(animation);
return result;
}
}
Creating the fade-in and fade-out effects would be pretty straightforward.
The fade-in effect:
public class FadeInEffect : FadeEffect
{
public FadeInEffect()
: base(0.0, 1.0, 3.0)
{
}
}
The fade-out effect:
public class FadeOutEffect : FadeEffect
{
public FadeOutEffect()
: base(1.0, 0.0, 3.0)
{
}
}
Here is an example code for creating a fade in effect and applying it:
FadeInEffect effect = new FadeInEffect();
effect.Start(target);
Now, you can apply fade in and fade out effects to any number of framework elements, and no need to copy your XAML code :-)
Conclusion
The sample code for this article contains several animation effects created using XAML code.
In this brief article, we’ve covered some simple dynamic effects; however, it is possible to create more complex effects with animation of the transforms and clip properties. It is also possible to chain simple effects to achieve some complex functionality, e.g., creating carousel effects.
I plan to post more articles regarding Silverlight animation effects, please let me know what you would want to see in future articles.
Points of Interest
- Adding animations for transforms and clip properties.
- Creating animation chains.
- Using split key frames to create non linear animation effects.
History
- March 21, 2008: Initial release.