Den is a DZone Zone Leader and has posted 460 posts at DZone. You can read more from them at their website. View Full User Profile

Loading animation in WPF

04.18.2010
| 33977 views |
  • submit to reddit

WPF offers extensive functionality when it comes to graphics in client applications. Today I am going to show how to create a loading animation unit, as seen in many applications or/and websites. I am going to create it without any code-behind – only pure XAML will be used.

First of all start Visual Studio and create a new WPF application. Doesn’t matter whether it is going to be a VB.NET or C# project – I won’t be coding any functionality for this – XAML code is the same across WPF projects create in different programming languages. Take a look at the structure of the existing XAML code. It should be similar to this:

<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">
<Grid>

</Grid>
</Window>


Pretty simple. Now, you need to create a circle that will serve as the loading indicator. To do this, there is the Ellipse primitive available. You can draw it directly in the designer and then adjust the properties, or you could insert this XAML snippet inside the existing grid:

<Ellipse x:Name="ellipse" Margin="55,49,54,38" StrokeThickness="20" RenderTransformOrigin="0.5,0.5">

</Ellipse>


As you see, first of all I am setting the name. It is completely up to you what this is going to be. The margins are set according to the size of the window (or the container where the ellipse is located). In this specific case, I set the ellipse to almost cover the entire window. The margins are set for a perfect circle. The StrokeThickness property can be adjusted as the developer wants, however, I would recommend keeping it several units thicker than 1, because it will be the key element that will facilitate the progress animation. Since the gradient will be transformed, there is the RenderTransformOrigin property that sets the transformation point for the current element. The values range between 0 and 1, therefore a value of 0.5 will set the transformation point to the center of the object.

Now, let’s go to the transformation code. Add this snippet inside the Ellipse element:

<Ellipse.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
</TransformGroup>
</Ellipse.RenderTransform>

These are the required transformations that will be performed on the ellipse, although it will be rotated only. Now, let’s set the gradient for the stroke. Here is the XAML snippet that should be placed inside the root Ellipse element:

<Ellipse.Stroke>
<LinearGradientBrush EndPoint="0.445,0.997" StartPoint="0.555,0.003">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="#FF0052FF" Offset="1"/>
</LinearGradientBrush>
</Ellipse.Stroke>

The gradient start and end points can be adjusted as the developer wants. In this specific case, I set them so it is equally distributing the color across the circle, one side being light blue and the other one white (that can be seen from the color declarations).

To actually perform an animation, a storyboard is needed, so I am going to add a new container element inside the root Window element:

<Window.Resources>

</Window.Resources>

This will be the place holder for the additional resources I am going to introduce. In fact, in this tutorial I will only be adding a storyboard inside:

<Storyboard x:Key="Storyboard1" RepeatBehavior="Forever">

</Storyboard>

Once again, the name is completely random. You can change it if you want to, just make sure you introduce proper references later on in the code. The RepeatBehavior property is set to Forever, this meaning that the animation will be continuous.  You can remove this property from the declaration, but in this case the animation will only happen once. Now, the animation itself is placed inside the storyboard:

<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)">
<SplineDoubleKeyFrame KeyTime="00:00:02" Value="360"/>
</DoubleAnimationUsingKeyFrames>

The BeginTime property shows when the animation will begin. If you want to introduce a delay to the animation, you can alter this setting accordingly. Storyboard.TargetName is the target of the animation – the ellipse in this case. Storyboard.TargetProperty is the property that will be modified. The DoubleAnimationUsingKeyFrames itself shows that a property (with values of type Double) will be animated.

The SplineDoubleKeyFrame shows the end time for the animation and the end value of the property that will be modified.

Almost there, but the animation needs to be triggered. For this purpose, I am adding the Window.Triggers element to the root Window element:

<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource Storyboard1}"/>
</EventTrigger>
</Window.Triggers>

This will cause the animation to load when the window is loaded and will start it from the storyboard created above. Now, build and run the application. The end result should look similar to this:
 

 

The complete XAML code for the window:

<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" RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)">
<SplineDoubleKeyFrame KeyTime="00:00:02" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource Storyboard1}"/>
</EventTrigger>
</Window.Triggers>
<Grid>
<Ellipse x:Name="ellipse" Margin="55,49,54,38" StrokeThickness="20" RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
</TransformGroup>
</Ellipse.RenderTransform>
<Ellipse.Stroke>
<LinearGradientBrush EndPoint="0.445,0.997" StartPoint="0.555,0.003">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="#FF0052FF" Offset="1"/>
</LinearGradientBrush>
</Ellipse.Stroke>
</Ellipse>
</Grid>
</Window>