Introduction
This article mainly explains how to use a custom Paint event handler to draw your own Windows control (in this case, a Form control). The GoogleTalkForm
class inherits and extends the System.Windows.Forms.Form
control (provided by the Microsoft .NET Framework) to provide the look and feel of the Google Talk Windows Form.
The class properties IsWindowSnappable
, IsResizable
, ResizableColor
, TitleColor
, TitleFont
, TitleBackColor
, TitleForeColor
, TitleStyle
, BodyBackColor
, BodyForeColor
, BodyStyle
, OutlineColor
, OutlineSize
, IconsNormalColor
, IconsHiLiteColor
, MinimumHeight
, and MinimumWidth
can be used to alter the form's standard look and feel, and behaviour.
The GoogleTalkForm
class provides:
- Drawing of the Windows Form.
- Drawing using double buffering.
- Form snapping to specific client desktop regions.
- Mouse event handling.
- System context menu item drawing.
How it works
First of all, we need to override the form events such as OnPaint
, OnMouseDown
, OnMouseUp
, and OnMouseMove
, OnDoubleClick
to handle the Form Paint event and mouse events.
protected override void OnPaint(PaintEventArgs e)
{
...
}
protected override void OnMouseDown(MouseEventArgs e)
{
...
}
The Form's style must be set using the method SetStyle
in the GoogleTalkForm
class constructor, to reflect the required behaviour.
this.SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.Selectable, true);
this.SetStyle(ControlStyles.StandardClick, true);
this.SetStyle(ControlStyles.StandardDoubleClick, true);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, false);
this.UpdateStyles();
The method isMousePointerInArea
is used to check if the current mouse position is within a specific client rectangle area. The mouse location with respect to the Windows Form has to be worked out because the Control.MousePosition
returns the mouse position relative to the desktop.
private bool isMousePointerInArea(Point mousePosition, Rectangle area)
{
Point relativePoint = new Point(0, 0);
relativePoint.X = mousePosition.X - this.Location.X;
relativePoint.Y = mousePosition.Y - this.Location.Y;
return area.Contains(relativePoint);
}
The custom Form painting is all done by the method OnFormPaint
onto a newly created Bitmap
object having the exact width and height of the form. A Graphics
object is created from the Bitmap
, and methods such as DrawString
, DrawImage
, DrawLine
, and DrawArc
are used. As soon as all painting is complete, the buffered bitmap is copied onto the form graphics instance, using the method DrawImageUnscaled
.
p = new Pen(this.OutlineColor, this.OutlineSize);
g.DrawArc(p, rectLeftCorner, 180, 90);
g.DrawArc(p, rectRightCorner, 270, 90);
g.DrawLine(p, edgeRadius, 0, this.Width - edgeRadius, 0);
g.DrawLine(p, 0, edgeRadius, 0, this.Height);
g.DrawLine(p, this.Width - 1, edgeRadius,
this.Width - 1, this.Height);
g.DrawLine(p, 0, this.Height - 1,
this.Width, this.Height - 1);
p.Dispose();
p = null;
A custom Region
is created and applied to the form, to make a rounded edge effect. To create the required region, we have to iterate pixel by pixel and add a 1x1 rectangle to the GraphicsPath
object (that will be used to create the Region
object) when the currently selected pixel is not the same color as the transparent color. To optimise the code, only the top corners of the form are checked for transparent colored pixels.
gpRegion = new GraphicsPath();
for (int x = rectLeftCorner.X; x < rectLeftCorner.Width; x++)
{
for (int y = rectLeftCorner.Y; y < rectLeftCorner.Height / 2; y++)
{
if (isSameColor(bmp.GetPixel(x, y),
this.transparentColor) == false)
{
gpRegion.AddRectangle(new Rectangle(x, y, 1, 1));
}
}
}
for (int x = rectRightCorner.X + 1; x <
rectRightCorner.X +
rectRightCorner.Width + 1; x++)
{
for (int y = rectRightCorner.Y; y <
rectRightCorner.Y + rectRightCorner.Height / 2; y++)
{
if (isSameColor(bmp.GetPixel(x, y),
this.transparentColor) == false)
{
gpRegion.AddRectangle(new Rectangle(x, y, 1, 1));
}
}
}
gpRegion.AddRectangle(new Rectangle(rectLeftCorner.Width, 0,
this.Width - (edgeRadius * 4), rectLeftCorner.Height / 2));
gpRegion.AddRectangle(new Rectangle(0,
rectLeftCorner.Height / 2, this.Width, this.Height));
this.Region = new Region(gpRegion);
History
- 1.0 - First release - (09 May 2006).
- 1.1 - (11 May 2006).
- Created a form designer to control the form (and child controls) behaviour.
- Fixed the form behaviour when hosted inside an MDI container.