The WPF Storyboard – what it is and how to use it?
Join the DZone community and get the full member experience.
Join For FreeIf you have ever used a piece of software that animates images or manipulates video content, you are probably familiar with the concept of a storyboard. If not, then simply explaining, a storyboard is a place where animation information is stored. A storyboard determines at what point in time a frame will occupy a specific position and will have specific properties.
In WPF, a storyboard is absolutely the same concept. You can work with the storyboard in three ways – by using Expression Blend, which will save you some time since you won’t be writing code manually, by using XAML and by using the code-behind capabilities (C# or VB.NET).
Let’s start with the first, and probably the easiest way to use the WPF storyboard. In Microsoft Expression Blend, create a new WPF Application project. You can add and arrange some controls on the window. I added a button with some custom text for now. The window should look similar to this:
In the lower left corner, you can see there is an Objects and Timeline pane. That’s where the storyboard will be managed. However, by default there is no storyboard defined. To create one, on the same pane click the + (plus) button by the selection box that says (No Storyboard open).
A window, asking for a name for the new storyboard will be shown:
Name it whatever you want. I am going to leave it as the default value says – Storyboard1.
Once you get this done, you can see that the window changed its appearance a bit. First of all, on the Objects and Timeline pane there is now a section that represents the current storyboard – with time labels and a gradation scale. The red frame around the active window designer means that the timeline recording is on – an action you specify here will be recorded in the storyboard.
I am going to experiment here a bit. Drag the yellow line in the storyboard manager, so it goes to 2 seconds (label with a 2 on it). Once it is there, move a control (in my case a button) a little bit. Doesn’t matter where – you can drag it to another side of the window, if you want.
Once you do that, you can see that in the Objects and Timeline pane, in the Storyboard management section there is a white dot right by the name of the button (shown as a part of the window visual root):
This is an animation point, that stores the changed position for the control. To test the animation, hit the Play button in the Storyboard management section. You will see the button (or any other control) slowly move from one part to another. The transition is done by using the following XAML code:
<Window x:Class="WPF_Test.Window1" xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml Title="Window1" Height="300" Width="300"> <Window.Resources> <Storyboard x:Key="Storyboard1"> <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="myButton" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"> <SplineDoubleKeyFrame KeyTime="00:00:02" Value="68"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="myButton" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"> <SplineDoubleKeyFrame KeyTime="00:00:02" Value="-90"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </Window.Resources> <Window.Triggers> <EventTrigger RoutedEvent="FrameworkElement.Loaded"> <BeginStoryboard Storyboard="{StaticResource Storyboard1}"/> </EventTrigger> </Window.Triggers> <Grid> <Button Name="myButton" Margin="77,105,81,107" Content="MyButton" MouseEnter="myButton_MouseEnter" RenderTransformOrigin="0.5,0.5"> <Button.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform/> </TransformGroup> </Button.RenderTransform> </Button> </Grid></Window>
Although at this moment the code seems quite complicated and confusing, once we look under the hood you’ll find out that it’s pretty basic.
First of all, here is the Storyboard declaration:
<Storyboard x:Key="Storyboard1"> <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="myButton" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"> <SplineDoubleKeyFrame KeyTime="00:00:02" Value="68"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="myButton" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"> <SplineDoubleKeyFrame KeyTime="00:00:02" Value="-90"/> </DoubleAnimationUsingKeyFrames></Storyboard>
There are two DoubleAnimationUsingKeyFrames elements that define the values in time for the X and Y location properties for the button.
The actual trigger that causes the animation to run is here:
<Window.Triggers> <EventTrigger RoutedEvent="FrameworkElement.Loaded"> <BeginStoryboard Storyboard="{StaticResource Storyboard1}"/> </EventTrigger></Window.Triggers>
In this specific case, the animation will start once the window is loaded. The animated button is defined here:
<Button Name="myButton" Margin="77,105,81,107" Content="MyButton" MouseEnter="myButton_MouseEnter" RenderTransformOrigin="0.5,0.5"> <Button.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform/> </TransformGroup> </Button.RenderTransform></Button>
It is a regular button, but with a RenderTransform element, that defines the actions that are performed with the button to transform its properties. This is a required element when it comes to control animation.
And that’s it! As you see, nothing complicated and you can easily add additional keyframes (animation points) to the Storyboard to change control states and animate things on the window.
Now, you might be wondering how this can be done from the code-behind. Here is my implementation:
Storyboard board = new Storyboard();DoubleAnimationUsingKeyFrames animation1 = new DoubleAnimationUsingKeyFrames();DoubleAnimationUsingKeyFrames animation2 = new DoubleAnimationUsingKeyFrames();animation1.SetValue(Storyboard.TargetNameProperty, "myButton");animation1.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"));animation2.SetValue(Storyboard.TargetNameProperty, "myButton");animation2.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)")); animation1.BeginTime = new TimeSpan(0, 0, 0);animation2.BeginTime = new TimeSpan(0, 0, 0);SplineDoubleKeyFrame keyFrame1 = new SplineDoubleKeyFrame();SplineDoubleKeyFrame keyFrame2 = new SplineDoubleKeyFrame();keyFrame1.KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 2));keyFrame2.KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 2));keyFrame1.Value = 68;keyFrame2.Value = -90;animation1.KeyFrames.Add(keyFrame1);animation2.KeyFrames.Add(keyFrame2);board.Children.Add(animation1);board.Children.Add(animation2);board.Begin(this);
This is a bit more lengthy than the XAML representation, but essentially it does the same thing. Note, that when referencing TransformGroup.Children, the following XAML code should be present inside the UI element declaration:
<Button.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform/> </TransformGroup></Button.RenderTransform>
This specific example is referring to a button, but any other UIElement can be used as well.
<Window x:Class="WPF_Test.Window1" xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml Title="Window1" Height="300" Width="300"> <Window.Resources> <Storyboard x:Key="Storyboard1"> <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="myButton" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"> <SplineDoubleKeyFrame KeyTime="00:00:02" Value="68"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="myButton" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"> <SplineDoubleKeyFrame KeyTime="00:00:02" Value="-90"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </Window.Resources> <Window.Triggers> <EventTrigger RoutedEvent="FrameworkElement.Loaded"> <BeginStoryboard Storyboard="{StaticResource Storyboard1}"/> </EventTrigger> </Window.Triggers> <Grid> <Button Name="myButton" Margin="77,105,81,107" Content="MyButton" MouseEnter="myButton_MouseEnter" RenderTransformOrigin="0.5,0.5"> <Button.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform/> </TransformGroup> </Button.RenderTransform> </Button> </Grid></Window>
Although at this moment the code seems quite complicated and confusing, once we look under the hood you’ll find out that it’s pretty basic.
First of all, here is the Storyboard declaration:
<Storyboard x:Key="Storyboard1"> <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="myButton" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"> <SplineDoubleKeyFrame KeyTime="00:00:02" Value="68"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="myButton" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"> <SplineDoubleKeyFrame KeyTime="00:00:02" Value="-90"/> </DoubleAnimationUsingKeyFrames></Storyboard>
There are two DoubleAnimationUsingKeyFrames elements that define the values in time for the X and Y location properties for the button.
The actual trigger that causes the animation to run is here:
<Window.Triggers> <EventTrigger RoutedEvent="FrameworkElement.Loaded"> <BeginStoryboard Storyboard="{StaticResource Storyboard1}"/> </EventTrigger></Window.Triggers>
In this specific case, the animation will start once the window is loaded. The animated button is defined here:
<Button Name="myButton" Margin="77,105,81,107" Content="MyButton" MouseEnter="myButton_MouseEnter" RenderTransformOrigin="0.5,0.5"> <Button.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform/> </TransformGroup> </Button.RenderTransform></Button>
It is a regular button, but with a RenderTransform element, that defines the actions that are performed with the button to transform its properties. This is a required element when it comes to control animation.
And that’s it! As you see, nothing complicated and you can easily add additional keyframes (animation points) to the Storyboard to change control states and animate things on the window.
Now, you might be wondering how this can be done from the code-behind. Here is my implementation:
Storyboard board = new Storyboard();DoubleAnimationUsingKeyFrames animation1 = new DoubleAnimationUsingKeyFrames();DoubleAnimationUsingKeyFrames animation2 = new DoubleAnimationUsingKeyFrames();animation1.SetValue(Storyboard.TargetNameProperty, "myButton");animation1.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"));animation2.SetValue(Storyboard.TargetNameProperty, "myButton");animation2.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)")); animation1.BeginTime = new TimeSpan(0, 0, 0);animation2.BeginTime = new TimeSpan(0, 0, 0);SplineDoubleKeyFrame keyFrame1 = new SplineDoubleKeyFrame();SplineDoubleKeyFrame keyFrame2 = new SplineDoubleKeyFrame();keyFrame1.KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 2));keyFrame2.KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 2));keyFrame1.Value = 68;keyFrame2.Value = -90;animation1.KeyFrames.Add(keyFrame1);animation2.KeyFrames.Add(keyFrame2);board.Children.Add(animation1);board.Children.Add(animation2);board.Begin(this);
This is a bit more lengthy than the XAML representation, but essentially it does the same thing. Note, that when referencing TransformGroup.Children, the following XAML code should be present inside the UI element declaration:
<Button.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform/> </TransformGroup></Button.RenderTransform>
This specific example is referring to a button, but any other UIElement can be used as well.
The Storyboard can be used for all kinds of animations, not necessarily just control movement. Scaling, rotation and skewing are also possible (you can figure this out by looking at the subset of elements for RenderTransform).
Opinions expressed by DZone contributors are their own.
Comments