<

Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x

Silverlight Circular Progress Control

Published on
14,453 Points
4,553 Views
4 Endorsements
Last Modified:
Awarded
For a while now I'v been searching for a circular progress control, much like the one you get when first starting your Silverlight application. I found a couple that were written in WPF and there were a few written in Silverlight, but all appeared overly complex, so I decided to write my own.

Design

So, to start with I added a Silverlight UserControl to my project and named it 'CircularProgressControl', and added a Canvas control to the Layout Grid. I then added 12 ellipses and arranged them in a circle around the canvas. I then set the Fill property to a RadialGradientBrush with 3 gradient stops and modified a gradient offset of each dot to appear slightly less intense as the previous. The result looked like this...

Dot Layout
To add a little flexability to the control and allow me to easily change the base color to something other than Blue, I added some property binding, which involved 3 things.

In the code behind of the UserControl, I added a property called DotColor....
    ''' <summary>
    ''' The base color for each of the dots on the progress control
    ''' </summary>
    Public Property DotColor As Color

Open in new window

I then added property binding to each of the dots in the control, so this....
            <Ellipse StrokeThickness="0" Width="15" Height="15" Canvas.Left="30" Canvas.Top="0" Name="Ellipse1">
                <Ellipse.Fill>
                    <RadialGradientBrush>
                        <GradientStop Color="Blue" Offset="0" />
                        <GradientStop Color="Transparent" Offset="1" />
                        <GradientStop Color="Blue" Offset="0.9" />
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>

Open in new window

...ended up like this....
            <Ellipse StrokeThickness="0" Width="15" Height="15" Canvas.Left="30" Canvas.Top="0" Name="Ellipse1">
                <Ellipse.Fill>
                    <RadialGradientBrush>
                        <GradientStop Color="{Binding DotColor}" Offset="0" />
                        <GradientStop Color="Transparent" Offset="1" />
                        <GradientStop Color="{Binding DotColor}" Offset="0.9" />
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>

Open in new window

I then needed to ensure the binding knew where to look, so I added this...
DataContext="{Binding RelativeSource={RelativeSource Self}}"

Open in new window

...in the XAML declaration. The final XAML looks like this....
<UserControl x:Class="TestProject.CircularProgressControl"
    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"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    mc:Ignorable="d"
    Visibility="Collapsed"
    d:DesignHeight="75" d:DesignWidth="75"
    DataContext="{Binding RelativeSource={RelativeSource Self}}">

    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Canvas Name="Body">
            <Ellipse StrokeThickness="0" Width="15" Height="15" Canvas.Left="30" Canvas.Top="0" Name="Ellipse1">
                <Ellipse.Fill>
                    <RadialGradientBrush>
                        <GradientStop Color="{Binding DotColor}" Offset="0" />
                        <GradientStop Color="Transparent" Offset="1" />
                        <GradientStop Color="{Binding DotColor}" Offset="0.9" />
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
            <Ellipse Canvas.Left="45" Canvas.Top="4" Height="15" StrokeThickness="0" Width="15" Name="Ellipse2">
                <Ellipse.Fill>
                    <RadialGradientBrush>
                        <GradientStop Color="{Binding DotColor}" Offset="0" />
                        <GradientStop Color="Transparent" Offset="1" />
                        <GradientStop Color="{Binding DotColor}" Offset="0.8" />
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
            <Ellipse Canvas.Left="56" Canvas.Top="15" Height="15" StrokeThickness="0" Width="15" Name="Ellipse3">
                <Ellipse.Fill>
                    <RadialGradientBrush>
                        <GradientStop Color="{Binding DotColor}" Offset="0" />
                        <GradientStop Color="Transparent" Offset="1" />
                        <GradientStop Color="{Binding DotColor}" Offset="0.7" />
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
            <Ellipse Canvas.Left="60" Canvas.Top="30" Height="15" StrokeThickness="0" Width="15" Name="Ellipse4">
                <Ellipse.Fill>
                    <RadialGradientBrush>
                        <GradientStop Color="{Binding DotColor}" Offset="0" />
                        <GradientStop Color="Transparent" Offset="1" />
                        <GradientStop Color="{Binding DotColor}" Offset="0.6" />
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
            <Ellipse Canvas.Left="56" Canvas.Top="44" Height="15" StrokeThickness="0" Width="15" Name="Ellipse5">
                <Ellipse.Fill>
                    <RadialGradientBrush>
                        <GradientStop Color="{Binding DotColor}" Offset="0" />
                        <GradientStop Color="Transparent" Offset="1" />
                        <GradientStop Color="{Binding DotColor}" Offset="0.5" />
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
            <Ellipse Canvas.Left="45" Canvas.Top="56" Height="15" StrokeThickness="0" Width="15" Name="Ellipse6">
                <Ellipse.Fill>
                    <RadialGradientBrush>
                        <GradientStop Color="{Binding DotColor}" Offset="0" />
                        <GradientStop Color="Transparent" Offset="1" />
                        <GradientStop Color="{Binding DotColor}" Offset="0.4" />
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
            <Ellipse Canvas.Left="30" Canvas.Top="60" Height="15" StrokeThickness="0" Width="15" Name="Ellipse7">
                <Ellipse.Fill>
                    <RadialGradientBrush>
                        <GradientStop Color="{Binding DotColor}" Offset="0" />
                        <GradientStop Color="Transparent" Offset="1" />
                        <GradientStop Color="{Binding DotColor}" Offset="0.3" />
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
            <Ellipse Canvas.Left="15" Canvas.Top="56" Height="15" StrokeThickness="0" Width="15" Name="Ellipse8">
                <Ellipse.Fill>
                    <RadialGradientBrush>
                        <GradientStop Color="{Binding DotColor}" Offset="0" />
                        <GradientStop Color="Transparent" Offset="1" />
                        <GradientStop Color="{Binding DotColor}" Offset="0.2" />
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
            <Ellipse Canvas.Left="4" Canvas.Top="45" Height="15" StrokeThickness="0" Width="15" Name="Ellipse9">
                <Ellipse.Fill>
                    <RadialGradientBrush>
                        <GradientStop Color="{Binding DotColor}" Offset="0" />
                        <GradientStop Color="Transparent" Offset="1" />
                        <GradientStop Color="{Binding DotColor}" Offset="0.1" />
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
            <Ellipse Canvas.Left="0" Canvas.Top="30" Height="15" StrokeThickness="0" Width="15" Name="Ellipse10">
                <Ellipse.Fill>
                    <RadialGradientBrush>
                        <GradientStop Color="{Binding DotColor}" Offset="0" />
                        <GradientStop Color="Transparent" Offset="1" />
                        <GradientStop Color="{Binding DotColor}" Offset="0.05" />
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
            <Ellipse Canvas.Left="4" Canvas.Top="15" Height="15" StrokeThickness="0" Width="15" Name="Ellipse11">
                <Ellipse.Fill>
                    <RadialGradientBrush>
                        <GradientStop Color="{Binding DotColor}" Offset="0" />
                        <GradientStop Color="Transparent" Offset="1" />
                        <GradientStop Color="{Binding DotColor}" Offset="0.03" />
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
            <Ellipse Canvas.Left="15" Canvas.Top="4" Height="15" StrokeThickness="0" Width="15" Name="Ellipse12">
                <Ellipse.Fill>
                    <RadialGradientBrush>
                        <GradientStop Color="{Binding DotColor}" Offset="0" />
                        <GradientStop Color="Transparent" Offset="1" />
                        <GradientStop Color="{Binding DotColor}" Offset="0.01" />
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
        </Canvas>
    </Grid>
</UserControl>

Open in new window

Note "x:Class" in the XAML declaration. My project name when designing this control was called "TestProject", so for it to work in your project, change "TestProject" to the Namespace of your own project.

Animation

Now for the animation. For this I decided to use a StoryBoard. I could also have used a StoryBoard within the XAML, but decided for the programatic route so I could stop and start the animation easily.

The code to run the animation is pretty straight-forward. Here it is in it's entirety...
 
Imports System.Windows.Threading

Partial Public Class CircularProgressControl
    Inherits UserControl

    Private WithEvents _storyBoard As Storyboard

    Public Sub New()

        InitializeComponent()

        'initialize the StoryBoard object
        _storyBoard = New Storyboard
        _storyBoard.Duration = TimeSpan.FromMilliseconds(100)

    End Sub

    ''' <summary>
    ''' The base color for each of the dots on the progress control
    ''' </summary>
    Public Property DotColor As Color

    Public Sub StartProgress()
        Me.Visibility = Windows.Visibility.Visible
        _storyBoard.Begin()
    End Sub

    Public Sub StopProgress()
        _storyBoard.Stop()
        Me.Visibility = Windows.Visibility.Collapsed
    End Sub

    Private Sub _storyBoard_Completed(ByVal sender As Object, ByVal e As System.EventArgs) Handles _storyBoard.Completed
        'move the fill properties anti-clockwise
        Ellipse12.Fill = Ellipse1.Fill
        Ellipse1.Fill = Ellipse2.Fill
        Ellipse2.Fill = Ellipse3.Fill
        Ellipse3.Fill = Ellipse4.Fill
        Ellipse4.Fill = Ellipse5.Fill
        Ellipse5.Fill = Ellipse6.Fill
        Ellipse6.Fill = Ellipse7.Fill
        Ellipse7.Fill = Ellipse8.Fill
        Ellipse8.Fill = Ellipse9.Fill
        Ellipse9.Fill = Ellipse10.Fill
        Ellipse10.Fill = Ellipse11.Fill
        Ellipse11.Fill = Ellipse12.Fill
        _storyBoard.Begin()
    End Sub
End Class

Open in new window

You can see straight away how I made the dots appear to move. I simply moved the fill property of each dot to the next one!

Starting and Stopping the progress updating is fairly straight forward as well. I exposed to public methods called "StartProgres" and "StopProgress", and in each I started or stopped the StoryBoard object. I also modified the Visibility of the UserControl to hide it once the progress has ended. If you look back up to the XAML, I have also set the UserControl's visibility to Collapsed so it is only displayed once the progress updating is underway.

Usage

Using the CircularProgressControl in your project is very easy. Once you've built your project, you will now see the control displayed in the Toolbox. It's simply a matter of adding it to your page, control, or wherever you wish to use it.

The XAML for the control will look something like this....
<my:CircularProgressControl x:Name="CircularProgressControl1" DotColor="Blue" />

Open in new window

Note the "DotColor" property. You can change that to any color you want.

Starting or stopping the progress is as easy as calling either of the "StartProgress" or "StopProgress" methods...
'start the progress
CircularProgressControl1.StartProgress()

'stop the progress
CircularProgressControl1.StopProgress()

Open in new window

4
Comment
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
3 Comments
 
LVL 38

Expert Comment

by:younghv
Wayne - nice work.
"Yes" vote above.

Vic
0
 
LVL 2

Expert Comment

by:baskar_ram
Nice control... The BusyIndicator control in the below url also so good..
http://www.silverlight.net/content/samples/sl4/toolkitcontrolsamples/run/default.html
0
 
LVL 48

Author Comment

by:Wayne Taylor (webtubbs)
The BusyIndicator is good, and that was what I settled with for a while. But, it's not circular :)
0

Featured Post

Free Backup Tool for VMware and Hyper-V

Restore full virtual machine or individual guest files from 19 common file systems directly from the backup file. Schedule VM backups with PowerShell scripts. Set desired time, lean back and let the script to notify you via email upon completion.  

Join & Write a Comment

This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Video by: ITPro.TV
In this episode Don builds upon the troubleshooting techniques by demonstrating how to properly monitor a vSphere deployment to detect problems before they occur. He begins the show using tools found within the vSphere suite as ends the show demonst…

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month