Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Circular Progress Indicator

4.84/5 (85 votes)
1 Jul 2010CPOL5 min read 323.9K   16.3K  
Firefox like circular progress indicator
Image 1

Introduction

I've always liked the little circular progress indicator on Firefox top right corner when loading a page but I couldn't find any like it to use with my projects, so I've made one myself.

Using the Code

Using the control is very simple, there are only four properties and two methods that you need to be aware of:

  • CircleColor - Changes the base color of the circles
  • CircleSize - Changes the diameter of the circles, this value is relative and proportional to the control size and ranges between 0.1 and 1.0
  • AutoStart - Sets if the animation should start automatically
  • AnimationSpeed - Sets the animation speed 
  • NumberOfCircles - Sets the maximum number of circles that can be drawn in the control, this value must be greater than 0
  • NumberOfVisibleCircles - Sets the number of circles that are visible in the control, must always be less or equal than the NumberOfCircles
  • Percentage - Sets a percentage value that can be shown in the center of the control, this value must be between 0 and 100
  • ShowPercentage - Sets a value that allows to show or hide the percentage value
  • ShowText - Sets a value that allows to show the control text
  • TextDisplay - This property is just a helper property, it allows to enable or disable both text and percentage with one property
  • Rotation - Sets the direction in which the control will rotate

To start the animation, just call Start():

C#
progressIndicator.Start(); 

And to stop it, just call Stop():

C#
progresIndicator.Stop();

The Control Code

Drawing the Circles

The circles are drawn around a circumference with the diameter of the control. Since the number of circles is variable, we need to calculate the rotation angle for each circle. The minimum number of circles must be 1 with no upper limit control. Drawing the circles is done using a call to RotateTransform that rotates the drawing position to the angle passed as a parameter. Drawing the circles in sequence, decreasing the amount of alpha in the circle color gives the fading effect of the control. To animate, the control is redrawn at time intervals always changing the position of the darker circle by one place forward, giving the illusion of motion. The control can be rotated clockwise or counter-clockwise and direction of rotation is achieved by multiplying the angle by 1 or -1. Checking the code, this line is found:

C#
e.Graphics.RotateTransform(angle * _value * (int)_rotation);

This piece of code sets the start position of each rotation phase multiplying the section angle by a value that is incremented by an internal timer. The maximum number of circles shown at a given time is defined by the NumberOfVisibleCircles property. This property determines the effective number of circles that are drawn in the control, this is different from the NumberOfCircles property. This last property sets the number of circles that can be drawn, is like reserving spaces for the circles even if we don't use them all. This gives the effect of tightening the circles together when the number of visible circles decreases and the opposite effect when it increases.

C#
float angle = 360.0F / _numberOfCircles;

GraphicsState oldState = e.Graphics.Save();

e.Graphics.TranslateTransform(Width / 2.0F, Height / 2.0F);
e.Graphics.RotateTransform(angle * _value * (int)_rotation);
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

for (int i = 1; i <= _numberOfCircles; i++)
{
    int alphaValue = (255.0F * (i / (float)_numberOfVisibleCircles)) > 255.0 ? 0 : 
	(int)(255.0F * (i / (float)_numberOfVisibleCircles));
    int alpha = _stopped ? (int)(255.0F * (1.0F / 8.0F)) : alphaValue;

    Color drawColor = Color.FromArgb(alpha, _circleColor);

    using (SolidBrush brush = new SolidBrush(drawColor))
    {
        float sizeRate = 4.5F / _circleSize;
        float size = Width / sizeRate;

        float diff = (Width / 4.5F) - size;

        float x = (Width / 9.0F) + diff;
        float y = (Height / 9.0F) + diff;
        e.Graphics.FillEllipse(brush, x, y, size, size);
        e.Graphics.RotateTransform(angle * (int)_rotation);
    }
}

e.Graphics.Restore(oldState);

Drawing the Percentage and the Text

The percentage is an optional feature, since the primary purpose isn't to show a progress value but that something is going on. The percentage text isn't shown by default and wasn't even part of the original idea. Anyway I have been reading some comments from people requesting new features and giving some nice ideas that I've decided to update the control and implement some new features. One of them was this one. The percentage is set through the Percentage property and can be enabled and disabled from the ShowPercentage property. The Text property follows the same reasoning. It wasn't part of the original plan, but as SohjSolwin suggested, it can be useful in some cases, we never know when or where this control will be used. So, from this version on, the control has the ability of drawing both of them like shown below:

C#
string percent = GetDrawText();

if (!string.IsNullOrEmpty(percent))
{
    SizeF textSize = e.Graphics.MeasureString(percent, Font);
    float textX = (Width / 2.0F) - (textSize.Width / 2.0F);
    float textY = (Height / 2.0F) - (textSize.Height / 2.0F);
    StringFormat format = new StringFormat
    {
        Alignment = StringAlignment.Center,
        LineAlignment = StringAlignment.Center
    };

    RectangleF rectangle = new RectangleF(textX, textY, textSize.Width, textSize.Height);
        

    using (SolidBrush textBrush = new SolidBrush(ForeColor))
    {
        e.Graphics.DrawString(percent, Font, textBrush, rectangle, format);
    } 
}

Why both Percentage and the Text?

When the control is being used to show progress, it is possible to show a message about what is going on and at the same time the progress percentage. Both of them are independent and have different purposes. Even if they are drawn at the same time internally, if I just need to change the value of the percentage, it's easier to do this:

C#
progressIndicator.Percentage = 50.25F;

And let the control add the "%" sign and do the rest of the formatting. It's much more intuitive and simple than this:

C#
progressIndicator.Text = string.Format("{0:0:##} %}, 50.25F);

On the other hand, the percentage property doesn't allow text, so it's needs another field to set the text to draw. The final string that is displayed is built using the Text and the Percentage property in the method displayed below, the method checks the values of the _showText and _showPercentage fields and constructs a string based on their values, returning an empty string if both fields are set to false.

C#
private string GetDrawText()
{
    string percent = string.Format(CultureInfo.CurrentCulture, "{0:0.##} %", _percentage);

    if (_showText && _showPercentage)
        return string.Format("{0}{1}{2}", percent, Environment.NewLine, Text);

    if (_showText)
        return Text;

    if (_showPercentage)
        return percent;

    return string.Empty;
}

The rest of the control code is short and simple to understand, so I've decided to remove it from the article leaving only the pieces that matter. If you have any questions, please leave a comment.

Points of Interest

There's nothing more to it. It is particularly useful in situations where the progress bar hasn't made any progress for a long time and we want to pass the message that something is being done. Mainly, it has been fun to program. :)

Credits

I would like to thank SohjSolwin for his contribution with the rotation and number of visible circles part of the code that I've included in this release with minor modifications, and also for the text drawing suggestion instead of only showing the percentage.

History

  • 2nd November, 2008: Initial post
  • 26th June, 2010: Update to version 1.1, new features are:
    • Variable number of circles (version 1 has a fixed number of 8)
    • It is possible to draw a percentage value
  • 1st July, 2010: Update to version 1.2, new features are:
    • Rotation can be clockwise or counter-clockwise
    • It is possible to draw a text string in addition to the percentage value
    • Variable number of visible circles (version 1.1 has a fixed number of 8)

License

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