Click here to Skip to main content
Email Password   helpLost your password?

Sample Image - mrg_loadingcircle.jpg

Introduction

When it's time to wait, we are used to seeing the classic blue progress bar. It is everywhere in Windows and many other applications. However, animations are getting more and more popular.

For example, when Firefox loads a page, a small spinning circle appears and shows you that the page is loading. Moreover, Microsoft also uses this kind of animation in the Windows Media Center, Encarta 2006, SQL Server Management Studio Express, etc.

So, why don't we use this concept to show to our users that our application is working and/or loading? Let's begin by the presentation of the component I developed.

Points of interest

Rotation speed

The LoadingCircle uses a timer, and it has two responsibilities: to determine the color of the next spoke, and to redraw the circle at a specified number of milliseconds. When you use the property RotationSpeed, you modify the timer's property named Interval. Higher the value is, slower will be the rotation. The default rotation speed is 80, so at every 80 milliseconds, the circle will be redrawn.

How to draw a spoke?

First of all, we need coordinates for each spoke. We use the function DrawLine of GDI+, which needs two points, the beginning and the end of the line.

So, let's review some simple math notions. In order to draw a perfect circle, you have to know the following trigonometry concept: the cosines of an angle in degrees give us the X and the sine gives us the Y.

The method GetCoordinate computes, for a specified radius and angle, the coordinates of a point.

private PointF GetCoordinate(PointF _objCircleCenter, 
               int _intRadius, double _dblAngle)
{
      PointF objPoint = new PointF();
      double dblAngle = Math.PI * _dblAngle / NUMBER_OF_DEGREES_HALF_CIRCLE;
      objPoint.X = _objCircleCenter.X + _intRadius * (float)Math.Cos(dblAngle);
      objPoint.Y = _objCircleCenter.Y + _intRadius * (float)Math.Sin(dblAngle);
      return objPoint;
}

The method DrawLine uses the coordinates computed by GetCoordinate, and draws a line with the two specified points and a color. Of course, we have to pass to this method the Graphics object of GDI+. As you can see, each line is rounded with the properties StartCap and EndCap of the Pen object.

private void DrawLine(Graphics _objGraphics, PointF _objPointOne, 
                      PointF _objPointTwo, 
                      Color _objColor, int _intLineThickness)
{
      using(Pen objPen = new Pen(new SolidBrush(_objColor), _intLineThickness))
      {
            objPen.StartCap = LineCap.Round;
            objPen.EndCap = LineCap.Round;
            _objGraphics.DrawLine(objPen, _objPointOne, _objPointTwo);
      }
}

How to use this component?

This component is quite easy to use. Once added to your toolbox, drag it to your form, and you are in business! Now, all you have to do is to play with the properties which change the look of the circle. Those are under the category "LoadingCircle" in the Properties panel. Also, you can change the Active property to true or false to see the animation running or not.

The component has the following properties.

Conclusion

Finally, I wish you will find this component useful. I had fun writing it, and wish you will use it. Thanks for reading this article.

Revision history

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generaljust an observation
binarydude2001
16:59 31 Jan '10  
I have noticed it can be quirky if I create as Active at design time, but if I create it as not Active at design time and set it to Active when I need it a runtime it works as I believe it is intended to be.

this.loadingCircleToolStripMenuItem1.LoadingCircleControl.Active = true;


In any case, thank you for the nice control.
Generalwell done
prolingua.geo
0:02 9 Nov '09  
well done. thanks alot
GeneralNice Work !!!
RicardoPDV
13:14 13 Aug '09  
thank you for a nice job ! Thumbs Up
Generalmemory leak?
val171
17:30 27 Oct '08  
while running your test app, i noticed that when the circle is animating, the memory usage in task manager keep increasing, does this mean memory leak? anyway, superb component...
thanks...
GeneralPrinting animation
UltraWhack
4:51 8 May '08  
Martin, great control, thanks.

Can I request a printing animation similar to MS-Word's statusstrip printing animation ? Maybe a printer icon ?
GeneralRe: Printing animation
jm33
13:47 4 Jun '08  
This is more what you are looking for:
http://www.codeproject.com/KB/miscctrl/AnimatedIcon.aspx[^]
GeneralRe: Printing animation
UltraWhack
6:06 13 Jun '08  
jm33, that's just perfect. Thanks!
GeneralNot disposing all GDI+ resources: System.AccessViolationException
bwaide
20:52 22 Apr '08  
Under some weird, non-reproducable cirumstances the method "DrawLine" can cause a "System.AccessViolationException". The problem seems to be that the following "using" clause will dispose the Pen, but not the Brush object:

