Click here to Skip to main content
15,867,330 members
Articles / Desktop Programming / WPF

Custom Loader Animations in Silverlight

Rate me:
Please Sign up or sign in to vote.
4.67/5 (8 votes)
22 Feb 2011CPOL5 min read 31.7K   856   22   4
A guide to making custom loading animations for your applications in Silverlight.

14.jpg

Check out the live demo of all the variations here. And view the original post on my blog here.

Introduction

In the previous post, I showed how to create a pretty simple navigation controller which, well, controls navigation. In this post, I am going to describe how to create a Silverlight loader.

Almost every application which has any sort of data etc., needs some method of letting the user know that it is busy and has not frozen. With the rise of AJAX websites/applications, there are many sites floating around which provide little GIF animations for download. Some of them even allow you to pick the colors and dimensions. Here are a few of those sites:

The problem is that Silverlight provides no built-in support for GIF images. I assume that they did not see the need for the added file size when Silverlight itself has a great animation engine. There are, of course, ways to get GIF images working (ImageTools at CodePlex), but I find it very useful to create them in XAML, especially when you are told to add one byte to the color or move something 1px to the left.

I am going to show you how to create a couple of different types of loaders.

The Code

For each loader that we create, we will make a UserControl which has a Storyboard that does the animation, and then expose certain properties of the DoubleAnimation(s) inside that Storyboard. Now, for every loader we make, we will add this code to the code file:

C#
private EasingFunctionBase _easingFunction;

public EasingFunctionBase EasingFunction
{
    get { return _easingFunction; }
    set
    {
        _easingFunction = value;
        foreach (Timeline anim in animStoryboard.Children)
        {
            if (anim is DoubleAnimation)
            {
                (anim as DoubleAnimation).EasingFunction = value;
            }
            else
            {
                if (anim is ColorAnimation)
                {
                    (anim as ColorAnimation).EasingFunction = value;
                }
                else
                {
                    if (anim is DoubleAnimationUsingKeyFrames)
                    {
                        foreach (DoubleKeyFrame key in (anim as 
                                     DoubleAnimationUsingKeyFrames).KeyFrames)
                        {
                            if (key is EasingDoubleKeyFrame)
                            {
                                (key as EasingDoubleKeyFrame).EasingFunction = value;
                            }
                        }
                    }
                }
            }
        }
    }
}

private RepeatBehavior _repeatBehavior;

public RepeatBehavior RepeatBehavior
{
    get { return _repeatBehavior; }
    set
    {
        _repeatBehavior = value;
        foreach (Timeline anim in animStoryboard.Children)
        {
            anim.RepeatBehavior = value;
        }
    }
}

private Duration _duration;

public Duration Duration
{
    get { return _duration; }
    set
    {
        _duration = value;
        foreach (Timeline anim in animStoryboard.Children)
        {
            anim.Duration = value;
        }
    }
}

private bool _autoReverse;

public bool AutoReverse
{
    get { return _autoReverse; }
    set
    {
        _autoReverse = value;
        foreach (Timeline anim in animStoryboard.Children)
        {
            anim.AutoReverse = value;
        }
    }
}

private TimeSpan? _addToBeginTime;

public TimeSpan? AddToBeginTime
{
    get { return _addToBeginTime; }
    set
    {
        _addToBeginTime = value; 
        foreach (Timeline anim in animStoryboard.Children)
        {
            anim.BeginTime += value;
        }
    }
}

SwirlyCircles.xaml

The first one we will make is a standard ring of circles which spins around. The easiest way to make those circles is to use Microsoft Expression Design. Below, I will go through some basic steps in case you haven't used it before. If you don't have it, no worries - just copy the resulting XAML code at the bottom.

  1. Open up Expression Design and click File > New and set the Width and Height to 100px and confirm.
  2. 1.jpg

  3. Now select the Ellipse icon in the toolbar, then click and drag on the canvas to draw a circle (don't worry about size or position).
  4. 2.png3.jpg

  5. Now, with the circle selected, change the values on the bottom toolbar to position and size it correctly.
  6. 4.jpg

  7. With the circle selected, copy and paste it. Now, position it in the same place, except at the bottom.
  8. 5.jpg

  9. With Shift held down, select both of them. Then copy (Ctrl+C) and paste in place (Ctrl+F).
  10. Again, with the Shift key held down, put your cursor just outside the corner block and click and drag down to rotate it diagonally.
  11. 6.jpg

  12. Do step 5 and 6 two more times so that you have a full ring.
  13. 7.jpg

  14. Now you can select them all and color them how you like on the right toolbar.
  15. 8.jpg

  16. Finally, with them still selected, click Edit > Copy XAML.
  17. 9.jpg

Now that that is done, you can go ahead and add a new UserControl to your project called SwirlyCircles.xaml. Add that code we mentioned previously to the .cs file. Then go to the XAML file and paste the XAML that you copied from Expression Design.

XML
<!--I removed some attributes from the Canvas tag that weren't needed-->
    <Canvas Width="100" Height="100">
        <Ellipse Width="13.25" Height="13.25" 
                 Canvas.Left="43.4583" 
                 Canvas.Top="7.62939e-006" 
                 Stretch="Fill">
            <Ellipse.Fill>
                <RadialGradientBrush RadiusX="0.5" 
                          RadiusY="0.5" Center="0.5,0.5" 
                          GradientOrigin="0.5,0.5">
                    <RadialGradientBrush.GradientStops>
                        <GradientStop Color="#FFFF0000" Offset="0"/>
                        <GradientStop Color="#FFFFFFFF" Offset="1"/>
                    </RadialGradientBrush.GradientStops>
                    <RadialGradientBrush.RelativeTransform>
                        <TransformGroup/>
                    </RadialGradientBrush.RelativeTransform>
                </RadialGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
        <Ellipse Width="14" Height="14" 
               Canvas.Left="43" 
               Canvas.Top="7.62939e-006" Stretch="Fill">
            <Ellipse.Fill>
                <RadialGradientBrush RadiusX="0.5" 
                        RadiusY="0.499999" 
                        Center="0.5,0.499999" 
                        GradientOrigin="0.5,0.499999">
                    <RadialGradientBrush.GradientStops>
                        <GradientStop Color="#FFFF0000" Offset="0"/>
                        <GradientStop Color="#FFFFFFFF" Offset="1"/>
                    </RadialGradientBrush.GradientStops>
                    <RadialGradientBrush.RelativeTransform>
                        <TransformGroup/>
                    </RadialGradientBrush.RelativeTransform>
                </RadialGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
        <Ellipse Width="14" Height="14" 
               Canvas.Left="43" Canvas.Top="86" 
               Stretch="Fill">
            <Ellipse.Fill>
                <RadialGradientBrush RadiusX="0.500001" 
                          RadiusY="0.499999" 
                          Center="0.5,0.499999" 
                          GradientOrigin="0.5,0.499999">
                    <RadialGradientBrush.GradientStops>
                        <GradientStop Color="#FFFF0000" Offset="0"/>
                        <GradientStop Color="#FFFFFFFF" Offset="1"/>
                    </RadialGradientBrush.GradientStops>
                    <RadialGradientBrush.RelativeTransform>
                        <TransformGroup/>
                    </RadialGradientBrush.RelativeTransform>
                </RadialGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
        <Path Width="14" Height="14" 
              Canvas.Left="73.4056" Canvas.Top="12.5944" 
              Stretch="Fill" 
              Data="F1 M 85.3553,14.6447C 88.089,17.3783 88.089,
                    21.8105 85.3553,24.5442C 82.6217,27.2778 78.1895,
                    27.2778 75.4558,24.5442C 72.7222,21.8105 72.7222,
                    17.3783 75.4558,14.6447C 78.1895,11.911 82.6217,
                    11.911 85.3553,14.6447 Z ">
            <Path.Fill>
                <RadialGradientBrush RadiusX="0.5" 
                          RadiusY="0.499999" 
                          Center="0.500001,0.5" 
                          GradientOrigin="0.500001,0.5">
                    <RadialGradientBrush.GradientStops>
                        <GradientStop Color="#FFFF0000" Offset="0"/>
                        <GradientStop Color="#FFFFFFFF" Offset="1"/>
                    </RadialGradientBrush.GradientStops>
                    <RadialGradientBrush.RelativeTransform>
                        <TransformGroup>
                            <RotateTransform CenterX="0.500001" 
                                 CenterY="0.5" Angle="45"/>
                        </TransformGroup>
                    </RadialGradientBrush.RelativeTransform>
                </RadialGradientBrush>
            </Path.Fill>
        </Path>
        <Path Width="14" Height="14" 
               Canvas.Left="12.5944" Canvas.Top="73.4056" 
               Stretch="Fill" 
               Data="F1 M 24.5442,75.4558C 27.2778,78.1895 27.2778,
                     82.6217 24.5442,85.3553C 21.8105,88.089 17.3783,
                     88.089 14.6447,85.3553C 11.911,82.6217 11.911,
                     78.1895 14.6447,75.4558C 17.3783,72.7222 21.8105,
                     72.7222 24.5442,75.4558 Z ">
            <Path.Fill>
                <RadialGradientBrush RadiusX="0.5" 
                       RadiusY="0.5" Center="0.5,0.5" 
                       GradientOrigin="0.5,0.5">
                    <RadialGradientBrush.GradientStops>
                        <GradientStop Color="#FFFF0000" Offset="0"/>
                        <GradientStop Color="#FFFFFFFF" Offset="1"/>
                    </RadialGradientBrush.GradientStops>
                    <RadialGradientBrush.RelativeTransform>
                        <TransformGroup>
                            <RotateTransform CenterX="0.5" 
                                 CenterY="0.5" Angle="45"/>
                        </TransformGroup>
                    </RadialGradientBrush.RelativeTransform>
                </RadialGradientBrush>
            </Path.Fill>
        </Path>
        <Ellipse Width="14" Height="14" 
               Canvas.Left="86" Canvas.Top="43" 
               Stretch="Fill">
            <Ellipse.Fill>
                <RadialGradientBrush RadiusX="0.5" 
                      RadiusY="0.5" Center="0.5,0.5" 
                      GradientOrigin="0.5,0.5">
                    <RadialGradientBrush.GradientStops>
                        <GradientStop Color="#FFFF0000" Offset="0"/>
                        <GradientStop Color="#FFFFFFFF" Offset="1"/>
                    </RadialGradientBrush.GradientStops>
                    <RadialGradientBrush.RelativeTransform>
                        <TransformGroup>
                            <RotateTransform CenterX="0.5" 
                                  CenterY="0.5" Angle="90"/>
                        </TransformGroup>
                    </RadialGradientBrush.RelativeTransform>
                </RadialGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
        <Ellipse Width="14" Height="14" 
              Canvas.Left="2.73207e-006" 
              Canvas.Top="43" Stretch="Fill">
            <Ellipse.Fill>
                <RadialGradientBrush RadiusX="0.5" 
                         RadiusY="0.499999" Center="0.500001,0.5" 
                         GradientOrigin="0.500001,0.5">
                    <RadialGradientBrush.GradientStops>
                        <GradientStop Color="#FFFF0000" Offset="0"/>
                        <GradientStop Color="#FFFFFFFF" Offset="1"/>
                    </RadialGradientBrush.GradientStops>
                    <RadialGradientBrush.RelativeTransform>
                        <TransformGroup>
                            <RotateTransform CenterX="0.500001" 
                                   CenterY="0.5" Angle="90"/>
                        </TransformGroup>
                    </RadialGradientBrush.RelativeTransform>
                </RadialGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
        <Path Width="14" Height="14" 
              Canvas.Left="73.4056" Canvas.Top="73.4056" 
              Stretch="Fill" 
              Data="F1 M 85.3553,85.3553C 82.6217,88.089 78.1895,
                    88.089 75.4558,85.3553C 72.7222,82.6217 72.7222,
                    78.1895 75.4558,75.4558C 78.1895,72.7222 82.6217,
                    72.7222 85.3553,75.4558C 88.089,78.1895 88.089,
                    82.6217 85.3553,85.3553 Z ">
            <Path.Fill>
                <RadialGradientBrush RadiusX="0.5" 
                       RadiusY="0.499999" Center="0.5,0.500001" 
                       GradientOrigin="0.5,0.500001">
                    <RadialGradientBrush.GradientStops>
                        <GradientStop Color="#FFFF0000" Offset="0"/>
                        <GradientStop Color="#FFFFFFFF" Offset="1"/>
                    </RadialGradientBrush.GradientStops>
                    <RadialGradientBrush.RelativeTransform>
                        <TransformGroup>
                            <RotateTransform CenterX="0.5" 
                                  CenterY="0.500001" Angle="135"/>
                        </TransformGroup>
                    </RadialGradientBrush.RelativeTransform>
                </RadialGradientBrush>
            </Path.Fill>
        </Path>
        <Path Width="14" Height="14" 
                Canvas.Left="12.5944" Canvas.Top="12.5944" 
                Stretch="Fill" 
                Data="F1 M 24.5442,24.5442C 21.8105,27.2778 17.3783,
                      27.2778 14.6447,24.5442C 11.911,21.8105 11.911,
                      17.3783 14.6447,14.6447C 17.3783,11.911 21.8105,
                      11.911 24.5442,14.6447C 27.2778,17.3783 27.2778,
                      21.8105 24.5442,24.5442 Z ">
            <Path.Fill>
                <RadialGradientBrush RadiusX="0.5" 
                        RadiusY="0.5" Center="0.5,0.5" 
                        GradientOrigin="0.5,0.5">
                    <RadialGradientBrush.GradientStops>
                        <GradientStop Color="#FFFF0000" Offset="0"/>
                        <GradientStop Color="#FFFFFFFF" Offset="1"/>
                    </RadialGradientBrush.GradientStops>
                    <RadialGradientBrush.RelativeTransform>
                        <TransformGroup>
                            <RotateTransform CenterX="0.5" 
                                   CenterY="0.5" Angle="135"/>
                        </TransformGroup>
                    </RadialGradientBrush.RelativeTransform>
                </RadialGradientBrush>
            </Path.Fill>
        </Path>
    </Canvas>

Now, just above the closing tag for the Canvas, we are going to give it a Render Transform to rotate it - because this loader will simply be spinning circles.

XML
<Canvas.RenderTransform>
    <RotateTransform x:Name="rotateTransform" 
            CenterX="50" CenterY="50"  />
</Canvas.RenderTransform>

That RotateTransform has an Angle property which is a double from 0 to 360 degrees. To control that, we are going to need a storyboard. To run a storyboard as the control loads, we can call .Begin(); in the constructor. But an easier way is to use a Trigger which will start the storyboard as soon as the control fires its Loaded event. To do that is simple. Add this just above the closing tag of the Canvas:

XML
<Canvas.Triggers>
    <EventTrigger RoutedEvent="UserControl.Loaded">
        <BeginStoryboard>
            <Storyboard x:Name="animStoryboard">
                <DoubleAnimation Duration="00:00:03" 
                    To="360" RepeatBehavior="Forever" 
                    Storyboard.TargetName="rotateTransform" 
                    Storyboard.TargetProperty="Angle"/>
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>
</Canvas.Triggers>

To see the result, first build the project, then go to MainPage.xaml and drag in your loader control from the Toolbox on the left. It will come out something like this:

XML
<my:SwirlyCircles />

If you remember from the first code block, there were a number of properties to do with the DoubleAnimation that we exposed. We can set those in MainPage.xaml or wherever you use the control, like this:

XML
<my:SwirlyCircles AutoReverse="True" 
        Duration="00:00:05" AddToBeginTime="00:00:00">
    <my:SwirlyCircles.EasingFunction>
        <BounceEase />
    </my:SwirlyCircles.EasingFunction>
</my:SwirlyCircles>

One quick thing to take note of is that, by default, the Background property of the default Grid control is White. Make sure you remove that or set it to Transparent to avoid having a white block around your loader.

When you run the project, you will see something like this:

10.jpg

LittleBoxes.xaml

To make this one, the steps are basically the same as the first, so I won't bore you with the details again. Just add a new UserControl, and put in those properties in the .cs file. Then to set up the look of it, along with the animations, put this in the existing Grid:

XML
<Canvas Width="100" Height="40">
    <Border x:Name="border1" Width="10" Height="10" 
          Canvas.Left="0" CornerRadius="5" 
          Background="#FF2B8EA0"/>
    <Border x:Name="border2" Width="10" 
          Height="10" Canvas.Left="10" 
          CornerRadius="5" Background="#FF2B8EA0" />
    <Border x:Name="border3" Width="10" 
          Height="10" Canvas.Left="20" 
          CornerRadius="5" Background="#FF2B8EA0" />
    <Border x:Name="border4" Width="10" 
          Height="10" Canvas.Left="30" 
          CornerRadius="5" Background="#FF2B8EA0" />
    <Border x:Name="border5" Width="10" 
          Height="10" Canvas.Left="40" 
          CornerRadius="5" Background="#FF2B8EA0" />
    <Border x:Name="border6" Width="10" 
          Height="10" Canvas.Left="50" 
          CornerRadius="5" Background="#FF2B8EA0" />
    <Border x:Name="border7" Width="10" 
          Height="10" Canvas.Left="60" 
          CornerRadius="5" Background="#FF2B8EA0" />
    <Border x:Name="border8" Width="10" 
          Height="10" Canvas.Left="70" 
          CornerRadius="5" Background="#FF2B8EA0" />
    <Border x:Name="border9" Width="10" 
          Height="10" Canvas.Left="80" 
          CornerRadius="5" Background="#FF2B8EA0" />
    <Border x:Name="border10" Width="10" 
          Height="10" Canvas.Left="90" 
          CornerRadius="5" Background="#FF2B8EA0" />
    <Canvas.Triggers>
        <EventTrigger RoutedEvent="UserControl.Loaded">
            <BeginStoryboard>
                <Storyboard x:Name="animStoryboard" 
                         Storyboard.TargetProperty="(Canvas.Top)">
                    <DoubleAnimation Storyboard.TargetName="border1" 
                          To="30" BeginTime="00:00:00.1" 
                          Duration="00:00:00.5" 
                          AutoReverse="True" RepeatBehavior="Forever"/>
                    <DoubleAnimation Storyboard.TargetName="border2" 
                          To="30" BeginTime="00:00:00.2" 
                          Duration="00:00:00.5" AutoReverse="True" 
                          RepeatBehavior="Forever"/>
                    <DoubleAnimation Storyboard.TargetName="border3" 
                        To="30" BeginTime="00:00:00.3" 
                        Duration="00:00:00.5" AutoReverse="True" 
                        RepeatBehavior="Forever"/>
                    <DoubleAnimation Storyboard.TargetName="border4" 
                        To="30" BeginTime="00:00:00.4" 
                        Duration="00:00:00.5" AutoReverse="True" 
                        RepeatBehavior="Forever"/>
                    <DoubleAnimation Storyboard.TargetName="border5" 
                        To="30" BeginTime="00:00:00.5" 
                        Duration="00:00:00.5" AutoReverse="True" 
                        RepeatBehavior="Forever"/>
                    <DoubleAnimation Storyboard.TargetName="border6" 
                        To="30" BeginTime="00:00:00.6" 
                        Duration="00:00:00.5" AutoReverse="True" 
                        RepeatBehavior="Forever"/>
                    <DoubleAnimation Storyboard.TargetName="border7" 
                        To="30" BeginTime="00:00:00.7" 
                        Duration="00:00:00.5" AutoReverse="True" 
                        RepeatBehavior="Forever"/>
                    <DoubleAnimation Storyboard.TargetName="border8" 
                        To="30" BeginTime="00:00:00.8" 
                        Duration="00:00:00.5" AutoReverse="True" 
                        RepeatBehavior="Forever"/>
                    <DoubleAnimation Storyboard.TargetName="border9" 
                        To="30" BeginTime="00:00:00.9" 
                        Duration="00:00:00.5" AutoReverse="True" 
                        RepeatBehavior="Forever"/>
                    <DoubleAnimation Storyboard.TargetName="border10" 
                        To="30" BeginTime="00:00:01" 
                        Duration="00:00:00.5" AutoReverse="True" 
                        RepeatBehavior="Forever"/>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Canvas.Triggers>
</Canvas>

And here is the result:

11.jpg

WP7.xaml

I really like the Windows Phone 7 loader, so this one is similar to that. As the animations are pretty specific for this, you may want to leave off a few of the properties.

XML
<Canvas Width="200" Height="7">
    <Rectangle x:Name="rect1" Width="7" Height="7" 
               Fill="#EE00ABA9" Stroke="Black" />
    <Rectangle x:Name="rect2" Width="7" Height="7" 
               Fill="#EE00ABA9" Stroke="Black" />
    <Rectangle x:Name="rect3" Width="7" Height="7" 
               Fill="#EE00ABA9" Stroke="Black" />
    <Rectangle x:Name="rect4" Width="7" Height="7" 
               Fill="#EE00ABA9" Stroke="Black" />
    <Rectangle x:Name="rect5" Width="7" Height="7" 
               Fill="#EE00ABA9" Stroke="Black" />
    <Canvas.Triggers>
        <EventTrigger RoutedEvent="UserControl.Loaded">
            <BeginStoryboard>
                <Storyboard SpeedRatio="1.5" 
                       x:Name="animStoryboard" 
                       Storyboard.TargetProperty="(Canvas.Left)">
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
                            Duration="00:00:03" 
                            Storyboard.TargetName="rect1" 
                            RepeatBehavior="Forever">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="80">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <QuinticEase EasingMode="EaseOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                               
                        <EasingDoubleKeyFrame KeyTime="00:00:03" Value="200">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <QuinticEase EasingMode="EaseIn" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.1" 
                              Duration="00:00:03" 
                              Storyboard.TargetName="rect2" 
                              RepeatBehavior="Forever">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="80">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <QuinticEase EasingMode="EaseOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                        
                        <EasingDoubleKeyFrame KeyTime="00:00:03" Value="200">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <QuinticEase EasingMode="EaseIn" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                    </DoubleAnimationUsingKeyFrames>

                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.2" 
                               Duration="00:00:03" 
                               Storyboard.TargetName="rect3" 
                               RepeatBehavior="Forever">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="80">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <QuinticEase EasingMode="EaseOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                      
                        <EasingDoubleKeyFrame KeyTime="00:00:03" Value="200">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <QuinticEase EasingMode="EaseIn" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                    </DoubleAnimationUsingKeyFrames>

                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.3" 
                             Duration="00:00:03" 
                             Storyboard.TargetName="rect4" 
                             RepeatBehavior="Forever">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="80">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <QuinticEase EasingMode="EaseOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                       
                        <EasingDoubleKeyFrame KeyTime="00:00:03" Value="200">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <QuinticEase EasingMode="EaseIn" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                    </DoubleAnimationUsingKeyFrames>

                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.4" 
                             Duration="00:00:03" 
                             Storyboard.TargetName="rect5" 
                             RepeatBehavior="Forever">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="80">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <QuinticEase EasingMode="EaseOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                       
                        <EasingDoubleKeyFrame KeyTime="00:00:03" Value="200">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <QuinticEase EasingMode="EaseIn" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
                             Duration="00:00:03" RepeatBehavior="Forever" 
                             Storyboard.TargetProperty="Opacity" 
                             Storyboard.TargetName="rect1">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="1"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:02.5" Value="1"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:03" Value="0"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.1" 
                             Duration="00:00:03" RepeatBehavior="Forever" 
                             Storyboard.TargetProperty="Opacity" 
                             Storyboard.TargetName="rect2">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="1"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:02.5" Value="1"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:03" Value="0"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.2" 
                           Duration="00:00:03" RepeatBehavior="Forever" 
                           Storyboard.TargetProperty="Opacity" 
                           Storyboard.TargetName="rect3">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="1"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:02.5" Value="1"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:03" Value="0"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.3" 
                           Duration="00:00:03" RepeatBehavior="Forever" 
                           Storyboard.TargetProperty="Opacity" 
                           Storyboard.TargetName="rect4">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="1"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:02.5" Value="1"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:03" Value="0"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.4" 
                           Duration="00:00:03" RepeatBehavior="Forever" 
                           Storyboard.TargetProperty="Opacity" 
                           Storyboard.TargetName="rect5">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="1"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:02.5" Value="1"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:03" Value="0"/>
                    </DoubleAnimationUsingKeyFrames>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Canvas.Triggers>
</Canvas>

It's not exactly the same, but similar to the one on the phones. After including it in MainPage.xaml or elsewhere, you will get this:

12.jpg

3D.xaml

OK, so this isn't actually 3D at all, but the thought is there. What happens is that the circles moves from left to right and then back to left again. On the trip from left to right, the width and height goes from 7px to 10px and back to 7px, then it goes from right to left and changes from 7px to 0px and back to 7px to start the loop again. This makes it look a bit like it is going back into space.

XML
<Canvas Width="100" Height="10">
    <Border x:Name="border1" Width="10" Height="10" 
         Canvas.Left="0" CornerRadius="5" Background="#EE00ABA9"/>
    <Border x:Name="border2" Width="10" Height="10" 
         Canvas.Left="0" CornerRadius="5" Background="#EE00ABA9"/>
    <Border x:Name="border3" Width="10" Height="10" 
         Canvas.Left="0" CornerRadius="5" Background="#EE00ABA9"/>
    <Border x:Name="border4" Width="10" Height="10" 
         Canvas.Left="0" CornerRadius="5" Background="#EE00ABA9"/>
    <Canvas.Triggers>
        <EventTrigger RoutedEvent="UserControl.Loaded">
            <BeginStoryboard>
                <Storyboard x:Name="animStoryboard">
                    <DoubleAnimationUsingKeyFrames Duration="00:00:01" 
                              BeginTime="00:00:00" RepeatBehavior="Forever" 
                              Storyboard.TargetName="border1" 
                              Storyboard.TargetProperty="Width">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.25" Value="10"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.75" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="7"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames Duration="00:00:01" 
                              BeginTime="00:00:00" RepeatBehavior="Forever" 
                              Storyboard.TargetName="border1" 
                              Storyboard.TargetProperty="Height">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.25" Value="10"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.75" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="7"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames Duration="00:00:01" 
                           BeginTime="00:00:00" RepeatBehavior="Forever" 
                           Storyboard.TargetName="border1" 
                           Storyboard.TargetProperty="(Canvas.Left)">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0" >
                            <EasingDoubleKeyFrame.EasingFunction>
                                <SineEase EasingMode="EaseInOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="90">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <SineEase EasingMode="EaseInOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="0">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <SineEase EasingMode="EaseInOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                    </DoubleAnimationUsingKeyFrames>

                    <DoubleAnimationUsingKeyFrames Duration="00:00:01" 
                            BeginTime="00:00:00.1" RepeatBehavior="Forever" 
                            Storyboard.TargetName="border2" 
                            Storyboard.TargetProperty="Width">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.25" Value="10"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.75" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="7"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames Duration="00:00:01" 
                           BeginTime="00:00:00.1" RepeatBehavior="Forever" 
                           Storyboard.TargetName="border2" 
                           Storyboard.TargetProperty="Height">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.25" Value="10"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.75" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="7"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames Duration="00:00:01" 
                            BeginTime="00:00:00.1" RepeatBehavior="Forever" 
                            Storyboard.TargetName="border2" 
                            Storyboard.TargetProperty="(Canvas.Left)">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0" >
                            <EasingDoubleKeyFrame.EasingFunction>
                                <SineEase EasingMode="EaseInOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="90">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <SineEase EasingMode="EaseInOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="0">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <SineEase EasingMode="EaseInOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                    </DoubleAnimationUsingKeyFrames>

                    <DoubleAnimationUsingKeyFrames Duration="00:00:01" 
                            BeginTime="00:00:00.2" RepeatBehavior="Forever" 
                            Storyboard.TargetName="border3" 
                            Storyboard.TargetProperty="Width">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.25" Value="10"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.75" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="7"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames Duration="00:00:01" 
                            BeginTime="00:00:00.2" RepeatBehavior="Forever" 
                            Storyboard.TargetName="border3" 
                            Storyboard.TargetProperty="Height">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.25" Value="10"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.75" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="7"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames Duration="00:00:01" 
                           BeginTime="00:00:00.2" RepeatBehavior="Forever" 
                           Storyboard.TargetName="border3" 
                           Storyboard.TargetProperty="(Canvas.Left)">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0" >
                            <EasingDoubleKeyFrame.EasingFunction>
                                <SineEase EasingMode="EaseInOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="90">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <SineEase EasingMode="EaseInOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="0">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <SineEase EasingMode="EaseInOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                    </DoubleAnimationUsingKeyFrames>

                    <DoubleAnimationUsingKeyFrames Duration="00:00:01" 
                             BeginTime="00:00:00.3" RepeatBehavior="Forever" 
                             Storyboard.TargetName="border4" 
                             Storyboard.TargetProperty="Width">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.25" Value="10"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.75" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="7"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames Duration="00:00:01" 
                           BeginTime="00:00:00.3" RepeatBehavior="Forever" 
                           Storyboard.TargetName="border4" 
                           Storyboard.TargetProperty="Height">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.25" Value="10"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="7"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.75" Value="0"/>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="7"/>
                    </DoubleAnimationUsingKeyFrames>
                    <DoubleAnimationUsingKeyFrames Duration="00:00:01" 
                            BeginTime="00:00:00.3" RepeatBehavior="Forever" 
                            Storyboard.TargetName="border4" 
                            Storyboard.TargetProperty="(Canvas.Left)">
                        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="0" >
                            <EasingDoubleKeyFrame.EasingFunction>
                                <SineEase EasingMode="EaseInOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                        <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="90">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <SineEase EasingMode="EaseInOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                        <EasingDoubleKeyFrame KeyTime="00:00:01" Value="0">
                            <EasingDoubleKeyFrame.EasingFunction>
                                <SineEase EasingMode="EaseInOut" />
                            </EasingDoubleKeyFrame.EasingFunction>
                        </EasingDoubleKeyFrame>
                    </DoubleAnimationUsingKeyFrames>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Canvas.Triggers>
</Canvas>

It's hard to see what it actually looks like from a still picture, but here goes:

13.jpg

Conclusion

I have just shown you some examples to get you started, but every application will be different and will need something different. So mess around a bit. If you followed the previous post, then you can put any one of these into the loader container.

Be sure to go to the demo page to see all the variations in action here. And view the original post on my blog here.

14.jpg

History

  • 20/2/11 - Original post.

License

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


Written By
Software Developer self employed
South Africa South Africa
I develop awesome Windows Phone/Windows 8 stuff, am a Nokia Developer Champion, I do Netduino electronics stuff, and blog a lot. I also occasionally do talks about development at Universities and conferences like TechEd. I run a small indie Windows Phone studio, currently working on an AppCampus-funded game.

Checkout my just-for-fun apps here: http://www.windowsphone.com/en-US/store/publishers?publisherId=RogueCode&appId=23d742d2-5b14-48a7-8e5f-b3b779537338
I also do Windows Phone (and Windows) development for clients, for example: http://www.windowsphone.com/en-za/store/app/dstv/a87feeed-a8dd-4bcb-8d47-15908340fdab

I am currently on hiatus from writing development articles for WPCentral.com.

My first book has just been published on home automation with a Netduino: http://www.amazon.co.uk/Netduino-Home-Automation-Projects-Cavanagh/dp/1849697825

Comments and Discussions

 
GeneralMy vote of 5 Pin
c.leftheris23-Feb-11 10:48
c.leftheris23-Feb-11 10:48 
GeneralRe: My vote of 5 Pin
Matt Cavanagh23-Feb-11 23:07
Matt Cavanagh23-Feb-11 23:07 
GeneralWorking with Gif images link is not working. Could you please give me the link Pin
ramuknavap22-Feb-11 16:44
ramuknavap22-Feb-11 16:44 
GeneralRe: Working with Gif images link is not working. Could you please give me the link Pin
Matt Cavanagh22-Feb-11 19:18
Matt Cavanagh22-Feb-11 19:18 

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.