Click here to Skip to main content
15,884,686 members
Articles / Desktop Programming / WPF
Tip/Trick

Creating Metro (Windows 8) Loading Animation Using XAML

Rate me:
Please Sign up or sign in to vote.
4.86/5 (16 votes)
19 Feb 2013CPOL2 min read 59.5K   18   11
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

XML
<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 

XML
<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)


Written By
Software Developer (Senior)
Thailand Thailand
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
AnswerIncreasing Size Pin
remy_24-Jun-15 9:59
remy_24-Jun-15 9:59 
GeneralMy vote of 4 Pin
Ivan Krivyakov14-Dec-14 12:07
Ivan Krivyakov14-Dec-14 12:07 
QuestionProgress Bar? Pin
Member 130939730-Nov-14 14:31
Member 130939730-Nov-14 14:31 
Good job. Thank you for sharing.
I'm new in XAML. I'm wondering if it's possible to also create a windows 8 progress bar using your method.
If you already created that one please share with us.

Thanks
QuestionHow to change height width of this loader Pin
XshivDeveloper11-Nov-14 21:57
XshivDeveloper11-Nov-14 21:57 
GeneralVery good job! Pin
AWriter25-Sep-14 1:15
AWriter25-Sep-14 1:15 
QuestionCool Pin
chathux21-Aug-14 7:30
chathux21-Aug-14 7:30 
QuestionBrilliant Pin
Dycz17-Jul-14 21:56
professionalDycz17-Jul-14 21:56 
QuestionGreat Pin
Member 1078974828-May-14 4:32
Member 1078974828-May-14 4:32 
GeneralMy vote of 5 Pin
Jasper4C#16-Feb-13 21:59
Jasper4C#16-Feb-13 21:59 
GeneralRe: My vote of 5 Pin
AesopTurtle19-Feb-13 18:57
AesopTurtle19-Feb-13 18:57 

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

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