Click here to Skip to main content
13,350,047 members (48,519 online)
Click here to Skip to main content
Add your own
alternative version


108 bookmarked
Posted 8 Jun 2005

Yet Another Analog Clock

, 8 Jun 2005
Rate this:
Please Sign up or sign in to vote.
Yet another analog clock control in C#, but this one is different.

Sample screenshot


There are many 'Analog clocks' at CodeProject and this is yet another (hence the title), but I have designed this one to be slight different. This analog clock is very much customizable (besides being pretty ;-)). You can resize it, change the colors, time, rendering quality add a background image and a lot. Take a look at the list of properties. Much of the inspiration comes from A Vector Graphics Rendered Animated Clock and Public Joe.

Background Mathematics

Let us delve back into some basic mathematics before getting involved in the programming aspects. You all know that a circle is of 360° degrees or 2Π in radians and that there are 12 digits on the face of the clock from one to twelve. To draw the twelve digits on the face, we need to place each of them at same degree from each other, so 360/12 = 30, so we need to place each digit 30° apart from the other. This means starting from 0° , 30°, 60° up to 360°. Also recall that angles are measured positive clockwise and negative anticlockwise. Now how are the minute, hour and second hand drawn. Quite simple, recall that we can calculate any point by using the formulas as seen in the picture below:

Sample screenshot

We need to know the angle θ and then we can draw a line from the centre of the circle. I.e., from origin to that point for a given θ. θ is the angle or should I say rotation. Note that a second hand takes 60 ticks to complete one complete rotation. So what should be the angle of rotation when sec = 1? Well it should be:

Second Rotation = 2.0 * Π * sec/60.0

Think of the term sec/60.0 as the percentage of how much rotation must be given for the given value of second. For example:

when sec = 0 , rotation is 0° ( i.e. the second hand is at 12)
when sec = 15 , rotation is 25% ( i.e. the second hand is at 3)
when sec = 30 , rotation is 50% ( i.e. the second hand is at 6)

Now that we have the value of θ or rotation, we can find the value of x and y and draw a line from the origin to the point P using the above mentioned formula.

Minute Rotation = 2.0 * Π * ( currentmin + currentsec/60.0 )/60.0

Now again think of the term ( currentmin + currentsec/60.0 )/60.0 as the percentage of how much rotation must be given for the given value of currentmin and currentsec. Note that in the above term we have also added second rotation so that the minute hand is updated every second. But updating minute hand every second doesn't look neat as it is updated only as significant changes occur in the value of minute rotation and it would look like that the minute hand moves unevenly. If you wish to update the minute hand after every minute only, just remove the term currentsec/60.0. Now when the second hand completes one rotation, the minute hand will move ahead. (In order to see what I am trying to tell, download the source code and observe how the minute hand behaves in the clock that displays the time for Honk Kong and the rest.)

Hour Rotation = 2.0 * Π * ( currenthour + currentmin/60.0 )/12.0

Similar technique here. The hour hand is updated every minute so we have added the minute rotation too. You can think of how the clock would behave if we didn't update the hour hand every minute.

Now let's move on to the implementation details. All the drawing is done in the function DrawClock: private void DrawClock(Graphics e). The Analog Clock exposes a property named Time. Whenever the value of this property is changed, the clock is updated.

Enabling Double Buffering

In the constructor of the UserControl, I have enabled Double-buffering on the control which prevents flicker caused by the redrawing of the control. To fully enable double-buffering, we must also set the UserPaint and AllPaintingInWmPaint style bits to true (to read more about Double Buffering read this).

this.SetStyle(ControlStyles.DoubleBuffer, true);

Enabling Anti-aliasing

To make the clock look smooth, I have set the SmoothingMode and TextRenderingHint property of the graphics object to Anti-alias. The SmoothingMode property makes the face of the clock look smooth and the TextRenderingHint property makes the clock numerals look smooth. Please note that SmoothingMode has no effect on the numeral that is drawn on the clock.

grfx.SmoothingMode = smoothingMode;
grfx.TextRenderingHint = textRenderingHint;
grfx.InterpolationMode = 

Here is how the analog clock appears without anti-aliasing.

Sample screenshot

Drawing the clock face

Next I have made four rectangles inside which circles are drawn. The drop shadow is also an eclipse which is at a slightly offset from the face eclipse. (Please note that there can be other ways to implement this, such as drawing eclipse directly).

//Define rectangles inside which we will draw circles.
Rectangle rect = new Rectangle(0+10 ,0+10 ,(int)x-20 ,(int)y-20);
Rectangle rectinner = new Rectangle(0+40 ,0+40 ,(int)x-80 ,(int)y-80);
Rectangle rectrim = new Rectangle(0+20 ,0+20 ,(int)x-40 ,(int)y-40);
Rectangle rectdropshadow = new Rectangle(0+10 ,0+10 ,(int)x-17 ,(int)y-17);

Next I have drawn a gradient filled eclipse inside all these rectangles,

//Drop Shadow
gb = new LinearGradientBrush(rect, Color.Transparent, 
     dropShadowColor, LinearGradientMode.BackwardDiagonal);
