Click here to Skip to main content
15,867,308 members
Articles / Database Development / SQL Server

Better WPF Circular Progress Bar

Rate me:
Please Sign up or sign in to vote.
4.89/5 (29 votes)
4 Jan 2010CPOL1 min read 198.1K   53   33
A while back I posted a blog post about a simple Circular Progress Bar that I did for WPF.

A while back I posted a blog post about a simple Circular Progress Bar that I did for WPF. The original post is right here : http://sachabarber.net/?p=429

It turns out that was not the best thing to do, as the old approach used a never ending animation, that was even running when the controls Visibility changed. I did notice this pretty quickly, when we profiled our app, and noticed this hot spot exactly where the progress bar was. So what we did to fix that is just remove the control when it should stop showing progress. Anyway that was the old way.

I am pleased to announce that I have a new improved Circular Progress Bar that no longer uses a never ending animation, in fact it is a lot simpler and just uses a DispatcherTimer and some elementary trigonometry, and it actually looks more like the style of progress bar we are all used to seeing on the web. Without further ado here is the code:

The xaml for the CircularProgressBar.xaml

C#
<UserControl x:Class="ThreadingComponent.CircularProgressBar"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="Auto" Width="Auto" Background="Transparent"
             IsVisibleChanged="HandleVisibleChanged">
    <Grid x:Name="LayoutRoot" Background="Transparent"
          ToolTip="Searching...."
          HorizontalAlignment="Center"
          VerticalAlignment="Center">
        <Canvas RenderTransformOrigin="0.5,0.5"
                HorizontalAlignment="Center"
             VerticalAlignment="Center" Width="120"
             Height="120" Loaded="HandleLoaded"
                Unloaded="HandleUnloaded"  >
            <Ellipse x:Name="C0" Width="20" Height="20"
                     Canvas.Left="0"
                     Canvas.Top="0" Stretch="Fill"
                     Fill="Black" Opacity="1.0"/>
            <Ellipse x:Name="C1" Width="20" Height="20"
                     Canvas.Left="0"
                     Canvas.Top="0" Stretch="Fill"
                     Fill="Black" Opacity="0.9"/>
            <Ellipse x:Name="C2" Width="20" Height="20"
                     Canvas.Left="0"
                     Canvas.Top="0" Stretch="Fill"
                     Fill="Black" Opacity="0.8"/>
            <Ellipse x:Name="C3" Width="20" Height="20"
                     Canvas.Left="0"
                     Canvas.Top="0" Stretch="Fill"
                     Fill="Black" Opacity="0.7"/>
            <Ellipse x:Name="C4" Width="20" Height="20"
                     Canvas.Left="0"
                     Canvas.Top="0" Stretch="Fill"
                     Fill="Black" Opacity="0.6"/>
            <Ellipse x:Name="C5" Width="20" Height="20"
                     Canvas.Left="0"
                     Canvas.Top="0" Stretch="Fill"
                     Fill="Black" Opacity="0.5"/>
            <Ellipse x:Name="C6" Width="20" Height="20"
                     Canvas.Left="0"
                     Canvas.Top="0" Stretch="Fill"
                     Fill="Black" Opacity="0.4"/>
            <Ellipse x:Name="C7" Width="20" Height="20"
                     Canvas.Left="0"
                     Canvas.Top="0" Stretch="Fill"
                     Fill="Black" Opacity="0.3"/>
            <Ellipse x:Name="C8" Width="20" Height="20"
                     Canvas.Left="0"
                     Canvas.Top="0" Stretch="Fill"
                     Fill="Black" Opacity="0.2"/>
            <Canvas.RenderTransform>
                <RotateTransform x:Name="SpinnerRotate"
                     Angle="0" />
            </Canvas.RenderTransform>
        </Canvas>
    </Grid>
</UserControl>

And here is the CircularProgressBar.xaml.cs

C#
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Windows.Input;
using System.Windows.Shapes;

namespace ThreadingComponent
{
    /// <summary>
    /// A circular type progress bar, that is simliar to popular web based
    /// progress bars
    /// </summary>
    public partial class CircularProgressBar
    {
        #region Data
        private readonly DispatcherTimer animationTimer;
        #endregion

        #region Constructor
        public CircularProgressBar()
        {
            InitializeComponent();

            animationTimer = new DispatcherTimer(
                DispatcherPriority.ContextIdle, Dispatcher);
            animationTimer.Interval = new TimeSpan(0, 0, 0, 0, 75);
        }
        #endregion

        #region Private Methods
        private void Start()
        {
            Mouse.OverrideCursor = Cursors.Wait;
            animationTimer.Tick += HandleAnimationTick;
            animationTimer.Start();
        }

        private void Stop()
        {
            animationTimer.Stop();
            Mouse.OverrideCursor = Cursors.Arrow;
            animationTimer.Tick -= HandleAnimationTick;
        }

        private void HandleAnimationTick(object sender, EventArgs e)
        {
            SpinnerRotate.Angle = (SpinnerRotate.Angle + 36) % 360;
        }

        private void HandleLoaded(object sender, RoutedEventArgs e)
        {
            const double offset = Math.PI;
            const double step = Math.PI * 2 / 10.0;

            SetPosition(C0, offset, 0.0, step);
            SetPosition(C1, offset, 1.0, step);
            SetPosition(C2, offset, 2.0, step);
            SetPosition(C3, offset, 3.0, step);
            SetPosition(C4, offset, 4.0, step);
            SetPosition(C5, offset, 5.0, step);
            SetPosition(C6, offset, 6.0, step);
            SetPosition(C7, offset, 7.0, step);
            SetPosition(C8, offset, 8.0, step);
        }