[...]
using(Pen objPen = new Pen(new SolidBrush(_objColor), _intLineThickness))
[...]


You have to nest two "using"s like this:

[...]
using(SolidBrush brush = new SolidBrush(_objColor))
{
using (Pen objPen = new Pen(brush, _intLineThickness))
{
[...]


Nevertheless, thanks for this great component!
GeneralRe: Not disposing all GDI+ resources: System.AccessViolationException
Martin Gagne
4:42 23 Apr '08  
Thank you for sharing that ! Smile
GeneralHow to put a Loading Circle in a datgridviewcolmun/cell ?
platinum07
0:08 31 Dec '07  
Hi Martin, hi developers,

first of all, I have to thank Martin for the great job he did. I usually use this feature in my applications, and it really rocks ! Wink

Now I'm trying to figure out how to implement a datagridviewCell/Column and a editing control to put the loading circle in the datagridview control.
I implemented three classes to do this, but the resulting behaviour is an empty unfashionned cell showing a white part and a piece of the form behind, but no loading circle at all...

could you please help find out why my cell behave that way ?

here is my code :




/////////////////////////////////LoadingCircleCell////////////////////////////////
internal class LoadingCircleCell : DataGridViewCell
{
private Type validatingType;

public LoadingCircleCell() : base()
{
validatingType = typeof (LoadingCircle);
}

public override Type EditType
{
get { return typeof (LoadingCircleEditingControl); }
}

// A Type object for the validating type.
public override Type ValueType
{
get { return typeof (LoadingCircle); }
}

public override object DefaultNewRowValue
{
get
{
// add a new loading circle
return new LoadingCircle();
}
}

public override void InitializeEditingControl(int rowIndex,
object initialFormattedValue,
DataGridViewCellStyle dataGridViewCellStyle)
{
base.InitializeEditingControl(rowIndex, initialFormattedValue,
dataGridViewCellStyle);

LoadingCircleEditingControl editingControl = DataGridView.EditingControl as LoadingCircleEditingControl;
if (editingControl != null)
editingControl.Active = (bool) Value;
}
}


/////////////////////////////////LoadingCircleEditingControl////////////////////////////////
public class LoadingCircleEditingControl: LoadingCircle, IDataGridViewEditingControl
{
protected int rowIndex;
protected DataGridView dataGridView;
protected bool valueChanged = false;

protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
// Let the DataGridView know about the value change
NotifyDataGridViewOfValueChange();
}

// Notify DataGridView that the value has changed.
protected virtual void NotifyDataGridViewOfValueChange()
{
this.valueChanged = true;
if (this.dataGridView != null)
{
this.dataGridView.NotifyCurrentCellDirty(true);
}
}

#region IDataGridViewEditingControl Members

// Indicates the cursor that should be shown when the user hovers their
// mouse over this cell when the editing control is shown.
public Cursor EditingPanelCursor
{
get
{
return base.Cursor;
}
}


// Returns or sets the parent DataGridView.
public DataGridView EditingControlDataGridView
{
get
{
return this.dataGridView;
}

set
{
this.dataGridView = value;
}
}


// Sets/Gets the formatted value contents of this cell.
public object EditingControlFormattedValue
{
set
{
this.Active = (bool)value;
NotifyDataGridViewOfValueChange();
}
get
{
return this.Active;
}

}

// Get the value of the editing control for formatting.
public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context)
{
return EditingControlFormattedValue;
}

// Process input key and determine if the key should be used for the editing control
// or allowed to be processed by the grid. Handle cursor movement keys for the MaskedTextBox
// control; otherwise if the DataGridView doesn't want the input key then let the editing control handle it.
public bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey)
{
switch (keyData & Keys.KeyCode)
{
case Keys.Right:

break;

case Keys.Left:

break;

case Keys.Home:
case Keys.End:


case Keys.Prior:
case Keys.Next:
if (this.valueChanged)
{
return true;
}
break;

case Keys.Delete:

break;
}

//
// defer to the DataGridView and see if it wants it.
//
return !dataGridViewWantsInputKey;
}


// Prepare the editing control for edit.
public void PrepareEditingControlForEdit(bool selectAll)
{

}

// Indicates whether or not the parent DataGridView control should
// reposition the editing control every time value change is indicated.
// There is no need to do this for the MaskedTextBox.
public bool RepositionEditingControlOnValueChange
{
get
{
return false;
}
}


// Indicates the row index of this cell. This is often -1 for the
// template cell, but for other cells, might actually have a value
// greater than or equal to zero.
public int EditingControlRowIndex
{
get
{
return this.rowIndex;
}

set
{
this.rowIndex = value;
}
}