grfx.FillEllipse(gb, rectdropshadow);

gb = new LinearGradientBrush(rect, rimColor1, rimColor2 , faceGradientMode);
grfx.FillEllipse(gb, rect);

gb = new LinearGradientBrush(rect, faceColor1, faceColor2, rimGradientMode);
grfx.FillEllipse(gb, rectrim);

This gives the basic face of the clock as follows:

Sample screenshot

Drawing the circular face image

To draw a circular image within the face of the clock, we have to define a circular clipping region and draw image inside it. Once we have done that, we'll have to reset the clipping region. Here is how it is done:

//Define a circular clip region and draw the image inside it. 
GraphicsPath path = new GraphicsPath(); 
if(this.img != null) 
grfx.DrawImage(this.img, rect); 
//Reset clip region 

where img represents the image. It is declared as:

private Image img;

The output is something like this:

Sample screenshot

Drawing the Numerals

Next we need to draw the numerals. But before we do that, we need to move the origin of the control to the center of the client area. (By default it is at the top right corner). We can do so by using the graphic object's TranslateTransform method. Here's how it is done.

grfx.TranslateTransform(midx, midy);

I have already defined midx and midy as the center of the control. Next we draw the numerals:

StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;

//Draw Numerals on the Face 
int deg = 360/12;
  for(int i=1; i <= 12; i++)
    grfx.DrawString(i.ToString() , textFont ,stringBrush , 
      -1 * GetX(i * deg +90) , -1 * GetY(i * deg + 90),format); 

As you can see, we have multiplied both the value of x and y by -1 so that the numeral 12 appears on the top, other wise it'll appear on bottom as seen below.

Sample screenshot

Drawing the Second Hand

First, we will draw the hour hand, then the minute hand and finally the second hand. The order is important because it is the same in real world clocks.

Our second hand can tick in two different ways -- as explained earlier, one is normal tick style which we usually see in quartz clocks and the other is smooth style usually seen in automatic clocks. I have defined enumerations for both of these styles which is used to determine the value of the rotation.

//Draw Minute hand

if(minHandTickStyle == TickStyle.Smooth)
  minuteAngle = (float)(2.0 * Math.PI* ( min + sec/60.0 )/60.0);
  minuteAngle = (float)(2.0 * Math.PI* ( min /60.0));

pen.EndCap = LineCap.Round;
pen.StartCap = LineCap.RoundAnchor;
pen.Width = (int) radius/14;
//--End Minute Hand

The drop shadow of the second hand is nothing but another second hand.

//Drop shadow
centre.Offset(2, 2);
pen.Color = Color.Gray;
Point minHandShadow = new Point( (int)( radius * Math.Sin(minuteAngle) ), 
     (int)( -(radius) * Math.Cos(minuteAngle)+2) );

  pen.Color = minuteHandDropShadowColor;
  grfx.DrawLine(pen, centre, minHandShadow);

centre.X = centre.Y = 0;
pen.Color = minHandColor;
Point minHand = new Point( (int)( radius * Math.Sin(minuteAngle) ), 
     (int)( -(radius) * Math.Cos(minuteAngle)) );
grfx.DrawLine(pen, centre, minHand);

Similar technique prevails for the hour and minute hand.

How to use the Analog Clock.

Add the AnalogClock control to your VS.NET toolbox. Then place the analog clock and a timer to your form. Set an appropriate interval for the timer and enable it. Double click the timer and add the following code:

analogClock1.Time = DateTime.Now;

List of Properties for the Analog Clock control

  • DrawDropShadow - Determines whether drop shadow for the clock is drawn or not.
  • DrawHourHand - Determines whether the hour Hand is shown.
  • DrawHourHandShadow - Determines whether the hour hand casts a drop shadow for added realism.
  • DrawMinuteHand - Determines whether the minute hand is shown.
  • DrawMinuteHandShadow - Determines whether the minute hand casts a drop shadow for added realism.
  • DrawNumerals - Determines whether the numerals are drawn on the clock face.
  • DrawRim - Determines whether the clock rim is drawn or not.
  • DrawSecondHand - Determines whether the second hand is shown.
  • DrawSecondHandShadow - Determines whether the second hand casts a drop shadow for added realism.
  • DropShadowColor - Sets or gets the color of the drop Shadow.
  • DropShadowOffset - Gets or sets the drop shadow offset.
  • FaceColorHigh - Determines the first color of the clock face gradient.
  • FaceColorLow - Determines the second color of the clock face gradient.
  • FaceGradientMode - Gets or sets the direction of the clock face gradient.
  • FaceImage - The Background image used in the clock face.
  • HourHandColor - Gets or sets the color of the hour hand.
  • HourHandDropShadowColor - Sets or gets the color of the hour hand drop shadow.
  • MinuteHandColor - Gets or sets the color of the minute hand.
  • MinuteHandDropShadowColor - Sets or gets the color of the minute hand drop shadow.
  • MinuteHandTickStyle - Defines the minute hand tick style.
  • NumeralColor - Sets or gets the color of the clock numerals.
  • RimColorHigh - Determines the first color of the rim gradient.
  • RimColorLow - Determines the second color of the rim face gradient.
  • RimGradientMode - Gets or sets the direction of the rim gradient.
  • SecondHandColor - Gets or sets the color of the seconds hand.
  • SecondHandDropShadowColor - Sets or gets the color of the second hand drop shadow.
  • SecondHandEndCap - Determines the seconds hand end line shape.
  • SecondHandTickStyle - Defines the second hand tick style.
  • SmoothingMode - Sets or gets the rendering quality of the clock.
  • TextRenderingHint - Sets or gets the text rendering mode used for the clock numerals.
  • Time - The System.DateTime structure which is used to display time.

