Click here to Skip to main content
15,867,453 members
Articles / Multimedia / GDI+
Article

Analog clock control in C#

Rate me:
Please Sign up or sign in to vote.
4.40/5 (40 votes)
23 Feb 2005CPOL5 min read 316.9K   16.3K   97   31
How to make an analog clock control in C#.

Image 1

Introduction

This article shows how to create an analog clock control in C# using simple and efficient coding, and such that it adjusts itself whenever resized to new dimensions. There are certain settings to make the control customizable. Being a component, we can add our control to the toolbox, drop it on a form, and draw it to any dimension, it will look fine.

Who should read this article

This article is for you if you are new to C# controls but quite familiar with C# and its syntax. It also assumes that you have some basic knowledge of high school mathematics.

Background

To start with our clock, we need some mathematics. The positions of hands in a clock are specified in polar coordinate system. The minute/hour/second values are nothing but a function of the angular displacement of the hand. Hence, we have to write some code to convert this polar quantity to a rectangular one because C# graphics doesn’t understand polar coordinate system; we have to specify the x and y coordinates of the hand.

We first need to find the values of current time.

C#
DateTime dateTime = DateTime.Now;
int hour = dateTime.Hour % 12; // also converts 24hrs to 12hrs format
int minute = dateTime.Minute;
int sec = dateTime.Second;

We have to convert these values to radians. In one complete rotation of a hand, there are 360 degrees (radians) but only 12 hours; so to convert hours to radians, we have to write:

C#
float hourRadian = hour * 360/12 * PI/180;

Similarly, as there are 60 minutes (and seconds) for one rotation, we can end up with the following formulae:

C#
float minRadian = minute * 360/60 * PI/180;
float secRadian = sec * 360/60 * PI/180;

Now the only thing left is to convert these polar quantities to rectangular ones so that we can draw a line (hand) using x and y coordinates. Let's recall our school mathematics formulae to solve this problem.

C#
X = r*cos(angle)
Y = r*sin(angle)

But there is one problem with this: a clock rotates in clock-wise manner while our polar system rotates in anti-clockwise manner; so if we use the above formula directly, the output will be an “image” of the clock (i.e., rotating in anti-clockwise direction). To have this problem solved, let's invert the position of sine and cosine in the above formulae: hence, our code to transform the position of clock hand (its end point) from polar to rectangular system will be:

C#
float hourEndPointX = lengthofHourHand * System.Math.Sin(hourRadian)
float hourEndPointY = lengthofHourHand * System.Math.Cos(hourRadian)

Now we’ve done all the transformation, it's time to draw a line from the centre of the clock to the endpoints of the hands. We can do something like:

C#
Line(centerX, centerY, hourEndPointX, hourEndPointY)

The above code works fine but this is a digital rather than an analog clock in the sense that hour hand is only updated after one entire hour. For instance, observe your clock when time changes from 9:59 to 10:00, the hour hand moves from 9 to 10 directly. We need to do something such that the hour hand also updates its position after every minute. For this, let us include fraction of minutes in the calculation of hour radians, as follows:

C#
float hour = dateTime.Hour%12 + (float)dateTime.Minute/60
float hourRadian = hour * 360/12 * PI/180;

Now convert these polar quantities to rectangular form and draw the hand.

C#
float hourEndPointX = lengthofHourHand * System.Math.Sin(hourRadian);
float hourEndPointY = lengthofHourHand * System.Math.Cos(hourRadian);
Line(centerX, centerY, hourEndPointX, hourEndPointY);

In a similar fashion, we can write code for drawing hands of minutes and seconds.

Creating our control

Now that we have learned the basics of clock implementation, it's time to build our control. Create a new project named "AnalogClockControl". Be sure to select "Windows Control Library" from the Templates.

Image 2

Delete "usercontrol1.cs" from your project, then add a new class to your project.

Image 3

Select "User Control" from the templates, rename "UserControl1.cs" to "AnalogClock.cs".

Open the design view and drag a timer control from your toolbox on your control. Set its Interval property to 1000, and Enabled property to true. This timer will be used to keep our clock "running", i.e. it will update our clock after every second.

Let's start our coding by declaring the following member variables:

C#
public class AnalogClock : System.Windows.Forms.UserControl
{
    const float PI=3.141592654F;

    DateTime dateTime;

    float fRadius;
    float fCenterX;
    float fCenterY;
    float fCenterCircleRadius;

    float fHourLength;
    float fMinLength;
    float fSecLength;

    float fHourThickness;
    float fMinThickness;
    float fSecThickness;

    bool bDraw5MinuteTicks=true;
    bool bDraw1MinuteTicks=true;
    float fTicksThickness=1;

    Color hrColor=Color.DarkMagenta ;
    Color minColor=Color.Green ;
    Color secColor=Color.Red ;
    Color circleColor=Color.Red;
    Color ticksColor=Color.Black;

    ...
    ...

Whenever our control is loaded, its attributes must be initialized. Our clock should also adjust itself whenever resized to a new value. So add the following code to the Load and Resize events of our control. In the Resize event, I have specified (hard coded) certain values to get a nice look, you can alter these values if you want.

C#
private void AnalogClock_Load(object sender, System.EventArgs e)
{
    dateTime=DateTime.Now;
    this.AnalogClock_Resize(sender,e);
}

private void AnalogClock_Resize(object sender, System.EventArgs e)
{
    this.Width = this.Height;
    this.fRadius = this.Height/2;
    this.fCenterX = this.ClientSize.Width/2;
    this.fCenterY = this.ClientSize.Height/2;
    this.fHourLength = (float)this.Height/3/1.65F;
    this.fMinLength = (float)this.Height/3/1.20F;
    this.fSecLength = (float)this.Height/3/1.15F;
    this.fHourThickness = (float)this.Height/100;
    this.fMinThickness = (float)this.Height/150;
    this.fSecThickness = (float)this.Height/200;
    this.fCenterCircleRadius = this.Height/50;
    this.Refresh();
}

Our dateTime variable should be immediately updated and the clock must be redrawn whenever our timer's Tick event is triggered. Double-click timer1 to go to the timer1_Tick event and add the following code:

C#
 private void timer1_Tick(object sender, System.EventArgs e)
 {
     this.dateTime=DateTime.Now;
     this.Refresh();
}

Let us write two functions, DrawLine() and DrawPolygon(), to draw clock hands of specified length, thickness and color, towards a point whose location is given as radians.

C#
private void DrawLine(float fThickness, float fLength, Color color,
       float fRadians, System.Windows.Forms.PaintEventArgs e)
{
    e.Graphics.DrawLine(new Pen( color, fThickness ),
    fCenterX - (float)(fLength/9*System.Math.Sin(fRadians)),
    fCenterY + (float)(fLength/9*System.Math.Cos(fRadians)),
    fCenterX + (float)(fLength*System.Math.Sin(fRadians)),
    fCenterY - (float)(fLength*System.Math.Cos(fRadians)));
}

private void DrawPolygon(float fThickness, float fLength, Color color,
                float fRadians, System.Windows.Forms.PaintEventArgs e)
{

    PointF A=new PointF( (float)(fCenterX+
             fThickness*2*System.Math.Sin(fRadians+PI/2)),
             (float)(fCenterY -
             fThickness*2*System.Math.Cos(fRadians+PI/2)) );
    PointF B=new PointF( (float)(fCenterX+
             fThickness*2*System.Math.Sin(fRadians-PI/2)),
            (float)(fCenterY -
            fThickness*2*System.Math.Cos(fRadians-PI/2)) );
    PointF C=new PointF( (float)(fCenterX+
             fLength*System.Math.Sin(fRadians)),
             (float) (fCenterY -
             fLength*System.Math.Cos(fRadians)) );
    PointF D=new PointF( (float)(fCenterX-
             fThickness*4*System.Math.Sin(fRadians)),
             (float)(fCenterY +
             fThickness*4*System.Math.Cos(fRadians) ));
    PointF[] points={A,D,B,C};
    e.Graphics.FillPolygon( new SolidBrush(color), points );
}

Now we include two more functions, Start() and Stop():

C#
public void Start()
{
        timer1.Enabled=true;
        this.Refresh();
}

public void Stop()
{
        timer1.Enabled=false;
}

Our control's Paint event will be actually drawing our clock, so we have to write the following code to our control's Paint event. This first converts the values of hour, minute and second into radians, and then calls DrawPolygon() and DrawLine() methods to draw the respective hands. It also draws Ticks for our clock.

C#
private void AnalogClock_Paint(object sender,
          System.Windows.Forms.PaintEventArgs e)
{
    float fRadHr=(dateTime.Hour%12+dateTime.Minute/60F) *30*PI/180;
    float fRadMin=(dateTime.Minute)*6*PI/180;
    float fRadSec=(dateTime.Second)*6*PI/180;

    DrawPolygon(this.fHourThickness,
          this.fHourLength, hrColor, fRadHr, e);
    DrawPolygon(this.fMinThickness,
          this.fMinLength, minColor, fRadMin, e);
    DrawLine(this.fSecThickness,
          this.fSecLength, secColor, fRadSec, e);


    for(int i=0;i<60;i++)
    {
        if ( this.bDraw5MinuteTicks==true && i%5==0 )
        // Draw 5 minute ticks
        {
            e.Graphics.DrawLine( new Pen( ticksColor, fTicksThickness ),
              fCenterX +
              (float)( this.fRadius/1.50F*System.Math.Sin(i*6*PI/180) ),
              fCenterY -
              (float)( this.fRadius/1.50F*System.Math.Cos(i*6*PI/180) ),
              fCenterX +
              (float)( this.fRadius/1.65F*System.Math.Sin(i*6*PI/180) ),
              fCenterY -
              (float)( this.fRadius/1.65F*System.Math.Cos(i*6*PI/180)) );
        }
        else if ( this.bDraw1MinuteTicks==true ) // draw 1 minute ticks
        {
            e.Graphics.DrawLine( new Pen( ticksColor, fTicksThickness ),
              fCenterX +
              (float) ( this.fRadius/1.50F*System.Math.Sin(i*6*PI/180) ),
              fCenterY -
              (float) ( this.fRadius/1.50F*System.Math.Cos(i*6*PI/180) ),
              fCenterX +
              (float) ( this.fRadius/1.55F*System.Math.Sin(i*6*PI/180) ),
              fCenterY -
              (float) ( this.fRadius/1.55F*System.Math.Cos(i*6*PI/180) ) );
        }
    }

    //draw circle at center
    e.Graphics.FillEllipse( new SolidBrush( circleColor ),
               fCenterX-fCenterCircleRadius/2,
               fCenterY-fCenterCircleRadius/2,
               fCenterCircleRadius, fCenterCircleRadius);
}

We are done with our clock control, now it's time to include some properties to our control so that the user can change its look.

C#
public Color HourHandColor
 {
     get { return this.hrColor; }
     set { this.hrColor=value; }
 }

