Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Simple Clock Using GDI+

0.00/5 (No votes)
9 Oct 2009 3  
This is a simple analog clock that demonstrates the use of GDI+.

Introduction

The code demonstrates the simplicity of using GDI+ for simple drawing tasks. I have used it to make a simple desktop clock. In this little clock, users have the ability to choose the clock image that they want (which is just an image file); I have also added this little feature that each clock image can be associated with an optional .ini file that has the settings of that clock image. These settings include: the color and size of each clock hand, the centre of the clock (in case that the image is not symmetric), showing date, etc.

I have actually put some clock images and their .ini files in the project folder to demonstrate the features of this program.

The Code Structure

There is the AceClockForm class which is the only form used in the program, all the calculations and drawings using GDI+ are done in this class. The INISettingsReaderWriter class is used to store and retrieve settings for the program, and to retrieve the settings of clocks. Finally, the Settings and ClockSettings classes store the settings of the program and clocks.

Drawing the Clock

The Math!

To draw the clock hands, which are simply lines in my application, we need to know their coordinates! For each line, one of the two points of the line is simply the centre of the clock; for the other point, the angle of that line (or clock hand) is calculated first:

 // alpha is the angle of the hour hand 
// second part(now.Minute / 2) is in fact now.Minute/60*(360/12) 
double alpha = now.Hour * (360/12) + now.Minute / 2;
//beta is the angle of the minute hand 
//in fact now.Minute * (360/60) + now.Second /60 * (360/60)
double beta = now.Minute * (360/60) + now.Second / 10; 
//gama is the angle of the second hand 
double gama = (now.Second+(drawSHContinuously? now.Millisecond/1000f : 0f)) * 360/60;    

Here is the explanation:

The hour hand: the circle of a clock face is divided by 12 hours, so each hour is 1/12 of the circle, and since a circle is 360 degrees, each hour is 360/12 degrees, so the angle of the hour hand is calculated this way, but as you all have noticed, no analog clock's hour hand stands still exactly on the current hour number until an hour is passed. Instead, it moves slowly towards the next number. In order to calculate this angle, the minute part of the time is taken into account. The calculation is almost the same except that since each 60 minutes equal an hour, a division by 60 is added.

The minute hand: It is like the hour hand, the difference is that each minute is 1/60 of the circle, so it is 360/60 degrees, and again, to make it look smoother, the seconds are taken into account, and since each 60 seconds is 1 minute, a division by 60 is added.

The second hand: again, each second is 1/60 of the circle, etc... but there is this drawSHContinuously thing. In my application, there is an option that allows the second hand to be drawn continually. If that option is activated, then the milliseconds become significant because the second hand is redrawn many times between each 2 seconds.

So far the angles of each hand are calculated, but these angles are in degrees, they are clockwise (!) (as opposed to trigonometry), and they are relative to the vertical line, so they are converted to CCW, radiant, relative to the horizontal line angles:

alpha = -alpha * Math.PI / 180d + Math.PI / 2d; 
beta = -beta * Math.PI / 180d + Math.PI / 2d; 
gama = -gama * Math.PI / 180d + Math.PI / 2d; 

Then these angles are used to calculate the coordinates of each hand's end points. This is done in the getTaleCoordinates() method, which gets the coordinates of one end point of a line, its length and angle, and calculates its other end point.

Using GDI+ to Draw the Clock

I have used BufferedGraphics in the code to eliminate possible blinks of the clock image. This BufferedGraphics is allocated in the SetImage() method in the code. The BufferedGraphics class has a Graphics property which returns the Graphics object on which the clock is drawn.

The actual drawing is done in the drawClock() method. The following line of code draws the clock image:

bgg.DrawImage(this.image, 0, 0, this.Width, this.Height);

and these lines draw the clock hands:

//hour hand
bgg.DrawLine(hourPen, 
  getTaleCoordinates(centerPoint.X, centerPoint.Y, 
                     clockSettings.HourTale, alpha+Math.PI), 
  getTaleCoordinates(centerPoint.X, centerPoint.Y, 
                     clockSettings.HourLength, alpha));

//minute hand
bgg.DrawLine(minutePen, 
  getTaleCoordinates(centerPoint.X, centerPoint.Y, 
                     clockSettings.MinuteTale, beta + Math.PI), 
  getTaleCoordinates(centerPoint.X, centerPoint.Y, 
                     clockSettings.MinuteLength, beta));

//second hand
bgg.DrawLine(secondPen, centerPoint, 
  getTaleCoordinates(centerPoint.X, centerPoint.Y, 
                     clockSettings.SecondTale, gama + Math.PI));
bgg.DrawLine(secondPen, centerPoint, 
  getTaleCoordinates(centerPoint.X, centerPoint.Y, 
                     clockSettings.SecondLength, gama));

The first parameter to the DrawLine method is a Pen, and the second and the third are the ending points of the line to be drawn.

The date is also drawn. The DrawString method is used to draw strings in GDI+. The use of this method is simple, it takes the string to be drawn, the font by which the string is to be drawn, a brush which can be used to define a texture for the text, and the coordinates of the upper left corner of the string. Here is the code:

bgg.DrawString(now.ToShortDateString(), dateFont, dateBrush, 
               clockSettings.DateCenterX - strsize.Width / 2, 
               clockSettings.DateCenterY - strsize.Height / 2);

Settings and ClockSettings Classes

The Settings class stores the settings of the program, and the ClcokSettings class stores the settings of a clock. Loading and saving the data of these classes is explained below.

Loading and Saving Clocks and Programs Settings

The INISettingsReaderWriter class is used to save and load settings for both the clocks and the application itself. As the name suggests, these settings are stored in the form of INI files, in which each piece of data is stored in a separate line in this form:

Key=Value ;this is a comment!

This class uses Regular Expressions to parse data stored in INI files, and uses Reflection to fill objects with these data, or write them to a file. Here is the Regular Expression pattern that is used:

static Regex iniFilePattern = 
    new Regex(@"^\s*(?<key>\w+)\s*=[\s-[\n]]*(?<value>" + 
              @"[\w//:/. -]*)/s*(;.*)?\\:\.]*)\s*(;.*)?",
    RegexOptions.Multiline | RegexOptions.Compiled);

The methods dictionaryToObject and objcetToDictionary are provided to store the data in a System.Collections.Generic.Dictionary<string, string> object in the properties of the given object, and vice versa. As I have mentioned above, this is done through Reflection:

Type t=obj.GetType();
foreach (var pi in t.GetProperties())
{
    ...
}

History

  • Version 1.0.

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