Click here to Skip to main content
Click here to Skip to main content

Creating Metro (Windows 8) Loading Animation Using XAML

, 19 Feb 2013
Rate this:
Please Sign up or sign in to vote.
Mimic a loading animation as seen in Windows 8 using pure XAML.

Introduction 

This article provides a complete XAML code with explanation required to create a Metro-style loading animation as seen in Microsoft Windows 8. A developer will be able to customize this code (e.g. change a particle color, a rotating speed, etc.) to suit his needs.

During my development, I use Kaxaml as a tool to create the animation.

Background 

There are plenty of articles out there providing tutorials about how to create an AJAX-style loading animation (busy indicator) in Silverlight/WPF using XAML. However, as my job required me to mimic a Metro-style loading animation, I decided to create one and shared it here Smile

Using the code 

The provided loading animation consists of five (five) rotating particles - each under a separate <border> element. The idea is not to rotate the particle itself. Instead, we rotate the <border> (which contains a particle) around its center. We then set the <border> background to transparent to make it look as if the particle itself is rotating around a center.

From the code below, you can change the following properties: 

  • ParticleColor: the color of the particles.
  • ParticleBackgroundColor: the color of the <border> background.
  • ParticleOpacity: the opacity of the particles.
  • StartingPointX: the location of the particles in the X-axis.
  • StartingPointY: the location of the particles in the Y-axis. A negative value indicates the go-up position. This becomes the radius of a circle the particles rotate around.
  • EllipseStyle -> Width: the width of the particles.
  • EllipseStyle -> Height: the height of the particles.
  • StoryBoard -> DoubleAnimation -> BeginTime: the time to begin rotating of each particle.
  • StoryBoard -> DoubleAnimation -> Duration: the rotating duration of each particle.
  • Border -> Ellipse -> Ellipse.RenderTransform -> TransformGroup -> RotateTransform -> Angle: the starting position (degree) of each particle.

By customizing these properties, you should be able to get the desired result right away.

WPF Version

