Click here to Skip to main content
Licence CPOL
First Posted 23 Oct 2007
Views 39,858
Downloads 1,895
Bookmarked 62 times

A Simple Analog Clock Widget

By | 23 Oct 2007 | Article
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.

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:

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.

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:

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)

About the Author

gan.gary

Software Developer

Malaysia Malaysia

Member

I am a software developer, deal with MS windows apps on daily work. I enjoy graphics programming and mobile programming, but daily work consumes most of my time :(

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
Generalgood work, thank you! PinmemberYifeng Ding21:56 18 Dec '07  
JokeRe: good work, thank you! Pinmembergan.gary4:03 24 Dec '07  
Generalvery good! [modified] PinmemberTefik Becirovic23:57 12 Nov '07  
GeneralRe: very good! Pinmembergan.gary13:46 13 Nov '07  
Generalgreat article Pinmemberlbc7:00 4 Nov '07  
GeneralRe: great article Pinmembergan.gary13:51 4 Nov '07  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    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 | Mobile
Web02 | 2.5.120517.1 | Last Updated 23 Oct 2007
Article Copyright 2007 by gan.gary
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid