WPF : Circular Progress Bar
Ever wanted a slightly different progress bar from the standard WPF Progress bar which looks like the following (IsIndeterminate is like the WinForms Marquee enum value) :
1: <ProgressBar VerticalAlignment="Top"
2: Height="22" IsIndeterminate="True"/>
What I would like is one that is more like the ones you see all over the web, where we have a round wheel sort of progress indicator. Would you like one of those?
Well fear not your search is over, here is a very simple idea, just arrange some Ellipses in a circle within a Canvas and do a constant Rotate StoryBoard and bam, a circular progress bar.
Here is the XAML for such a UserControl.
1: <UserControl x:Class="Sonic.CircularProgressBar"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: Height="120" Width="120" Background="Transparent">
5: <Grid x:Name="LayoutRoot" Background="Transparent"
6: HorizontalAlignment="Center" VerticalAlignment="Center">
7: <Grid.RenderTransform>
8: <ScaleTransform x:Name="SpinnerScale"
9: ScaleX="1.0" ScaleY="1.0" />
10: </Grid.RenderTransform>
11: <Canvas RenderTransformOrigin="0.5,0.5"
12: HorizontalAlignment="Center"
13: VerticalAlignment="Center"
14: Width="120" Height="120" >
15: <Ellipse Width="21.835" Height="21.862"
16: Canvas.Left="20.1696"
17: Canvas.Top="9.76358"
18: Stretch="Fill" Fill="Orange"
19: Opacity="1.0"/>
20: <Ellipse Width="21.835" Height="21.862"
21: Canvas.Left="2.86816"
22: Canvas.Top="29.9581" Stretch="Fill"
23: Fill="Black" Opacity="0.9"/>
24: <Ellipse Width="21.835" Height="21.862"
25: Canvas.Left="5.03758e-006"
26: Canvas.Top="57.9341" Stretch="Fill"
27: Fill="Black" Opacity="0.8"/>
28: <Ellipse Width="21.835" Height="21.862"
29: Canvas.Left="12.1203"
30: Canvas.Top="83.3163" Stretch="Fill"
31: Fill="Black" Opacity="0.7"/>
32: <Ellipse Width="21.835" Height="21.862"
33: Canvas.Left="36.5459"
34: Canvas.Top="98.138" Stretch="Fill"
35: Fill="Black" Opacity="0.6"/>
36: <Ellipse Width="21.835" Height="21.862"
37: Canvas.Left="64.6723"
38: Canvas.Top="96.8411" Stretch="Fill"
39: Fill="Black" Opacity="0.5"/>
40: <Ellipse Width="21.835" Height="21.862"
41: Canvas.Left="87.6176"
42: Canvas.Top="81.2783" Stretch="Fill"
43: Fill="Black" Opacity="0.4"/>
44: <Ellipse Width="21.835" Height="21.862"
45: Canvas.Left="98.165"
46: Canvas.Top="54.414" Stretch="Fill"
47: Fill="Black" Opacity="0.3"/>
48: <Ellipse Width="21.835" Height="21.862"
49: Canvas.Left="92.9838"
50: Canvas.Top="26.9938" Stretch="Fill"
51: Fill="Black" Opacity="0.2"/>
52: <Ellipse Width="21.835" Height="21.862"
53: Canvas.Left="47.2783"
54: Canvas.Top="0.5" Stretch="Fill"
55: Fill="Black" Opacity="0.1"/>
56: <Canvas.RenderTransform>
57: <RotateTransform x:Name="SpinnerRotate"
58: Angle="0" />
59: </Canvas.RenderTransform>
60: <Canvas.Triggers>
61: <EventTrigger RoutedEvent="ContentControl.Loaded">
62: <BeginStoryboard>
63: <Storyboard>
64: <DoubleAnimation
65: Storyboard.TargetName
66: ="SpinnerRotate"
67: Storyboard.TargetProperty
68: ="(RotateTransform.Angle)"
69: From="0" To="360"
70: Duration="0:0:01"
71: RepeatBehavior="Forever" />
72: </Storyboard>
73: </BeginStoryboard>
74: </EventTrigger>
75: </Canvas.Triggers>
76: </Canvas>
77: </Grid>
78: </UserControl>
And here is all the C# codebehind
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Windows;
6: using System.Windows.Controls;
7: using System.Windows.Data;
8: using System.Windows.Documents;
9: using System.Windows.Input;
10: using System.Windows.Media;
11: using System.Windows.Media.Animation;
12: using System.Windows.Media.Imaging;
13: using System.Windows.Navigation;
14: using System.Windows.Shapes;
15:
16: namespace Sonic
17: {
18: /// <summary>
19: /// Provides a circular progress bar
20: /// </summary>
21: public partial class CircularProgressBar : UserControl
22: {
23: public CircularProgressBar()
24: {
25: InitializeComponent();
26:
27: //Use a default Animation Framerate of 20, which uses less CPU time
28: //than the standard 50 which you get out of the box
29: Timeline.DesiredFrameRateProperty.OverrideMetadata(
30: typeof(Timeline),
31: new FrameworkPropertyMetadata { DefaultValue = 20 } );
32: }
33: }
34: }
And here is an example of one of these CircularProgressBar controls in use:
1: <Window
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:local="clr-namespace:Sonic"
5: x:Class="Sonic.MainWindow">
6:
7:
8: <Grid>
9: <local:MediaView/>
10: </Grid>
11:
12: </Window>
And here it is in action (obviously it looks better in run time)….Enjoy
And for those that are interested here is a little demo project, progress.zip


