// Make the MaskedTextBox control match the style and colors of
// the host DataGridView control and other editing controls
// before showing the editing control.
public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)
{
this.Font = dataGridViewCellStyle.Font;
this.ForeColor = dataGridViewCellStyle.ForeColor;
this.BackColor = dataGridViewCellStyle.BackColor;
//this.TextAlign = translateAlignment(dataGridViewCellStyle.Alignment);
}


// Gets or sets our flag indicating whether the value has changed.
public bool EditingControlValueChanged
{
get
{
return valueChanged;
}

set
{
this.valueChanged = value;
}
}

#endregion // IDataGridViewEditingControl.
}


/////////////////////////////////LoadingCircleColumn ////////////////////////////////
public class LoadingCircleColumn : DataGridViewColumn
{

// Initializes a new instance of this class, making sure to pass
// to its base constructor an instance of a MaskedTextBoxCell
// class to use as the basic template.
public LoadingCircleColumn() : base(new LoadingCircleCell())
{
}

// The template cell that will be used for this column by default,
// unless a specific cell is set for a particular row.
//
// A MaskedTextBoxCell cell which will serve as the template cell
// for this column.
public override DataGridViewCell CellTemplate
{
get { return base.CellTemplate; }

set
{
// Only cell types that derive from MaskedTextBoxCell are supported as the cell template.
if (value != null && !value.GetType().IsAssignableFrom(typeof (LoadingCircleCell)))
{
string s = "Cell type is not based upon the LoadingCircleCell.";
//CustomColumnMain.GetResourceManager().GetString("excNotMaskedTextBox");
throw new InvalidCastException(s);
}

base.CellTemplate = value;
}
}
}


These are, to my mind, as basic as they can be.

Thanks for you help.
GeneralRe: How to put a Loading Circle in a datgridviewcolmun/cell ?
Martin Gagne
4:03 31 Dec '07  
Hello platinum07!

First of all, thanks for your comment. I'm glad to see that you appreciate my control. However, I don't think that I can help you very much since I've never really used DataGrid control in .NET.

Despite that, I suppose that is possible to draw something "manually" in a DataGrid. I would suggest you to try to create your own custom cell. A custom cell which derive from LoadingCircle.

I did some research and here what I found...

* http://msdn2.microsoft.com/en-us/library/ms171618(vs.80).aspx
* http://msdn2.microsoft.com/en-us/library/7fb61s43.aspx

As you know, my control derived from Control. I would suggest you to only change the derived class from Control to DataGridViewImageCell or DataGridViewTextBoxCell or something like that. According to the articles, you should be able to do such thing.

If anyone else tried to do this before, please let us know. I could update the article with a LoadingCircle for a DataGrid!

Martin
GeneralRe: How to put a Loading Circle in a datgridviewcolmun/cell ?
platinum07
9:28 1 Jan '08  
Hi Martin,

thanks for the links. I am gonna try to figure out how to complete this(kindda DataGridLoadingCircleCell !) and will let you know when I'm done.
GeneralRe: How to put a Loading Circle in a datgridviewcolmun/cell ?
Patrick S
11:47 30 May '08  
Darn - I wish I had seen this comment earlier.

I have already completed this task, creating a loading circle column for a datagridview. I can email you the code, if you'd like. I had intended to write an article here for it, but never got around to it, but I can definitely share my code.


It has become appallingly obvious that our technology has exceeded our humanity. - Albert Einstein

GeneralWindows.Forms.TImer needs to be changed to System.Timers.Timer
Suicide20
18:42 23 Dec '07  
I ran into an issue with this control in conjunction with an Async WaitHandle
The control refused to animate while waiting for the WaitHandle to return.
This seemed odd since another loading circle control didn't behave in this manor.
I investigated the differences between the two and determined that the other loading indicator was using System.Timers object.
I made a quick change to the code and this loading circle started to perform as expected.

Heres an example use of the AsyncWaitHandle
The Following Code assumes a windows form object with an added Loading Circle control.


delegate void DoSomethingDelegate();
DoSometihngDelegate iDoSomething;

//On Load Event Handler
void OnLoad
{
iDoSomething = new iDoSOmethingDelegate(WorkerMethod);
IAsyncResult aResult = iDoSomething.BeginInvoke(null, null);
aResult.AsyncWaitHandle.WaitOne();

iDoSomething.EndInvoke(aResult);
}

private void WorkerMethod()
{
int waitfor = 1000;
for(int i = 0; i < waitfor; i++)
Thread.Sleep(100);
}