Know Problems/Issues

  • Using a large image for the FaceImage property results in poor performance and increased memory consumption, since the image is redrawn every time the Time property is changed.

  • Using large font size on a large clock results in numerals going out the clock face. I haven't given much attention to this detail. This is a design flaw.

  • The width of the rim remains constant and doesn't change with the size of the clock. This is yet another design flaw.

  • The numeral font size doesn't change automatically with the size of the clock. This hasn't been implemented.


  • v1.0 - 08 June 2005


This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


About the Author

Obaid ur Rehman
United States United States
Obaid ur Rehman is an undergraduate Computer Science student at the University of Karachi.

You may also be interested in...

Comments and Discussions

GeneralMy vote of 5 Pin
Chana Winkler13-Dec-10 22:41
memberChana Winkler13-Dec-10 22:41 
Questionoscillating effect Pin
yyxl25-Nov-10 15:04
memberyyxl25-Nov-10 15:04 
GeneralI did it too Pin
gajatko24-Aug-07 9:15
membergajatko24-Aug-07 9:15 
GeneralRe: I did it too Pin
gajatko24-Aug-07 9:19
membergajatko24-Aug-07 9:19 
NewsRound Control Pin
flow19659-Apr-07 9:38
memberflow19659-Apr-07 9:38 
GeneralHI Pin
/-\ \/\/ /-\ /\/ [GUY]13-Mar-07 8:01
member/-\ \/\/ /-\ /\/ [GUY]13-Mar-07 8:01 
GeneralHI Pin
/-\ \/\/ /-\ /\/ [GUY]13-Mar-07 7:33
member/-\ \/\/ /-\ /\/ [GUY]13-Mar-07 7:33 
QuestionHow to add to toolbox? Pin
wizkid126-Aug-06 16:31
memberwizkid126-Aug-06 16:31 
AnswerRe: Chews up CPU Pin
wizkid126-Aug-06 17:45
memberwizkid126-Aug-06 17:45 
AnswerRe: Chews up CPU Pin
wizkid126-Aug-06 17:55
memberwizkid126-Aug-06 17:55 
AnswerRe: How to add to toolbox? Pin
SystemExam25-Dec-09 21:44
memberSystemExam25-Dec-09 21:44 
GeneralSpelling nit Pin
jmueller9-Jan-06 13:29
memberjmueller9-Jan-06 13:29 
QuestionHow to build? Pin
852y3agnna3-Jul-05 20:27
member852y3agnna3-Jul-05 20:27 
AnswerRe: How to build? Pin
Obaid ur Rehman4-Jul-05 4:19
memberObaid ur Rehman4-Jul-05 4:19 
GeneralRe: How to build? Pin
852y3agnna4-Jul-05 20:04
member852y3agnna4-Jul-05 20:04 
GeneralRe: How to build? Pin
Obaid ur Rehman4-Jul-05 22:34
memberObaid ur Rehman4-Jul-05 22:34 
GeneralRe: How to build? Pin
852y3agnna4-Jul-05 22:43
member852y3agnna4-Jul-05 22:43 
GeneralSmooth mode suggestion Pin
SGInut17-Jun-05 3:46
memberSGInut17-Jun-05 3:46 
GeneralKarachi numerals Pin
AndyGarcia16-Jun-05 5:39
memberAndyGarcia16-Jun-05 5:39 
GeneralRe: Karachi numerals Pin
Obaid ur Rehman16-Jun-05 8:02
memberObaid ur Rehman16-Jun-05 8:02 
GeneralConverted to VB.NET Pin
GraGra_3314-Jun-05 19:02
memberGraGra_3314-Jun-05 19:02 
GeneralRe: Converted to VB.NET Pin
Obaid ur Rehman14-Jun-05 22:47
memberObaid ur Rehman14-Jun-05 22:47 
GeneralRe: Converted to VB.NET Pin
GraGra_3314-Jun-05 23:07
memberGraGra_3314-Jun-05 23:07 
It's been sent to your email address. Please let me know if you update your C# code so that I can keep the VB version in sync. Smile | :)
GeneralRe: Converted to VB.NET Pin
GraGra_3314-Jun-05 23:58
memberGraGra_3314-Jun-05 23:58 
GeneralRe: Converted to VB.NET Pin
Obaid ur Rehman16-Jun-05 21:56
memberObaid ur Rehman16-Jun-05 21:56 

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.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.180111.1 | Last Updated 8 Jun 2005
Article Copyright 2005 by Obaid ur Rehman
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid