Click here to Skip to main content
15,880,608 members
Articles / Multimedia / GDI+
Article

A Simple Analog Clock Widget

Rate me:
Please Sign up or sign in to vote.
4.48/5 (19 votes)
23 Oct 2007CPOL2 min read 72.1K   4.6K   68   7
Create a simple C# analog clock widget; you can do it too!
Simple C# widget and windows gadget side-by-side.

Introduction

While seeing the "Dashboard" for Mac OS and Yahoo's Konfabulator in action for so long, Microsoft finally came out with its own version of the gadget for Windows in its latest OS: Windows Vista. However, they are all in HTML and JavaScript. To test what I have learned from C# programming so far, I also came out with a C# version of the Windows gadget. I think it's quite challenging for a beginner because it requires a simple background in math, image processing and 3D graphics programming.

Using the Code

First, we need a background image for our widget, to let it have a professional look. You can set it when the form is loading. To prevent the image from being lost, I embedded the image into an application. What this code does is grab the left-top pixel and set the rest of the pixels to invisible when they meet the same color in another part of the same bitmap. Then it sets it to background and sets the form to fit the image size.

C#
private void SetFormBackgroundImage(Bitmap bmpImage)
{
    Color clrPixel = bmpImage.GetPixel(0, 0);
    bmpImage.MakeTransparent(clrPixel);
    this.BackgroundImage = bmpImage;
    // Set the form size from image size
    this.Size = bmpImage.Size;
}

To create flicker-free animation, you will need double-buffering. To achieve that:

C#
public void EnableDoubleBuffering()
{
    // Set the value of the double-buffering style bits to true.
    this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint |
        ControlStyles.AllPaintingInWmPaint, true);
    this.UpdateStyles();
}

To capture the mouse-down event, you need to store the mouse's point when it's down and offset it when the mouse moves.

C#
private void frmIrregular_MouseDown(object sender, MouseEventArgs e)
{
    ptMouseOffset = new Point(-e.X, -e.Y);
}

private void frmIrregular_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        Point ptCurrMousePos = Control.MousePosition;
        ptCurrMousePos.Offset(ptMouseOffset.X, ptMouseOffset.Y);
        this.Location = ptCurrMousePos;
    }
}

Now comes the important part of this widget: drawing the hour-hand, minute-hand and second-hand. The basic theory is...

  1. Set the origin of the form to center from left-top (so that the following rotation can take place)
  2. Save the current state
  3. Rotate the new graphics objects
  4. Draw the second-hand (or any hands) at the new origin and new orientation
  5. Restore the saved state
  6. Rotate the new graphics objects
  7. Draw the minute-hand (or any hands left) at the new origin and new orientation
  8. Reload the identity
  9. Reset the origin and orientation (restore the saved state if it seems failed)
  10. Rotate the new graphics objects
  11. Draw the hour-hand (or any hands left) at the new origin and new orientation

Translating these steps into codings:

C#
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
    // Set the origin to center of the form
    e.Graphics.TranslateTransform(80.0F, 80.0F);

    // Save translated graphics state; So origin 
    // will remain at center of form when restore
    GraphicsState transState = e.Graphics.Save();

    // Capture a copy of current time for consistent
    DateTime dtNow = DateTime.Now;

    // rotation starts from new center of the form
    e.Graphics.RotateTransform(dtNow.Second * 6.0F - 90.0F);
    // Anti-alias only affect the next shape
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    // draw the second hand at new center of the form
    e.Graphics.FillRectangle(new SolidBrush(Color.Silver), -1, -1, 55, 2);

    //// Restore graphics state to translated state and fill second hand
    e.Graphics.Restore(transState);

    // minus 90 degree because start at x-axis
    e.Graphics.RotateTransform(dtNow.Minute * 6.0F - 90.0F);
    // Anti-alias only affect the next shape
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    e.Graphics.FillRectangle(new SolidBrush(Color.Silver), -1, -1, 45, 3);

    //// Restore graphics state to translated state and fill minute hand
    //gHands.Restore(transState);
    // Reset transformation matrix to identity and fill rectangle.
    e.Graphics.ResetTransform();
    // Set the origin to center of the form
    e.Graphics.TranslateTransform(80.0F, 80.0F);

    // minus 90 degree because start at x-axis; Minute affects hour hand too
    e.Graphics.RotateTransform(
        dtNow.Hour * 30.0F - 90.0F + dtNow.Minute * 0.5F);
    // Anti-alias only affect the next shape
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    e.Graphics.FillRectangle(new SolidBrush(Color.Silver), -1, -1, 35, 4);
}

By forcing the form to re-paint at every second through the help of the timer: voila! I think I have presented 90% of my coding here; simple huh? You can get it from my blog too.

Version

  • 2007-09-26: First release

License

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


Written By
Software Developer
Malaysia Malaysia
My blog: http://start-coding.blogspot.com/

Comments and Discussions

 
QuestionGreat little utility - Context Menu for exiting? Pin
Tim8w7-Jan-21 14:46
Tim8w7-Jan-21 14:46 
Generalgood work, thank you! Pin
Yifeng Ding18-Dec-07 21:56
Yifeng Ding18-Dec-07 21:56 
JokeRe: good work, thank you! Pin
gan.gary24-Dec-07 4:03
gan.gary24-Dec-07 4:03 
Generalvery good! [modified] Pin
Tefik Becirovic12-Nov-07 23:57
Tefik Becirovic12-Nov-07 23:57 
GeneralRe: very good! Pin
gan.gary13-Nov-07 13:46
gan.gary13-Nov-07 13:46 
Generalgreat article Pin
lbc4-Nov-07 7:00
lbc4-Nov-07 7:00 
GeneralRe: great article Pin
gan.gary4-Nov-07 13:51
gan.gary4-Nov-07 13:51 

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.