Click here to Skip to main content
15,893,663 members
Articles / Programming Languages / C#
Article

A Resizable Analog Clock in C# using GDI+ & Windows Forms

Rate me:
Please Sign up or sign in to vote.
3.71/5 (21 votes)
8 Sep 20032 min read 84.2K   2.1K   26   9
An analog clock using anti-aliasing & double-buffering which can be resized, even into elliptical shapes.

Sample Image - time_ana.jpg

Introduction

Unlike other clock programs, this program possesses the following special features:

  • Resizability: The main feature is that it can be resized according to the user's demands. Even an elliptical shape can be created. The hands, numerals and clock face all can have their shape changed at will.
  • Code Size & Elegance: Special importance has been given to reduce the code size and create elegant code. This program is perhaps the smallest analog clock program in terms of number of lines.
  • Double Buffering: It uses double buffering to eliminate flicker.
  • Anti-aliasing: It uses anti-aliasing to reduce jaggedness.

The Program

The main part of the program is the plotting of the hands. For this, we use polar co-ordinates with the origin at the center of the window and with angle starting at the twelve-o'clock position running clockwise. The semi-width or x-radius of the clock is taken to be one-third of that of the enclosing window, ie. Width/3. Consequently, the x value is given by the sum of the x co-ordinate of the center and the radius multiplied by the sine of the angle:

int x_sec = Width/2  + 
    (int)(Width/3  * Math.Sin(2 * Math.PI * (double)i_sec/60));

The angle is simply the fraction of a full circle, where 60 seconds correspond to 2 pi radians. In case of the y co-ordinate of the hand, we must take into account the fact that the 12 o'clock position is that of minimum y value, so that the cosine must be subtracted:

int y_sec = Height/2 - 
    (int)(Height/3 * Math.Cos(2 * Math.PI * (double)i_sec/60));

It is often helpful to draw a diagram to elucidate the above concepts. Of course, the program uses standard coding conventions, ie. i,x,y denote integers, g denotes Graphics and f denotes Font. The minutes hand is drawn in a similar fashion. In case of hours, it must be remembered that only 12 hours make a full circle.

In order to make the numerals resizable, the following for loop is used:

for(int j=1; j <= 12; j++)   // draw numerals
   {
    g.DrawString(""+j, f, Brushes.Red,
      Width/2 + (int)(Width/3 * Math.Sin( j * Math.PI/6))
      - (int)g.MeasureString(""+j,f).Width/2,
      Height/2 - (int)(Height/3 * Math.Cos( j * Math.PI/6))
      - (int)g.MeasureString(""+j,f).Height/2);
   }
}

Here, the fact that each numeral represents one-sixth of one pi or semicircle is used to plot the values. Moreover, the calculated half value of the string widths and heights are subtracted in order to provide correct centering of the numerals.

To compile and run, enter

> cs time_ana.cs     
> time_ana.exe

History

Project completed on 6th Sept. 2003.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Generalthis is good Pin
alrsds23-Sep-09 17:18
alrsds23-Sep-09 17:18 
Generalcreate elegant code Pin
Carlos H. Perez9-Sep-03 13:18
Carlos H. Perez9-Sep-03 13:18 
The statement "create elegant code" got my attention. It is quite a strong statement to make about one's code, so I assumed that your code was written with special care to make it look "elegant".

I was dissapointed with the supposely elegance of the code.
In fact is has several serious flaws --both from a elegance and efficiency point of view:

1- I don't know if my tab settings are different from the one you used to create the source file but there is not tab or blank spaces between the first bracket of the methods and the source code that makes the body of the method. It is customary to leave an indentation between the first bracket of a method and the code or body of the method. It does look akward to have the most outer brackets of the method aligned with the code of the method.

2- You are putting a horrible stress on the system resources but creating new fonts and new pens everytime you paint the clock. You need to use the "using" statement or directly call the Dispose method to get rid of those resources as soon as possible. The ideal way of doing it however would be to create static pens --since your always using the same colors-- and dispose of those pens on the Dispose method of the form. This would be a way more efficient way of doing it. The same goes for the Font. It is very inefficient to create a new font everytime you paint the clock.

3- What about that timer in the constructor too. It needs to be dispose of when the class is destroyed.

4- Why there is a Graphic object declared at the class level when it is only use on the OnPaint method? There is not need for
that class level "global" declaration.

5- There is not need --unless you are a "dumb" code generator to use the "this" keyword in human written code. The biggest reason that I have heard why programmers use it is because it helps them to pop the intellisense drop down window and choose the method they need. This basically means laziness. The "this" keyword is superflous and have no place in "elegant" code.
I notice a trend on many programmer to use this kind of coding. I guess this come from the fact that they see the code generator to use the "this" keyword on the automatically generated code and they think that imitating a "dumb" code generator is a good thing to do. Well, it is not.

6- What's the meaning of the method "f_draw". First it has the signature of an event handler but it is not. Second it is has an unfriendly name and third if you need to programmatically force the form to redraw the form already expose the Invalidate method inherited from the base Control class.

7- If you really want the less line of code possible, the code for the double buffering set up could have been just one line instead of three by using the Or flag in just one call.

8- It looks really odd to have the hours indicators to get out the bounds of the clock itself. They should be inside the bounds of the clock circle.

If you fix all of the above, the code would have a better chance to meet the claims you made.

Regards,
Carlos.













GeneralRe: create elegant code Pin
ted f.a. van gaalen10-Sep-03 0:04
ted f.a. van gaalen10-Sep-03 0:04 
GeneralRe: create elegant code Pin
eanderson16-Sep-03 4:01
eanderson16-Sep-03 4:01 
GeneralRe: create elegant code Pin
Keith Rule16-Sep-03 5:34
professionalKeith Rule16-Sep-03 5:34 
GeneralRe: create elegant code Pin
Torben Jakobsen20-Sep-03 21:59
Torben Jakobsen20-Sep-03 21:59 
GeneralRe: create elegant code Pin
jeff.hill21-Feb-05 14:23
jeff.hill21-Feb-05 14:23 
QuestionWhy not using this? Pin
Hamed Musavi9-Feb-09 7:29
Hamed Musavi9-Feb-09 7:29 
GeneralRe: create elegant code Pin
JayJeckel23-Nov-13 8:03
JayJeckel23-Nov-13 8:03 

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.