 public Color MinuteHandColor
 {
     get { return this.minColor; }
     set { this.minColor=value; }
 }

 public Color SecondHandColor
 {
     get { return this.secColor; }
 set { this.secColor=value;
       this.circleColor=value; }
 }

 public Color TicksColor
 {
     get { return this.ticksColor; }
     set { this.ticksColor=value; }
 }

 public bool Draw1MinuteTicks
 {
     get { return this.bDraw1MinuteTicks; }
     set { this.bDraw1MinuteTicks=value; }
 }

 public bool Draw5MinuteTicks
 {
     get { return this.bDraw5MinuteTicks; }
     set { this.bDraw5MinuteTicks=value; }
 }

It's all done. Compile the control by building the solution. It will create "AnalogClock.dll". This is the file we'll use later on in other projects when we want to use this control.

Using the control

Once we have successfully build the solution, we can easily use our control in other projects. Here's the method: Select "Add/Remove Items.." from the toolbox popup menu and locate "AnalogClock.dll" using Browse button. Once the control is added to the toolbox, we can simply draw it on our forms and use it like other regular controls. Enjoy.

Image 4

History

  • Version 1.0.
    • Initial version. Needs your suggestions for improvement.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Pakistan Pakistan

Syed Mehroz Alam, living in Karachi, Pakistan, is a developer focusing Microsoft technologies. He has completed his bachelors as a Computer Systems Engineer in 2006 and is currently pursuing a Masters degree in Computer Science. He loves to learn, discover and master all aspects of .NET and SQL Server. Mehroz has developed rich internet enterprise applications using Silverlight in addition to the traditional ASP.NET and Windows Forms applications. He has worked with all three components of SQL Business Intelligence Studio: SSIS, SSRS and SSAS for developing BI Solutions and Data warehouse. He loves to write complex TSQL queries and evaluate his skills by participating in various TSQL Challenges. His blog can be viewed at http://smehrozalam.wordpress.com.


Comments and Discussions

 
Questioncode detecting system time for analog clock in turbo c++ Pin
Member 1082437716-May-14 18:52
Member 1082437716-May-14 18:52 
QuestionSecound Hand Pin
Pouya4725-Nov-12 7:44
Pouya4725-Nov-12 7:44 
GeneralSevere Memory Leaks... Pin
Wasia5-Apr-10 10:45
Wasia5-Apr-10 10:45 
QuestionRe: Severe Memory Leaks... Pin
xeiura0397422329-Mar-11 7:19
xeiura0397422329-Mar-11 7:19 
AnswerRe: Severe Memory Leaks... Pin
Kelanor16-Aug-12 3:25
Kelanor16-Aug-12 3:25 
AnswerRe: Severe Memory Leaks... Pin
RickZeeland11-Mar-13 9:40
mveRickZeeland11-Mar-13 9:40 
GeneralRe: Severe Memory Leaks... Pin
Terps Fan9-Sep-15 5:59
Terps Fan9-Sep-15 5:59 
Generaldouble buffer Pin
Harm Salomons28-Mar-10 6:22
Harm Salomons28-Mar-10 6:22 
GeneralRe: double buffer Pin
Syed Mehroz Alam4-Apr-10 19:58
Syed Mehroz Alam4-Apr-10 19:58 
GeneralRe: double buffer Pin
Terps Fan9-Sep-15 6:04
Terps Fan9-Sep-15 6:04 
GeneralRe: double buffer Pin
Harm Salomons9-Sep-15 11:43
Harm Salomons9-Sep-15 11:43 
Generalnice! thanks a lot Pin
Nitin S10-Dec-09 23:25
professionalNitin S10-Dec-09 23:25 
GeneralRe: nice! thanks a lot Pin
Syed Mehroz Alam15-Dec-09 2:59
Syed Mehroz Alam15-Dec-09 2:59 
Generaltanx Pin
nequee9-Jan-09 11:45
nequee9-Jan-09 11:45 
Generaltanx Pin
nequee9-Jan-09 11:40
nequee9-Jan-09 11:40 
GeneralRe: tanx Pin
Syed Mehroz Alam15-Dec-09 2:58
Syed Mehroz Alam15-Dec-09 2:58 
GeneralNice Work Pin
bilal haider21-Nov-08 3:57
bilal haider21-Nov-08 3:57 
GeneralRe: Nice Work Pin
Syed Mehroz Alam21-Nov-08 22:17
Syed Mehroz Alam21-Nov-08 22:17 
GeneralVERY helpful. Pin
Oxcarz10-Aug-08 13:25
Oxcarz10-Aug-08 13:25 
GeneralRe: VERY helpful. Pin
Syed Mehroz Alam10-Aug-08 19:06
Syed Mehroz Alam10-Aug-08 19:06 
GeneralDigital Clock Control Pin
mycsharpcorner3-Apr-07 4:27
mycsharpcorner3-Apr-07 4:27 
GeneralSimply of use Pin
amrelgarhy8127-Oct-06 16:13
amrelgarhy8127-Oct-06 16:13 
GeneralVB.Net Version Pin
Graeme_Grant9-Jul-05 18:20
mvaGraeme_Grant9-Jul-05 18:20 
GeneralDid you know Pin
Anonymous5-Mar-05 2:06
Anonymous5-Mar-05 2:06 
GeneralRe: Did you know Pin
tvcompass7-Mar-05 0:39
tvcompass7-Mar-05 0:39 

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.