Articles‎ > ‎

AnimatedThrobber

Introduction

You all know what throbber is. This is small rotated circle used to indicate that program is working. Used usually for progress bar replacement when current progress state is unknown (i.e. infinity progress bar). For example throbber is used in Firefox browser.

Background

Several months ago I found a nice control named LoadingCircle on CodeProject. The idea was good and control looks beautiful, but is has strange design-time behavior and its toolstrip control are almost useless. So I decided take best from this control (painting logic) and write another much more design-time friendly component. And I rename LoadingCircle to Throbber (a name used by Firefox).

Doing the job

First of all I moved all painting logic to independent class called ThrobberRenderer is has only two public methods and several public properties for customization. This class is needed because I want to create several controls with throbber logic (custom control and ToolStripItem ancestor). I do some optimizations and code cleanup, but the most of code and all ideas are from original control.

Second part is to create control which uses this ThrobberRenderer class to paint itself, add animation logic, autosize logic. There was a problem with implementing autosize logic, because just override AutoSize property and GetPrefferedSize is not enough, but this problem is solved by overriding SetBoundsCore method and some design-time class are needed too (for prevent resize autosized control).

Third part is to create ToolStripThrobberButton class, a ToolStripButton ancestor with clickable button behavior. The main idea here is hide all image behavior and replace them with throbber. This component can have text and looks like as standard toolstrip button with image replaced with throbber.

And as end part I create ToolStripThrobberItem a simplified version of ToolStripThrobberButton that inherited from ToolStripItem (instead of ToolStripButton) and can only draw itself, without text or clickable behavior support.

Using the code

Using is pretty simple. Just drop the control on form (or create toolstripitem on toolstrip), set up several properties and that's all.

All these components have several basic properties:

  • InnerCircleRadius
  • OuterCircleRadius
  • NumberOfSpoke
  • SpokeThickness
  • Style - enumeration that has Custom, Firefox, MacOSX and IE7 values

First four properties used to customize throbber, and the last one used to set all these properties at once.

The control has animated behavior when enabled and static when disabled. Animation speed can be controlled through AnimationSpeed property. I have also added BorderStyle property to mimic any other control from standard library.

ToolStripThrobberButton has also ThrobberColor property to distinguish text color (ForeColor) from throbber color.

Points of interest

Firsts of all we need to hide all properties that exist in Control class but don't need in this control. This is done by overriding these properties and hide them from world by using special attributes.

[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false)]
public override Font Font
{
get { return base.Font; }
set { base.Font = value; }
}

Then I need to rewrite serialization logic. For example when Style property is set, there is no need to store InnerCircleRadius, OuterCircleRadius, etc. This is done by using special private method. Such method starts with ShouldSerialize prefix followed with the property name and is used by design serialization logic to decide write property or no.

private bool ShouldSerializeInnerCircleRadius()
{
return Renderer.Style == ThrobberStyle.Custom;
}

private bool ShouldSerializeStyle()
{
return Renderer.Style != ThrobberStyle.Custom;
}

When control is autosized I expect non-resizable behavior in designer. But I was wrong. After some investigation I found that custom designer is needed to achieve needed behavior.

public class AnimatedThrobberDesigner : ControlDesigner
{
public override SelectionRules SelectionRules
{
get
{
return base.SelectionRules &
~(((Control)Component).AutoSize ? SelectionRules.AllSizeable : 0);
}
}
}

Toolstrip items was created with some cut'n'paste work. Logic is almost the same, and this is a place where multiple inheritance (unsupported by C#) can reduce amount of code to write.

Conclusion

Work is done. And I hope that you find these components useful. And I wish to thank Martin Gagne, without your work this cannot be done.

Translation

And again I must say that my English is far from perfect. So if you find some mistakes fell free to email me.

Downloads

animatedthrobber.zip | Mirror (Slow)
AnimatedThrobber components sources, as well as small test application and precompiled demo.

Comments