<Grid
    xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:system = "clr-namespace:System;assembly=mscorlib"
    xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid.Resources>
        <!-- Particle Styling -->
        <SolidColorBrush x:Key = "ParticleColor" Color = "#006699"/>
        <SolidColorBrush x:Key = "ParticleBackgroundColor" Color = "Transparent"/>
        <system:Double x:Key = "ParticleOpacity">1</system:Double>
        <system:Double x:Key = "ParticleRadius">5</system:Double>
        
        <system:Double x:Key = "StartingPointX">0</system:Double>
        <system:Double x:Key = "StartingPointY">-20</system:Double>
        
        <system:Double x:Key = "RotationPointX">0.5</system:Double>
        <system:Double x:Key = "RotationPointY">0.5</system:Double>       
        
        <!-- StoryBoard -->
        <system:TimeSpan x:Key = "StoryBoardBeginTimeP0">00:00:00.000</system:TimeSpan>
        <system:TimeSpan x:Key = "StoryBoardBeginTimeP1">00:00:00.100</system:TimeSpan>
        <system:TimeSpan x:Key = "StoryBoardBeginTimeP2">00:00:00.200</system:TimeSpan>
        <system:TimeSpan x:Key = "StoryBoardBeginTimeP3">00:00:00.300</system:TimeSpan>
        <system:TimeSpan x:Key = "StoryBoardBeginTimeP4">00:00:00.400</system:TimeSpan>
        <Duration x:Key = "StoryBoardDuration">00:00:01.800</Duration>
        
        <!-- Particle Origin Angles -->
        <system:Double x:Key = "ParticleOriginAngleP0">0</system:Double>
        <system:Double x:Key = "ParticleOriginAngleP1">-10</system:Double>
        <system:Double x:Key = "ParticleOriginAngleP2">-20</system:Double>
        <system:Double x:Key = "ParticleOriginAngleP3">-30</system:Double>
        <system:Double x:Key = "ParticleOriginAngleP4">-40</system:Double>
        
        <!-- Particle Position & Timing 1 -->
        <system:Double x:Key = "ParticleBeginAngle1">0</system:Double>
        <system:Double x:Key = "ParticleEndAngle1">90</system:Double>
        <system:TimeSpan x:Key = "ParticleBeginTime1">00:00:00.000</system:TimeSpan>
        <Duration x:Key = "ParticleDuration1">00:00:00.750</Duration>
        
        <!-- Particle Position & Timing 2 -->
        <system:Double x:Key = "ParticleBeginAngle2">90</system:Double>
        <system:Double x:Key = "ParticleEndAngle2">270</system:Double>        
        <system:TimeSpan x:Key = "ParticleBeginTime2">00:00:00.751</system:TimeSpan>
        <Duration x:Key = "ParticleDuration2">00:00:00.300</Duration>
        
        <!-- Particle Position & Timing 3 -->
        <system:Double x:Key = "ParticleBeginAngle3">270</system:Double>
        <system:Double x:Key = "ParticleEndAngle3">360</system:Double>        
        <system:TimeSpan x:Key = "ParticleBeginTime3">00:00:01.052</system:TimeSpan>
        <Duration x:Key = "ParticleDuration3">00:00:00.750</Duration>
        
        <Style x:Key = "EllipseStyle" TargetType = "Ellipse">
            <Setter Property = "Width" Value = "{StaticResource ParticleRadius}"/>
            <Setter Property = "Height" Value = "{StaticResource ParticleRadius}"/>
            <Setter Property = "Fill" Value = "{StaticResource ParticleColor}"/>
            <Setter Property = "RenderTransformOrigin" Value = "0.5, 0.5"/>
            <Setter Property = "Opacity" Value = "{StaticResource ParticleOpacity}"/>
        </Style>
    </Grid.Resources>
    <Canvas Width = "50" Height = "50">
        <Canvas.Triggers>
            <EventTrigger RoutedEvent = "Canvas.Loaded">
                <EventTrigger.Actions>
                    <BeginStoryboard>
                        <Storyboard
                            x:Key = "MetroLoadingAnimation"
                            BeginTime = "{StaticResource StoryBoardBeginTimeP0}"
                            Duration = "{StaticResource StoryBoardDuration}"
                            RepeatBehavior = "Forever">
                            <DoubleAnimation
                                Storyboard.TargetName = "p0"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle1}"
                                To = "{StaticResource ParticleEndAngle1}"
                                BeginTime = "{StaticResource ParticleBeginTime1}"
                                Duration = "{StaticResource ParticleDuration1}"/>
                            <DoubleAnimation
                                Storyboard.TargetName = "p0"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle2}"
                                To = "{StaticResource ParticleEndAngle2}"
                                BeginTime = "{StaticResource ParticleBeginTime2}"
                                Duration = "{StaticResource ParticleDuration2}"/>
                            <DoubleAnimation
                                Storyboard.TargetName = "p0"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle3}"
                                To = "{StaticResource ParticleEndAngle3}"
                                BeginTime = "{StaticResource ParticleBeginTime3}"
                                Duration = "{StaticResource ParticleDuration3}"/>
                        </Storyboard>
                    </BeginStoryboard>
                    <BeginStoryboard>
                        <Storyboard
                            x:Key = "MetroLoadingAnimation"
                            BeginTime = "{StaticResource StoryBoardBeginTimeP1}"
                            Duration = "{StaticResource StoryBoardDuration}"
                            RepeatBehavior = "Forever">                             

                            <DoubleAnimation
                                Storyboard.TargetName = "p1"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle1}"
                                To = "{StaticResource ParticleEndAngle1}"
                                BeginTime = "{StaticResource ParticleBeginTime1}"
                                Duration = "{StaticResource ParticleDuration1}"/>
                            <DoubleAnimation
                                Storyboard.TargetName = "p1"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle2}"
                                To = "{StaticResource ParticleEndAngle2}"
                                BeginTime = "{StaticResource ParticleBeginTime2}"
                                Duration = "{StaticResource ParticleDuration2}"/>
                            <DoubleAnimation
                                Storyboard.TargetName = "p1"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle3}"
                                To = "{StaticResource ParticleEndAngle3}"
                                BeginTime = "{StaticResource ParticleBeginTime3}"
                                Duration = "{StaticResource ParticleDuration3}"/>
                        </Storyboard>
                    </BeginStoryboard>
                    <BeginStoryboard>
                        <Storyboard
                            x:Key = "MetroLoadingAnimation"
                            BeginTime = "{StaticResource StoryBoardBeginTimeP2}"
                            Duration = "{StaticResource StoryBoardDuration}"
                            RepeatBehavior = "Forever">                             

                            <DoubleAnimation
                                Storyboard.TargetName = "p2"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle1}"
                                To = "{StaticResource ParticleEndAngle1}"
                                BeginTime = "{StaticResource ParticleBeginTime1}"
                                Duration = "{StaticResource ParticleDuration1}"/>
                            <DoubleAnimation
                                Storyboard.TargetName = "p2"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle2}"
                                To = "{StaticResource ParticleEndAngle2}"
                                BeginTime = "{StaticResource ParticleBeginTime2}"
                                Duration = "{StaticResource ParticleDuration2}"/>
                            <DoubleAnimation
                                Storyboard.TargetName = "p2"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle3}"
                                To = "{StaticResource ParticleEndAngle3}"
                                BeginTime = "{StaticResource ParticleBeginTime3}"
                                Duration = "{StaticResource ParticleDuration3}"/>
                        </Storyboard>
                    </BeginStoryboard>

                    <BeginStoryboard>
                        <Storyboard
                            x:Key = "MetroLoadingAnimation"
                            BeginTime = "{StaticResource StoryBoardBeginTimeP3}"
                            Duration = "{StaticResource StoryBoardDuration}"
                            RepeatBehavior = "Forever">                             

                            <DoubleAnimation
                                Storyboard.TargetName = "p3"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle1}"
                                To = "{StaticResource ParticleEndAngle1}"
                                BeginTime = "{StaticResource ParticleBeginTime1}"
                                Duration = "{StaticResource ParticleDuration1}"/>
                            <DoubleAnimation
                                Storyboard.TargetName = "p3"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle2}"
                                To = "{StaticResource ParticleEndAngle2}"
                                BeginTime = "{StaticResource ParticleBeginTime2}"
                                Duration = "{StaticResource ParticleDuration2}"/>
                            <DoubleAnimation
                                Storyboard.TargetName = "p3"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle3}"
                                To = "{StaticResource ParticleEndAngle3}"
                                BeginTime = "{StaticResource ParticleBeginTime3}"
                                Duration = "{StaticResource ParticleDuration3}"/>
                        </Storyboard>
                    </BeginStoryboard>
                    
                    <BeginStoryboard>
                        <Storyboard
                            x:Key = "MetroLoadingAnimation"
                            BeginTime = "{StaticResource StoryBoardBeginTimeP4}"
                            Duration = "{StaticResource StoryBoardDuration}"
                            RepeatBehavior = "Forever">                             

                            <DoubleAnimation
                                Storyboard.TargetName = "p4"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle1}"
                                To = "{StaticResource ParticleEndAngle1}"
                                BeginTime = "{StaticResource ParticleBeginTime1}"
                                Duration = "{StaticResource ParticleDuration1}"/>
                            <DoubleAnimation
                                Storyboard.TargetName = "p4"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle2}"
                                To = "{StaticResource ParticleEndAngle2}"
                                BeginTime = "{StaticResource ParticleBeginTime2}"
                                Duration = "{StaticResource ParticleDuration2}"/>
                            <DoubleAnimation
                                Storyboard.TargetName = "p4"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)"
                                From = "{StaticResource ParticleBeginAngle3}"
                                To = "{StaticResource ParticleEndAngle3}"
                                BeginTime = "{StaticResource ParticleBeginTime3}"
                                Duration = "{StaticResource ParticleDuration3}"/>
                        </Storyboard>
                    </BeginStoryboard>                    
                </EventTrigger.Actions>
            </EventTrigger>
        </Canvas.Triggers>
        <Border
            x:Name = "p0"
            Background = "{StaticResource ParticleBackgroundColor}"
            Opacity = "{StaticResource ParticleOpacity}">
            <Border.RenderTransform>
                <RotateTransform/>
            </Border.RenderTransform>
            <Border.RenderTransformOrigin>
                <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
            </Border.RenderTransformOrigin>
            <Ellipse Style = "{StaticResource EllipseStyle}">
                <Ellipse.RenderTransform>
                    <TransformGroup>
                        <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
                        <RotateTransform Angle = "{StaticResource ParticleOriginAngleP0}"/>
                    </TransformGroup>
                </Ellipse.RenderTransform>
            </Ellipse>
        </Border>
        <Border
            x:Name = "p1"
            Background = "{StaticResource ParticleBackgroundColor}"
            Opacity = "{StaticResource ParticleOpacity}">
            <Border.RenderTransform>
                <RotateTransform/>
            </Border.RenderTransform>
            <Border.RenderTransformOrigin>
                <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
            </Border.RenderTransformOrigin>
            <Ellipse Style = "{StaticResource EllipseStyle}">
                <Ellipse.RenderTransform>
                    <TransformGroup>
                        <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
                        <RotateTransform Angle = "{StaticResource ParticleOriginAngleP1}"/>
                    </TransformGroup>
                </Ellipse.RenderTransform>
            </Ellipse>
        </Border>
        <Border
            x:Name = "p2"
            Background = "{StaticResource ParticleBackgroundColor}"
            Opacity = "{StaticResource ParticleOpacity}">
            <Border.RenderTransform>
                <RotateTransform/>
            </Border.RenderTransform>
            <Border.RenderTransformOrigin>
                <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
            </Border.RenderTransformOrigin>
            <Ellipse Style = "{StaticResource EllipseStyle}">
                <Ellipse.RenderTransform>
                    <TransformGroup>
                        <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
                        <RotateTransform Angle = "{StaticResource ParticleOriginAngleP2}"/>
                    </TransformGroup>
                </Ellipse.RenderTransform>
            </Ellipse>
        </Border>
        <Border
            x:Name = "p3"
            Background = "{StaticResource ParticleBackgroundColor}"
            Opacity = "{StaticResource ParticleOpacity}">
            <Border.RenderTransform>
                <RotateTransform/>
            </Border.RenderTransform>
            <Border.RenderTransformOrigin>
                <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
            </Border.RenderTransformOrigin>
            <Ellipse Style = "{StaticResource EllipseStyle}">
                <Ellipse.RenderTransform>
                    <TransformGroup>
                        <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
                        <RotateTransform Angle = "{StaticResource ParticleOriginAngleP3}"/>
                    </TransformGroup>
                </Ellipse.RenderTransform>
            </Ellipse>
        </Border>
        <Border
            x:Name = "p4"
            Background = "{StaticResource ParticleBackgroundColor}"
            Opacity = "{StaticResource ParticleOpacity}">
            <Border.RenderTransform>
                <RotateTransform/>
            </Border.RenderTransform>
            <Border.RenderTransformOrigin>
                <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
            </Border.RenderTransformOrigin>
            <Ellipse Style = "{StaticResource EllipseStyle}">
                <Ellipse.RenderTransform>
                    <TransformGroup>
                        <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
                        <RotateTransform Angle = "{StaticResource ParticleOriginAngleP4}"/>
                    </TransformGroup>
                </Ellipse.RenderTransform>
            </Ellipse>
        </Border>
    </Canvas>
</Grid>

However, due to the limitation of Silverlight, the above code will give you some errors during the design time. Here is the code for Silverlight:

Silverlight Version 

<Grid
    xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:system = "clr-namespace:System;assembly=mscorlib"
    xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions">
    <Grid.Resources>
        <!-- Particle Styling -->
        <SolidColorBrush x:Key = "ParticleColor" Color = "#006699"/>
        <SolidColorBrush x:Key = "ParticleBackgroundColor" Color = "Transparent"/>
        <system:Double x:Key = "ParticleOpacity">1</system:Double>
        <system:Double x:Key = "ParticleRadius">5</system:Double>
        
        <system:Double x:Key = "StartingPointX">0</system:Double>
        <system:Double x:Key = "StartingPointY">-20</system:Double>
        
        <system:Double x:Key = "RotationPointX">0.5</system:Double>
        <system:Double x:Key = "RotationPointY">0.5</system:Double>       
        
        <!-- Particle Origin Angles -->
        <system:Double x:Key = "ParticleOriginAngleP0">0</system:Double>
        <system:Double x:Key = "ParticleOriginAngleP1">-10</system:Double>
        <system:Double x:Key = "ParticleOriginAngleP2">-20</system:Double>
        <system:Double x:Key = "ParticleOriginAngleP3">-30</system:Double>
        <system:Double x:Key = "ParticleOriginAngleP4">-40</system:Double>
        
        <!-- Particle Position & Timing 1 -->
        <system:Double x:Key = "ParticleBeginAngle1">0</system:Double>
        <system:Double x:Key = "ParticleEndAngle1">90</system:Double>
        
        <!-- Particle Position & Timing 2 -->
        <system:Double x:Key = "ParticleBeginAngle2">90</system:Double>
        <system:Double x:Key = "ParticleEndAngle2">270</system:Double>
        
        <!-- Particle Position & Timing 3 -->
        <system:Double x:Key = "ParticleBeginAngle3">270</system:Double>
        <system:Double x:Key = "ParticleEndAngle3">360</system:Double>
        
        <Style x:Key = "EllipseStyle" TargetType = "Ellipse">
            <Setter Property = "Width" Value = "{StaticResource ParticleRadius}"/>
            <Setter Property = "Height" Value = "{StaticResource ParticleRadius}"/>
            <Setter Property = "Fill" Value = "{StaticResource ParticleColor}"/>
            <Setter Property = "RenderTransformOrigin" Value = "0.5, 0.5"/>
            <Setter Property = "Opacity" Value = "{StaticResource ParticleOpacity}"/>
        </Style>
    </Grid.Resources>
    <Canvas Width = "50" Height = "50">
        <Canvas.Triggers>
            <EventTrigger RoutedEvent = "Canvas.Loaded">
                <EventTrigger.Actions>
                    <BeginStoryboard>
                        <Storyboard
                            BeginTime = "00:00:00.000"
                            Duration = "00:00:01.800"
                            RepeatBehavior = "Forever">
                            <DoubleAnimationUsingKeyFrames 
                                Storyboard.TargetName = "p0"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)">
                                <EasingDoubleKeyFrame KeyTime="00:00:00.000" Value="0"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:00.750" Value="90"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:01.050" Value="270"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:01.800" Value="360"/>
                            </DoubleAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                    <BeginStoryboard>
                        <Storyboard
                            BeginTime = "00:00:00.100"
                            Duration = "00:00:01.800"
                            RepeatBehavior = "Forever">
                            <DoubleAnimationUsingKeyFrames 
                                Storyboard.TargetName = "p1"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)">
                                <EasingDoubleKeyFrame KeyTime="00:00:00.000" Value="0"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:00.750" Value="90"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:01.050" Value="270"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:01.800" Value="360"/>
                            </DoubleAnimationUsingKeyFrames>                        
                        </Storyboard>
                    </BeginStoryboard>
                    <BeginStoryboard>
                        <Storyboard
                            BeginTime = "00:00:00.200"
                            Duration = "00:00:01.800"
                            RepeatBehavior = "Forever">
                            <DoubleAnimationUsingKeyFrames 
                                Storyboard.TargetName = "p2"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)">
                                <EasingDoubleKeyFrame KeyTime="00:00:00.000" Value="0"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:00.750" Value="90"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:01.050" Value="270"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:01.800" Value="360"/>
                            </DoubleAnimationUsingKeyFrames>                        
                        </Storyboard>
                    </BeginStoryboard>
                    <BeginStoryboard>
                        <Storyboard
                            BeginTime = "00:00:00.300"
                            Duration = "00:00:01.800"
                            RepeatBehavior = "Forever">
                            <DoubleAnimationUsingKeyFrames 
                                Storyboard.TargetName = "p3"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)">
                                <EasingDoubleKeyFrame KeyTime="00:00:00.000" Value="0"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:00.750" Value="90"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:01.050" Value="270"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:01.800" Value="360"/>
                            </DoubleAnimationUsingKeyFrames>                        
                        </Storyboard>
                    </BeginStoryboard>
                    <BeginStoryboard>
                        <Storyboard
                            BeginTime = "00:00:00.400"
                            Duration = "00:00:01.800"
                            RepeatBehavior = "Forever">
                            <DoubleAnimationUsingKeyFrames 
                                Storyboard.TargetName = "p4"
                                Storyboard.TargetProperty = "(UIElement.RenderTransform).(RotateTransform.Angle)">
                                <EasingDoubleKeyFrame KeyTime="00:00:00.000" Value="0"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:00.750" Value="90"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:01.050" Value="270"/>
                                <EasingDoubleKeyFrame KeyTime="00:00:01.800" Value="360"/>
                            </DoubleAnimationUsingKeyFrames>                        
                        </Storyboard>
                    </BeginStoryboard>                    
                </EventTrigger.Actions>
            </EventTrigger>
        </Canvas.Triggers>
        <Border
            x:Name = "p0"
            Background = "{StaticResource ParticleBackgroundColor}"
            Opacity = "{StaticResource ParticleOpacity}">
            <Border.RenderTransform>
                <RotateTransform/>
            </Border.RenderTransform>
            <Border.RenderTransformOrigin>
                <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
            </Border.RenderTransformOrigin>
            <Ellipse Style = "{StaticResource EllipseStyle}">
                <Ellipse.RenderTransform>
                    <TransformGroup>
                        <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
                        <RotateTransform Angle = "{StaticResource ParticleOriginAngleP0}"/>
                    </TransformGroup>
                </Ellipse.RenderTransform>
            </Ellipse>
        </Border>
        <Border
            x:Name = "p1"
            Background = "{StaticResource ParticleBackgroundColor}"
            Opacity = "{StaticResource ParticleOpacity}">
            <Border.RenderTransform>
                <RotateTransform/>
            </Border.RenderTransform>
            <Border.RenderTransformOrigin>
                <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
            </Border.RenderTransformOrigin>
            <Ellipse Style = "{StaticResource EllipseStyle}">
                <Ellipse.RenderTransform>
                    <TransformGroup>
                        <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
                        <RotateTransform Angle = "{StaticResource ParticleOriginAngleP1}"/>
                    </TransformGroup>
                </Ellipse.RenderTransform>
            </Ellipse>
        </Border>
        <Border
            x:Name = "p2"
            Background = "{StaticResource ParticleBackgroundColor}"
            Opacity = "{StaticResource ParticleOpacity}">
            <Border.RenderTransform>
                <RotateTransform/>
            </Border.RenderTransform>
            <Border.RenderTransformOrigin>
                <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
            </Border.RenderTransformOrigin>
            <Ellipse Style = "{StaticResource EllipseStyle}">
                <Ellipse.RenderTransform>
                    <TransformGroup>
                        <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
                        <RotateTransform Angle = "{StaticResource ParticleOriginAngleP2}"/>
                    </TransformGroup>
                </Ellipse.RenderTransform>
            </Ellipse>
        </Border>
        <Border
            x:Name = "p3"
            Background = "{StaticResource ParticleBackgroundColor}"
            Opacity = "{StaticResource ParticleOpacity}">
            <Border.RenderTransform>
                <RotateTransform/>
            </Border.RenderTransform>
            <Border.RenderTransformOrigin>
                <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
            </Border.RenderTransformOrigin>
            <Ellipse Style = "{StaticResource EllipseStyle}">
                <Ellipse.RenderTransform>
                    <TransformGroup>
                        <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
                        <RotateTransform Angle = "{StaticResource ParticleOriginAngleP3}"/>
                    </TransformGroup>
                </Ellipse.RenderTransform>
            </Ellipse>
        </Border>
        <Border
            x:Name = "p4"
            Background = "{StaticResource ParticleBackgroundColor}"
            Opacity = "{StaticResource ParticleOpacity}">
            <Border.RenderTransform>
                <RotateTransform/>
            </Border.RenderTransform>
            <Border.RenderTransformOrigin>
                <Point X = "{StaticResource RotationPointX}" Y = "{StaticResource RotationPointY}"/>
            </Border.RenderTransformOrigin>
            <Ellipse Style = "{StaticResource EllipseStyle}">
                <Ellipse.RenderTransform>
                    <TransformGroup>
                        <TranslateTransform X = "{StaticResource StartingPointX}" Y = "{StaticResource StartingPointY}"/>
                        <RotateTransform Angle = "{StaticResource ParticleOriginAngleP4}"/>
                    </TransformGroup>
                </Ellipse.RenderTransform>
            </Ellipse>
        </Border>
    </Canvas>
</Grid>

You can also override the styles of the default busy indicator provided in the Silverlight toolkit (if you are using Silverlight) by adding the canvas and the storyboard at appropriate locations and customize them as needed.

Points of Interest

My current implementation still doesn't look exactly like the original Metro-style loading animation. The radius of the circle the particles rotate around, the radius of the particles themselves, and the timing should be the key.

History 

  • 14 Feb, 2013: First release.
  • 19 Feb, 2013: Updated the code to make the animation more Metro-like and moved all the configurations to the top.
  • 20 Feb, 2013: Added the XAML code for Silverlight.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

AesopTurtle
Software Developer (Senior)
Thailand Thailand
No Biography provided
Follow on   Twitter

Comments and Discussions

 
QuestionBrilliant PinprofessionalDycz17-Jul-14 21:56 
QuestionGreat PinmemberMember 1078974828-May-14 4:32 
GeneralMy vote of 5 PinmemberJasper4C#16-Feb-13 21:59 
GeneralRe: My vote of 5 PinmemberAesopTurtle19-Feb-13 18:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140814.1 | Last Updated 20 Feb 2013
Article Copyright 2013 by AesopTurtle
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid