Free Silverlight and WPF loading spinners

Here’s some free spinners I’ve been making with Blend and Illustrator that you can use as indeterminate loaders for Silverlight or any WPF app, all that needs changing to suit is the storyboard trigger. Enjoy!

Click to see sample XAML code of DotCircle:
<UserControl
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	mc:Ignorable="d"
	xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:im="clr-namespace:Microsoft.Expression.Interactivity.Media;assembly=Microsoft.Expression.Interactions"
	x:Class="xamlSpinners.ucSpinner_1">

	<UserControl.Resources>
		<Storyboard x:Name="spinner" RepeatBehavior="ForEver" BeginTime="00:00:00">
			<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse01" Storyboard.TargetProperty="(UIElement.Opacity)">
				<EasingDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0.5"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.2000000" Value="0.25"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
			</DoubleAnimationUsingKeyFrames>
			<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse12" Storyboard.TargetProperty="(UIElement.Opacity)">
				<EasingDoubleKeyFrame KeyTime="00:00:00" Value="0.5"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0.25"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.2000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:01" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:01.1000000" Value="1"/>
			</DoubleAnimationUsingKeyFrames>
			<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse11" Storyboard.TargetProperty="(UIElement.Opacity)">
				<EasingDoubleKeyFrame KeyTime="00:00:00" Value="0.25"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.9000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:01" Value="1"/>
				<EasingDoubleKeyFrame KeyTime="00:00:01.1000000" Value="0.5"/>
			</DoubleAnimationUsingKeyFrames>
			<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse02" Storyboard.TargetProperty="(UIElement.Opacity)">
				<EasingDoubleKeyFrame KeyTime="00:00:00.1000000" Value="1"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.2000000" Value="0.5"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.25"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0"/>
			</DoubleAnimationUsingKeyFrames>
			<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse03" Storyboard.TargetProperty="(UIElement.Opacity)">
				<EasingDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.2000000" Value="1"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0.5"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0.25"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.5000000" Value="0"/>
			</DoubleAnimationUsingKeyFrames>
			<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse04" Storyboard.TargetProperty="(UIElement.Opacity)">
				<EasingDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.2000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0.5"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.5000000" Value="0.25"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.6000000" Value="0"/>
			</DoubleAnimationUsingKeyFrames>
			<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse05" Storyboard.TargetProperty="(UIElement.Opacity)">
				<EasingDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.4000000" Value="1"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.5000000" Value="0.5"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.6000000" Value="0.25"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.7000000" Value="0"/>
			</DoubleAnimationUsingKeyFrames>
			<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse06" Storyboard.TargetProperty="(UIElement.Opacity)">
				<EasingDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.5000000" Value="1"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.6000000" Value="0.5"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.7000000" Value="0.25"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.8000000" Value="0"/>
			</DoubleAnimationUsingKeyFrames>
			<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse07" Storyboard.TargetProperty="(UIElement.Opacity)">
				<EasingDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.5000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.6000000" Value="1"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.7000000" Value="0.5"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.8000000" Value="0.25"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.9000000" Value="0"/>
			</DoubleAnimationUsingKeyFrames>
			<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse08" Storyboard.TargetProperty="(UIElement.Opacity)">
				<EasingDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.6000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.7000000" Value="1"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.8000000" Value="0.5"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.9000000" Value="0.25"/>
				<EasingDoubleKeyFrame KeyTime="00:00:01" Value="0"/>
			</DoubleAnimationUsingKeyFrames>
			<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse09" Storyboard.TargetProperty="(UIElement.Opacity)">
				<EasingDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.7000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.8000000" Value="1"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.9000000" Value="0.5"/>
				<EasingDoubleKeyFrame KeyTime="00:00:01" Value="0.25"/>
				<EasingDoubleKeyFrame KeyTime="00:00:01.1000000" Value="0"/>
			</DoubleAnimationUsingKeyFrames>
			<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse10" Storyboard.TargetProperty="(UIElement.Opacity)">
				<EasingDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.8000000" Value="0"/>
				<EasingDoubleKeyFrame KeyTime="00:00:00.9000000" Value="1"/>
				<EasingDoubleKeyFrame KeyTime="00:00:01" Value="0.5"/>
				<EasingDoubleKeyFrame KeyTime="00:00:01.1000000" Value="0.25"/>
			</DoubleAnimationUsingKeyFrames>
		</Storyboard>
	</UserControl.Resources>

	<Canvas x:Name="spinner_1" HorizontalAlignment="Center" Width="24" Height="24">
		<i:Interaction.Triggers>
			<i:EventTrigger EventName="Loaded">
				<im:ControlStoryboardAction Storyboard="{StaticResource spinner}"/>
			</i:EventTrigger>
		</i:Interaction.Triggers>
		<Ellipse x:Name="ellipse01" Fill="White" Height="4" Width="4" Canvas.Top="10" Opacity="0" />
		<Ellipse x:Name="ellipse02" Fill="White" Height="4" Width="4" Canvas.Top="5" Opacity="0" Canvas.Left="1.33975" />
		<Ellipse x:Name="ellipse03" Fill="White" Height="4" Width="4" Canvas.Top="1.4"  Opacity="0" Canvas.Left="5"/>
		<Ellipse x:Name="ellipse04" Fill="White" Height="4" Width="4" Canvas.Top="0"  Opacity="0" Canvas.Left="10"/>
		<Ellipse x:Name="ellipse05" Fill="White" Height="4" Width="4" Canvas.Top="1.4"  Opacity="0" Canvas.Left="15"/>
		<Ellipse x:Name="ellipse06" Fill="White" Height="4" Width="4" Canvas.Top="5"  Opacity="0" Canvas.Left="18.6603"/>
		<Ellipse x:Name="ellipse07" Fill="White" Height="4" Width="4" Canvas.Top="10" Canvas.Left="20" Opacity="0" />
		<Ellipse x:Name="ellipse08" Fill="White" Height="4" Width="4" Canvas.Top="15" Canvas.Left="18.7" Opacity="0" />
		<Ellipse x:Name="ellipse09" Fill="White" Height="4" Width="4" Canvas.Top="18.66025" Canvas.Left="15" Opacity="0"/>
		<Ellipse x:Name="ellipse10" Fill="White" Height="4" Width="4" Canvas.Top="20" Canvas.Left="10" Opacity="0"/>
		<Ellipse x:Name="ellipse11" Fill="White" Height="4" Width="4" Canvas.Top="18.66025" Canvas.Left="5" Opacity="0"/>
		<Ellipse x:Name="ellipse12" Fill="White" Height="4" Width="4" Canvas.Top="15" Canvas.Left="1.4" Opacity="0"/>

		<Grid x:Name="LayoutRoot" Height="24" Width="24"/>
	</Canvas>
</UserControl>


Update

Several people have requested a WPF version, so I created a WPF project and added it below :)

Download files:

19 Responses to “Free Silverlight and WPF loading spinners”

  1. Wow, this is really awesome. I particularly like the spinning gears, and the flipping squares? Nice job!

  2. [...] Also as a related point, with this release we've update all the build-in container templates, they now match the Silverlight's standard way of templating content controls. Another changes is that the template now shows an overlay with an animated indicator whilst navigating (it uses a spinning indicator by Felix Corke). [...]

  3. Excellent work, can I provide a WPF version? Also if you group the canvas into a ViewBox you can size the spinners.

    Thanks for sharing.

  4. WPF project added. Also Normski, rather than using a viewbox, I’d recommend using a scale transform, it’s more efficient. Cheers!

  5. Felix
    Thanks for the WPF version and the Transform scaling tip.

  6. Cool ….
    I love it….

  7. Thanks!!!

  8. Why does the lower cog seem to skip a beat every rotation?

  9. These are awesome! Easily customizable from other wpf spinners I’ve seen. Great job – easy to integrate.

  10. They are indeed very good.

    Is there an easy way to scale them bigger?

    I need one that is about 4 or 5 times bigger.

  11. @glen – sure, just use a scale transform on the usercontrol



  12. Awesome stuff. Just what I was looking for.

    However one thing, the .cs files are not in the zip for the silverlight project ? I get an error when trying to compile in VS2010.

  13. Is there any way I can change the color of the user controls? Like the white Apple spinner to black.

  14. Fantastic! I extended it slightly by allowing customisable colours. Others wishing to do the same can simply replace Fill=”White” in the XAML with Fill=”{Binding Path=SpinColor, ElementName=UserControl}”
    (Or whatever x:Name you have for the control)

    And in the code behind add:
    public Brush SpinColor
    {
    get { return (Brush)this.GetValue(SpinColorProperty); }
    set { this.SetValue(SpinColorProperty, value); }
    }

    public static readonly DependencyProperty SpinColorProperty =
    DependencyProperty.RegisterAttached(“SpinColor”, typeof(Brush),
    typeof(AppleSpinner),
    new UIPropertyMetadata(Brushes.White));

  15. hello
    thank you for this
    how can i use spinner into a button in silverlight ?

  16. Hi Nafiseh, in blend, select the spinner and go to Tools > Make new control > Button :)

  17. As Beej commented, the gear animation seems to skip a beat or jump just a bit in the animation. The same is true for the animation to the left and right of the gears. Once you first start noticing it, you can’t ignore it. So I had to figure out why it looked like that. It turns out it’s really simple to fix. You just need to extend the animation with one more step at the end and change the properties of the animated parts to they match the very first frame.
    The reason the animation jumps is because there is no interval when the animation restarts, which means there is no interval between the last frame and the first frame. So there is no transition happening between the end state and the start state, and that makes it ‘jump’.

  18. That’s really useful stuff. Many thanks for sharing!

    @Robin Anderson – I got around it by binding to Foreground property, no need for new one.

  19. Very nice job, work like a charm. thanks

Leave a Reply