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;
this.Size = bmpImage.Size;
}
To create flicker-free animation, you will need double-buffering. To achieve that:
public void EnableDoubleBuffering()
{
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...
- Set the origin of the form to center from left-top (so that the following rotation can take place)
- Save the current state
- Rotate the new graphics objects
- Draw the second-hand (or any hands) at the new origin and new orientation
- Restore the saved state
- Rotate the new graphics objects
- Draw the minute-hand (or any hands left) at the new origin and new orientation
- Reload the identity
- Reset the origin and orientation (restore the saved state if it seems failed)
- Rotate the new graphics objects
- 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)
{
e.Graphics.TranslateTransform(80.0F, 80.0F);
GraphicsState transState = e.Graphics.Save();
DateTime dtNow = DateTime.Now;
e.Graphics.RotateTransform(dtNow.Second * 6.0F - 90.0F);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillRectangle(new SolidBrush(Color.Silver), -1, -1, 55, 2);
e.Graphics.Restore(transState);
e.Graphics.RotateTransform(dtNow.Minute * 6.0F - 90.0F);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillRectangle(new SolidBrush(Color.Silver), -1, -1, 45, 3);
e.Graphics.ResetTransform();
e.Graphics.TranslateTransform(80.0F, 80.0F);
e.Graphics.RotateTransform(
dtNow.Hour * 30.0F - 90.0F + dtNow.Minute * 0.5F);
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