GeneralHow to add this circle control to my toolbox component?
CSharp2005
5:35 26 Nov '07  
I downloaded the demo and solutions and working with it. But I would like to add this control to my application and how would I do it? I read it that I need to add this control to component and drag drop on the form. How would I add this control to component?

Thanks for the help!
QuestionBug report
cacerzard
23:12 4 Sep '07  
I put this control in a simple form to indicate calculation progress.A simple application created and closed this form for more than ten thousand times approximately.A "TargetInvocationException" was thrown by this form when calling "progressForm.ShowDialog()".During this period my computer became slower and the memory cost by this application increased continuously.
AnswerRe: Bug report
Martin Gagne
4:20 5 Sep '07  
Hi cacerzard,

Thank you for your message... Well, I am happy to know that someone did some stress test to the component. I will try to reproduce that and let you know what is going on...

Martin
QuestionLoading Circle in statusStrip
seang_sophorn
18:03 19 Aug '07  
Hi, I have testing your control in my application and I got a problem is that. When I add the Loading Circle into statusStrip, it work fine in the MDIForm.Smile
this.CircleProgress.LoadingCircleControl.Active = true;

But when I want to run Loading Circle from SubForm, it not work. I have set the control in Public already.Confused
MDIForm frm = new MDIForm();
frm.CircleProgress.LoadingCircleControl.Active = true;

Any idea about that?
Best regards.

QuestionHow can use this control in Visual Basic 6
sixtoja
8:04 9 Aug '07  
Hi Anybody knows how can use this control in Visual Basic 6..???

Thanks!!
AnswerRe: How can use this control in Visual Basic 6
Martin Gagne
8:46 9 Aug '07  
Well, the LoadingCircle is a .NET 2.0 component. It's not possible to use it with VB6 unless you decide to rewrite the component for VB6.

Martin
GeneralRe: How can use this control in Visual Basic 6
sixtoja
9:36 9 Aug '07  
Thanks Martin for for U answer.

So, the component is gif image file ?, or a shape and timer control with changes, in the backcolor property. some idea for write this component ?, What's the objects working in the component design?

Regards,

Sixto
Generalanother animated progress bar
OwfAdmin
10:24 5 Aug '07  
try this animated progress bar with cool circles
http://www.openwinforms.com/infinite_progress_bar.html


GeneralThreads and loading circle
CostasAn
23:17 17 Jul '07  
I am trying to use the loading circle animation in my application. What I do is when a long process takes place, before starting the process I open a new window (splash screen) which contains the loading circle control and then start my process. As said before, the use of DoEvents() works. But I dont want to call DoEvents in my process. Is there a way to open the new window using a new thread or the BackGround worker and run the process with the default thread? Or perhaps call DoEvents using a different thread or again the BackgroundWorker?

Btw Martin, I will use your control on a non commercial University research application. Could you provide you email so I can add you to the references of the application?
GeneralRe: Threads and loading circle
Martin Gagne
5:06 18 Jul '07  
Hi CostasAn,

Glad to know that you use my control for a University Research Application. First of all, I don't personally recommend the use of DoEvents(). It reminds me VB6 and it's the wrong way to add responsiveness to your application.

The solution that you bring, which consists in using threads, is the right way to add responsiveness. However, you can’t create, change, draw, etc. anything in the UI with other threads. I would suggest you to see your problem from a different point of view. Start your process, the one which take a long time to execute, in a thread (or a BackgroundWorker); open a modal widow (the popup) and start the LoadingCircle and wait for the "end event" from the BackgroundWorker. Let me know what you think of that.

My email is mgagne_98 (at) hotmail (dot) com.

Regards,
Martin


GeneralRe: Threads and loading circle
CostasAn
21:34 18 Jul '07  
Hi Martin,

Your suggestion is correct in my opinion but consider this: I develop an application which contains some number of long procedures so I will use loading circle a number of times. I developed a component (which is a splash screen containing the loading circle) which I wish to use in the following way:

splashscreen.Show()
longprocedure()
splashscreen.Close()

What I want to achieve is the code of the original application to remain as is and just add show() and close() of the splash screen in it. So I desire to add code to the splash screen so the animation will be shown without changing the original code, because the splashscreen component will be given to other programers of the application as is. What I think is if there is some way to "call" the loading circle using a thread or something similar.

I tried to add a background worker in the splashscreen which will call a procedure which continuasly calls DoEvents() in a different thread, but this didnt work for some reason I do not understand.

BTW loading circle is a very nice and good looking component. If we could find a solution for this problem rest assured that it will be used further not only in the application I am developing but perhaps in more research applications.

Thanks and regards,
Costas


Last Updated 15 Feb 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010