Josh Smith said
am February 3 2009 @ 8:55 pm
Sweeeet!
Karl Shifflett said
am February 3 2009 @ 10:08 pm
You are the Man!
Walt Ritscher said
am February 4 2009 @ 12:14 am
Another nice and easy article. Keep ‘em coming.
Eric Burke said
am February 4 2009 @ 1:02 am
Nice, Sacha!
One thing you might consider doing is lowering the DesiredFrameRate on the Storyboard to something that is still acceptable in terms of smoothness. This will go a long way in saving you CPU load, which directly translates into power savings.
sacha said
am February 4 2009 @ 8:42 am
Eric thanks for that idea, Ill look into this later.
Pete O'Hanlon said
am February 4 2009 @ 10:07 am
Very nice there sir. Such a simple idea, and yet so cool.
sacha said
am February 4 2009 @ 10:39 am
Thanks Guys, A am just going to do the Framerate metadata override as suggested by Eric tonight. Its so easy though.
Dew Drop - February 4, 2009 | Alvin Ashcraft's Morning Dew said
am February 4 2009 @ 4:59 pm
[...] WPF : Circular Progress Bar (Sacha Barber) [...]
Raul Mainardi Neto said
am February 4 2009 @ 8:23 pm
Simple, yet fantastic… as usual… keep up the excelent work. and thanks for the aid with my project =D cheers mate
sacha said
am February 4 2009 @ 8:28 pm
Thanks Raul
Martin C said
am February 4 2009 @ 11:57 pm
For other WPF newbies (like myself), don’t forget to define the “local” namespace. It’s just one extra attribute in the Window element in the XAML using the control:
xmlns:local=”clr-namespace:Sonic”
Brian Wang said
am February 11 2009 @ 2:44 am
Excellent!
BTW, I want to ask you a question about the article in
http://www.codeproject.com/KB/WPF/Ink1.aspx?display=PrintAll&f&df=90&mpp=25&noise=3&sort=Position&view=Quick&fr=26&select=2073180#SaveAsISF
since the article was posted years before, I’m not sure if anything question posted following that page would be viewed by you, so I write here.
my question is when save the ink as bmp file, why there are black black strips on the top and left sides? how to remove them?
thanks a lot!
sacha said
am February 11 2009 @ 8:44 am
Brian
I know the article you mean, and that doesn’t happen on my PC. Sorry to inform you.
Thomas Freudenberg said
am February 24 2009 @ 10:30 am
Great work. However, it would be awesome if the control would be resizable. Currently it has a fixed size of 120×120, it should seamlessly adopt the available space (and support some kind of DesiredSize)
sacha said
am February 24 2009 @ 10:38 am
Thomas
This is easily fixedjust wrap it in a ViewBox where you need to use it and give the ViewBox a Width and Height.
Bingo, job done.
Paley said
am February 26 2009 @ 2:49 pm
Am I being daft or is there a way to get the source code without the line numbers?
sacha said
am February 27 2009 @ 9:28 am
Doh, sorry I didnt post the source for this, ill fix this after this weekend ok.
Martin said
am March 2 2009 @ 2:19 pm
I’m just getting started on WPF. I’m having trouble incorporating this into an example WPF app that I’ve made. What steps do I need to take to get it to work in another VS solution?
Any help is greatly appreciated.
sacha said
am March 2 2009 @ 3:41 pm
Ill post some demo project very soon.
sacha said
am March 2 2009 @ 9:21 pm
Martin, there is now a small demo project. Enjoy
kdawg said
am April 5 2009 @ 3:09 pm
Dude, You amaze me. Splendid.
sacha said
am April 5 2009 @ 3:11 pm
Kdawg the simple ones are often the best.
Aarthy said
am April 9 2009 @ 12:26 pm
The progress bar is really cool. But I’m having some problem running it in the background. The circular bar stops rotating whenever some work is being done, even if I use Background Worker or Dispatcher. Please give a demo of how to use it in the background.
E.g. This is what I want to do.
OnButtonClick()
{
//show the progress bar
perform some long operation
//hide progress bar
}
Although the progress bar is shown, it stops rotating once it enters the long operation.
How to rectify that?
sacha said
am April 9 2009 @ 12:31 pm
We are using it running in the background and it works fine.
We are using something like
Dispatcher.BgeingInvoke((Action) delegate {
//show progress
//do work
//hide progress
}, DispatcherPriority.Background);
Aarthy said
am April 9 2009 @ 12:53 pm
If you don’t mind, could you mail me a small demo of the application where you have used it in the background? Because I have already tried what you have suggested and it doesn’t work.
ivana said
am June 9 2009 @ 2:11 pm
I am using it and it works just fine, but i have this same problem:
OnButtonClick()
{
//show the progress bar
perform some long operation
//hide progress bar
}
Although the progress bar is shown, it stops rotating once it enters the long operation.
sacha said
am June 9 2009 @ 2:22 pm
We are using it running in the background and it works fine.
We are using something like
Dispatcher.BgeingInvoke((Action) delegate {
//show progress
//do work
//hide progress
}, DispatcherPriority.Background);
This works for us
ivana said
am June 9 2009 @ 2:34 pm
But i have this:
When i define it in xaml I put:
so that i don´t see it when i start.
Then, when i click on a combobox, y load some things and:
private void cboServidores_MouseEnter(object sender, MouseEventArgs e)
{
if (tablaServidores.Rows.Count == 0)
{
barra.Visibility=Visibility.Visible;
loadThings();
barra.Visibility=Visibility.Hidden;
}
if I use:
Dispatcher.BgeingInvoke((Action) delegate {
barra.Visibility=Visibility.Visible;
loadThings();
barra.Visibility=Visibility.Hidden;
}, DispatcherPriority.Background);
it shows me the progress circile after the loadThings is finished
ANY HELP!!!
THANKS
ivana said
am June 10 2009 @ 5:55 pm
any ideas?
Krunal said
am July 7 2009 @ 1:58 am
Sacha,
Any ideas on how to use this circular progress bar as animated cursor?
Robert said
am August 18 2009 @ 7:35 am
I like it! Thanks!
sacha said
am August 18 2009 @ 8:44 am
Robert
Thanks. One thing to note we use this at work, and its good, but when you no longer need to the progress bar, you need to remove it from any container, as the animation never stops, so its eats CPU even if not visible, at least it did for our app at work.
Steven Karan said
am January 12 2010 @ 3:12 pm
Ivana you have to set the UserControl Visibility to Visible outside the Thread Code and after Finishing processing, then the thread tells the dispatcher to set Visibility as Hidden.
What I did, I set the UserControls Visibility like:
I used the class ProgressVisibility:
public class ProgressVisibility : Binding, IValueConverter
{
public ProgressVisibility()
: base()
{
this.Converter = this;
}
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((bool)value)
{
return Visibility.Collapsed;
}
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
sacha said
am January 12 2010 @ 3:16 pm
You could use my new more improved progress wheel http://sachabarber.net/?p=639
Steven Karan said
am January 13 2010 @ 6:20 am
That’s awesome! Thanks for both…