        private void SetPosition(Ellipse ellipse, double offset,
            double posOffSet, double step)
        {
            ellipse.SetValue(Canvas.LeftProperty, 50.0
                + Math.Sin(offset + posOffSet * step) * 50.0);

            ellipse.SetValue(Canvas.TopProperty, 50
                + Math.Cos(offset + posOffSet * step) * 50.0);
        }

        private void HandleUnloaded(object sender, RoutedEventArgs e)
        {
            Stop();
        }

        private void HandleVisibleChanged(object sender,
            DependencyPropertyChangedEventArgs e)
        {
            bool isVisible = (bool)e.NewValue;

            if (isVisible)
                Start();
            else
                Stop();
        }
        #endregion
    }
}

And to use it you can simply make it any size you like by putting it into a ViewBox like so:

C#
<UserControl x:Class="ThreadingComponent.BusyUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ThreadingComponent"
    Height="Auto" Width="Auto"
    HorizontalAlignment="Stretch"
    VerticalAlignment="Stretch">

        <Viewbox Width="200" Height="200"
                HorizontalAlignment="Center"
                VerticalAlignment="Center">
            <local:CircularProgressBar />
        </Viewbox>

    </Grid>

</UserControl>

And here is what it looks like when its running

All the code is here is a cut and pastable format, so no ZIP file this time, just cut and paste this code, if you don’t know how to do that, step away from the XAML.

This article was originally posted at http://sachabarber.net?p=639

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)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
GeneralMy vote of 5 Pin
Marty718-Dec-20 10:26
Marty718-Dec-20 10:26 
AnswerSee here for example execution Pin
brinko9919-Feb-18 13:50
brinko9919-Feb-18 13:50 
BugFixing Stutter Pin
Member 133253262-Aug-17 3:25
Member 133253262-Aug-17 3:25 
QuestionCircular Progress Bar image not shown Pin
Tridip Bhattacharjee9-May-17 23:53
professionalTridip Bhattacharjee9-May-17 23:53 
GeneralThe code for the code-behind has mostly disappeared Pin
SteveGreene25-Jul-15 19:44
SteveGreene25-Jul-15 19:44 
GeneralRe: The code for the code-behind has mostly disappeared Pin
edmolko15-Oct-15 0:23
edmolko15-Oct-15 0:23 
GeneralRe: The code for the code-behind has mostly disappeared Pin
A Korell2-Nov-15 6:19
A Korell2-Nov-15 6:19 
GeneralRe: The code for the code-behind has mostly disappeared Pin
VEMS2-Mar-16 12:08
VEMS2-Mar-16 12:08 
GeneralRe: The code for the code-behind has mostly disappeared Pin
yonicc30-Aug-16 22:43
yonicc30-Aug-16 22:43 
QuestionI made a no code behind version based on this. Pin
Fred Gras23-Jul-15 9:27
Fred Gras23-Jul-15 9:27 
GeneralThanks Pin
K.Vishwa24-Jun-15 6:06
K.Vishwa24-Jun-15 6:06 
BugError Pin
Member 1152561613-Jun-15 23:28
Member 1152561613-Jun-15 23:28 
QuestionError Pin
Member 1152561613-Jun-15 22:56
Member 1152561613-Jun-15 22:56 
QuestionHow to increase the size of the circle? Pin
Anishaaaaa5-Feb-15 3:00
Anishaaaaa5-Feb-15 3:00 
QuestionThank!!!!! Pin
JustMe4TheCodeProject4-Oct-14 22:21
JustMe4TheCodeProject4-Oct-14 22:21 
SuggestionRe-done to make it a WPF Custom control Pin
Member 79875048-Apr-14 10:39
Member 79875048-Apr-14 10:39 
GeneralRe: Re-done to make it a WPF Custom control Pin
Sacha Barber8-Apr-14 12:02
Sacha Barber8-Apr-14 12:02 
SuggestionViewbox optimization Pin
pr0gg3r12-Apr-13 2:42
pr0gg3r12-Apr-13 2:42 
QuestionCircular Progress bar not updating unless I move the window it is in Pin
coolestnerd30-Sep-11 2:29
coolestnerd30-Sep-11 2:29 
AnswerRe: Circular Progress bar not updating unless I move the window it is in Pin
coolestnerd30-Sep-11 3:21
coolestnerd30-Sep-11 3:21 
GeneralRe: Circular Progress bar not updating unless I move the window it is in Pin
Sacha Barber1-Oct-11 10:36
Sacha Barber1-Oct-11 10:36 
GeneralMy vote of 4 Pin
isaks10-Jul-11 0:47
isaks10-Jul-11 0:47 
Excellent sample! Thanks for sharing!
GeneralMy vote of 5 Pin
Tawani Anyangwe12-Mar-11 8:01
Tawani Anyangwe12-Mar-11 8:01 
GeneralMy vote of 5 Pin
radupoe23-Oct-10 9:15
radupoe23-Oct-10 9:15 
GeneralMouse.OverrideCursor Pin
EinarG8-Jun-10 2:23
EinarG8-Jun-10 2:23 

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.