Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating Metro (Windows 8) Loading Animation Using XAML

0.00/5 (No votes)
19 Feb 2013 2  
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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here