Introduction
In this brief article we are going to take a look into Silverlight animations and 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 worst case you would have to copy the same xaml and change it for each object you what to apply the animation. You’ll get into deeper trap if you need to create an animation which should be different depending on an 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 needed a simple yet powerful way to reuse animations for different set of objects. We also needed a way to dynamically add animation effects to silverlight objects.
In this article we’ll cover some implementation details on how this problem can be solved.
AgEffects Library
At Cellbi Software we are working on the AgEffects library to simplify animation tasks and provide easy to use, but powerful way to reuse silverlight animations.
AgEffects Sample - sample page showing several dynamic animation effects.
The library contains several most commonly used animation effects: carousel effect, fade in effect, fade out effect, scale effect, move effect and adds support for combining dynamic effects into parallel or sequence chains.
Using this library you can dynamically add/remove effects to any framework elements reuse your animation and keep your xaml code as clean as possible. You don’t need to worry about animating your UI elements. Instead just use already available effects in the AgEffects library.
We plan to enhance the library and we are greatly interested in your feedback and suggestions for new effects.
Cellbi Forum - please use this forum to post your feedback and suggestions.
Creating Simple Storyboards
As you probably know silverlight animations are handled by the Storyboard class. Creating storyboard in xaml is easy, e.g. if you need to create 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 DoubleAnimation element to animate UIElement.Opactiry property from 0 value to 1 with SpeedRatio set to 3. Storyboard.TargetName attribute is used to specify 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 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: 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 element’s scale then our target element should have appropriate transforms inside it’s RenderTransform property:
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1">
</ScaleTransform>
</TransformGroup>
</Border.RenderTransform>
Now we can reference 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 and AgEffects library is completely based on this approach.
In other words the library will manage creating and adding appropriate transforms for you, all you need to do is just pick the effect needed, create it and then run it.
Let’s consider some procedural code showing how we can create animations dynamically. The following code shows how 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 SetTarget and SetTargetProperty static methods on the Storyboard class to set target element and target property for the double animation element. Last line of code adds 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 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:
Collapse
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;
}
DynamicEffect class defines Start and Stop public methods to start and stop our effect, and there is also useful Completed event defined.
Start method will add effect’s storyboard to the target framework element and then call Begin to start animation. In turn, Stop method will call Stop on the effect’s storyboard and then remove it from the target element.
All we need is just create descendant effect class and override it’s CreateStoryboard virtual method.
Fade In and Fade Out Effects
Here is how we would create FadeEffect class:
Collapse
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 FadeIn and FadeOut effects would pretty straightforward.
Fade in effect:
public class FadeInEffect : FadeEffect
{
public FadeInEffect()
: base(0.0, 1.0, 3.0)
{
}
}
Fade out effect:
public class FadeOutEffect : FadeEffect
{
public FadeOutEffect()
: base(1.0, 0.0, 3.0)
{
}
}
Here is an example code for creating 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 you xaml code :-)