Click here to Skip to main content
15,890,690 members
Articles / Desktop Programming / Windows Forms
Article

Google Talk styled Windows Form

Rate me:
Please Sign up or sign in to vote.
4.57/5 (35 votes)
8 May 2006CPOL2 min read 224.3K   5.4K   179   66
How to use a custom Paint event handler to draw your own Windows Form.

Sample Image - GoogleTalkWindowsForm.gif

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.

C#
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.

C#
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.

C#
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.

C#
// Create a new Pen object
p = new Pen(this.OutlineColor, this.OutlineSize);

// Draw the form outline
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);

// Dispose the Pen object
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.

C#
// Create GraphicsPath to be used to crop the region required
gpRegion = new GraphicsPath();

// Loop through every pixel in the top left corner.
// Create a 1 x 1 rectangle regions of pixels
// that do not match the transparent color
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));
        }
    }
}

// Loop through every pixel in the top right corner.
// Create a 1 x 1 rectangle regions of pixels
// that do not match the transparent color
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));
        }
    }
}

// Create the remaining rectangular regions
// to complete cover all the windows form area
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));

// Apply region
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.

License

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


Written By
Web Developer
Malta Malta
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Generalgood ,excellent Pin
wdpnever27-Jul-06 23:16
wdpnever27-Jul-06 23:16 
QuestionHow to add a graphic Pin
realmontanakid10-Jul-06 3:21
realmontanakid10-Jul-06 3:21 
GeneralExecutable Pin
HakunaMatada21-Jun-06 1:48
HakunaMatada21-Jun-06 1:48 
GeneralRe: Executable Pin
User 226147424-Jun-06 9:17
User 226147424-Jun-06 9:17 
GeneralMissing functionalities Pin
gabis15-May-06 19:59
gabis15-May-06 19:59 
GeneralRe: Missing functionalities Pin
PunCha18-May-06 4:41
PunCha18-May-06 4:41 
GeneralIncompatbility problem Pin
ChrisFischer15-May-06 4:14
ChrisFischer15-May-06 4:14 
AnswerRe: Incompatbility problem Pin
NiaWs15-May-06 21:32
NiaWs15-May-06 21:32 
Hi chris,

I've download the MacOS styled button source code and had some chance to play around to identity the problem. The grey rectangle you've mentioned is the BackColor value of the parent form. There are 2 solutions to this problem :

Solution 1 (settings in the Google Talk Styled inherited form) :
Set BackColor property value = BodyBackColor property value
Set BodyStyle property value = Solid

Solution 2 (alterations in the MacOS styled buttons):
The following code can be added to the MacOS styled button so that it copies the graphics painted by the parent form underneath the button's area.

Add the following in the Member Variables region
C#
private Point curLocation;
private Size  curSize;
private Bitmap parentBitmap;


Add the following in the Implementation region
C#
protected virtual void DrawBackground(Graphics g )
{
	Graphics gfx = null;
	Rectangle rectParentForm;
	Rectangle rectSource;
	Rectangle rectDestination;

	if (isButtonStateChanged == true || this.parentBitmap == null)
	{
		if (this.parentBitmap != null)
			this.parentBitmap.Dispose();

		try
		{
			this.parentBitmap = new Bitmap(this.Parent.Width, this.Parent.Height);
			gfx = Graphics.FromImage(this.parentBitmap);
			rectParentForm = new Rectangle(0, 0, this.Parent.Width, this.Parent.Height);

			PaintEventArgs e = new PaintEventArgs(gfx, rectParentForm);
			this.InvokePaint(this.Parent, e);
		}
		finally
		{
			if (gfx != null)
			{
				gfx.Flush();
				gfx.Dispose();
			}
		}
	}

	rectSource = new Rectangle(this.Location, this.Size);
	rectDestination = new Rectangle(0, 0, this.Size.Width, this.Size.Height);
	g.DrawImage(this.parentBitmap, rectDestination, rectSource, GraphicsUnit.Pixel);
}

private bool isButtonStateChanged
{
	get
	{
		bool stateChanged = false;

		if (curLocation == Point.Empty)
			stateChanged = true;
		else if (curSize == Size.Empty)
			stateChanged = true;
		else if (this.curLocation != this.Location)
			stateChanged = true;
		else if (this.curSize != this.Size)
			stateChanged = true;

		if (stateChanged == true)
			saveButtonState();

		return stateChanged;
	}
}

private void saveButtonState()
{
	this.curLocation = this.Location;
	this.curSize = this.Size;
}


Modify the following in the Implementation region
C#
protected virtual void Draw( Graphics g )
{
	DrawBackground(g);
	DrawButton(g);
	DrawText(g);
}


I hope that helps to solve your problem. Thanks for trying out my custom control Smile | :)
GeneralQuestion Pin
kresso11-May-06 1:46
kresso11-May-06 1:46 
AnswerRe: Question Pin
NiaWs11-May-06 4:11
NiaWs11-May-06 4:11 
GeneralRe: Question Pin
kresso11-May-06 4:24
kresso11-May-06 4:24 
GeneralNice job, but... Pin
D_Guidi10-May-06 0:57
D_Guidi10-May-06 0:57 
GeneralRe: Nice job, but... Pin
NiaWs11-May-06 1:12
NiaWs11-May-06 1:12 
GeneralRe: Nice job, but... Pin
D_Guidi11-May-06 2:15
D_Guidi11-May-06 2:15 
GeneralRe: Nice job, but... Pin
D_Guidi11-May-06 3:32
D_Guidi11-May-06 3:32 
GeneralRe: Nice job, but... Pin
Nir Livne19-Aug-06 23:22
Nir Livne19-Aug-06 23:22 
GeneralCode tip Pin
AlrightyThen9-May-06 4:59
AlrightyThen9-May-06 4:59 
GeneralRe: Code tip Pin
NiaWs9-May-06 8:37
NiaWs9-May-06 8:37 
GeneralRe: Code tip Pin
Filip Duyck9-May-06 23:42
Filip Duyck9-May-06 23:42 
Generalnice graphical job Pin
paillave9-May-06 1:28
paillave9-May-06 1:28 
GeneralA couple more comments. Pin
Christopher Pearce9-May-06 3:40
Christopher Pearce9-May-06 3:40 
GeneralRe: A couple more comments. Pin
NiaWs9-May-06 11:18
NiaWs9-May-06 11:18 
GeneralRe: A couple more comments. Pin
Christopher Pearce9-May-06 22:39
Christopher Pearce9-May-06 22:39 
GeneralRe: A couple more comments. Pin
Christopher Pearce11-May-06 0:58
Christopher Pearce11-May-06 0:58 
GeneralRe: A couple more comments. Pin
NiaWs11-May-06 1:26
NiaWs11-May-06 1:26 

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.