Introduction

This article shows you how to create bitmap regions for forms and buttons. I think there are several articles on this for MFC and this one uses a similiar technique, Regions. Beside using this technique on forms, it also allows you to do that to Buttons, with hovering effect(i.e. Change another image and/or region when mouse hovering on the button) without the need to create a custom control.

Main Functions Overview

Below are the codes for the 2 main functions used to create your bitmap regions. They are located in BitmapRegion.cs.

// Create and apply the given bitmap region on the supplied controlpublicstaticvoid CreateControlRegion(Control control, Bitmap bitmap)
{
// Return if control and bitmap are nullif(control == null || bitmap == null)
return;
// Set our control's size to be the same as the bitmap
control.Width = bitmap.Width;
control.Height = bitmap.Height;
// Check if we are dealing with Form hereif(control is System.Windows.Forms.Form)
{
// Cast to a Form object
Form form = (Form)control;
// Set our form's size to be a little larger that the bitmap just // in case the form's border style is not set to none in the first // place
form.Width += 15;
form.Height += 35;
// No border
form.FormBorderStyle = FormBorderStyle.None;
// Set bitmap as the background image
form.BackgroundImage = bitmap;
// Calculate the graphics path based on the bitmap supplied
GraphicsPath graphicsPath = CalculateControlGraphicsPath(bitmap);
// Apply new region
form.Region = new Region(graphicsPath);
}
// Check if we are dealing with Button hereelseif(control is System.Windows.Forms.Button)
{
// Cast to a button object
Button button = (Button)control;
// Do not show button text
button.Text = "";
// Change cursor to hand when over button
button.Cursor = Cursors.Hand;
// Set background image of button
button.BackgroundImage = bitmap;
// Calculate the graphics path based on the bitmap supplied
GraphicsPath graphicsPath = CalculateControlGraphicsPath(bitmap);
// Apply new region
button.Region = new Region(graphicsPath);
}
}
// Calculate the graphics path that representing the figure in the bitmap // excluding the transparent color which is the top left pixel.privatestatic GraphicsPath CalculateControlGraphicsPath(Bitmap bitmap)
{
// Create GraphicsPath for our bitmap calculation
GraphicsPath graphicsPath = new GraphicsPath();
// Use the top left pixel as our transparent color
Color colorTransparent = bitmap.GetPixel(0, 0);
// This is to store the column value where an opaque pixel is first found.// This value will determine where we start scanning for trailing // opaque pixels.int colOpaquePixel = 0;
// Go through all rows (Y axis)for(int row = 0; row < bitmap.Height; row ++)
{
// Reset value
colOpaquePixel = 0;
// Go through all columns (X axis)for(int col = 0; col < bitmap.Width; col ++)
{
// If this is an opaque pixel, mark it and search // for anymore trailing behindif(bitmap.GetPixel(col, row) != colorTransparent)
{
// Opaque pixel found, mark current position
colOpaquePixel = col;
// Create another variable to set the current pixel positionint colNext = col;
// Starting from current found opaque pixel, search for // anymore opaque pixels trailing behind, until a transparent// pixel is found or minimum width is reachedfor(colNext=colOpaquePixel; colNext<bitmap.Width; colNext++)
if(bitmap.GetPixel(colNext, row) == colorTransparent)
break;
// Form a rectangle for line of opaque pixels found and // add it to our graphics path
graphicsPath.AddRectangle(new Rectangle(colOpaquePixel,
row, colNext - colOpaquePixel, 1));
// No need to scan the line of opaque pixels just found
col = colNext;
}
}
}
// Return calculated graphics pathreturn graphicsPath;
}

Creating our bitmap region for Forms

To create the bitmap region for our form, you only need these 2 lines of code. Note that you need not explicitly change the form's border to none, as it will be done for you.

Now if you want to change the bitmap of the button when mouse is over it, I am glad to say that you need not create a custom button control for that. You can just simply handle the MouseLeave and MouseEnter events as shown below.

Dragging the Form

Since the form now doesn't have a title bar, we need to make it such that the user is able to left click anywhere in the form and drag it. The solution used for the MFC version is to handle WM_LBUTTONDOWN and doing a PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x,point.y)) to fool windows into thinking that we clicked on the caption. However, I couldn't get this to work with Window Forms, so the code below is an alternative solution.

privatevoid Form1_MouseMove(object sender,
System.Windows.Forms.MouseEventArgs e)
{
// Check if dragging of the form has occurredif(e.Button == MouseButtons.Left)
{
// If this is the first mouse move event for left click dragging // of the form, store the current point clicked so that we can use // it to calculate the form's new location in subsequent mouse move // events due to left click dragging of the formif(isFirst == true)
{
// Store previous left click position
prevLeftClick = new Point(e.X, e.Y);
// Subsequent mouse move events will not be treated as first time,// until the left mouse click is released or other mouse click // occur
isFirst = false;
}
// On subsequent mouse move events with left mouse click down. // (i.e. During dragging of form)else
{
// This flag here is to do alternate processing for the form // dragging because it causes serious flicking when u allow // every such events to change the form's location.// You can try commenting this out to see what i meanif(toBlock == false)
this.Location = new Point(this.Location.X + e.X -
prevLeftClick.X, this.Location.Y + e.Y - prevLeftClick.Y);
// Store new previous left click position
prevLeftClick = new Point(e.X, e.Y);
// Allow or deny next mouse move dragging event
toBlock = !toBlock;
}
}
// This is a new mouse move event so reset flagelse
isFirst = true;
}

Issues to take note

There is a problem with the focus rect drawn on the button when it receives focus. I am unable to disable this focus rect. As a result, I have made my bitmaps slightly larger so that the focus rect ends up being drawn outside the region of the button. Take a look at the figure below.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Hi
I wanted to know if you could tell me of a sureshot way of how to resize the form because when you use image maps you loose a lot of the functionality of the standard windows form.
I want to be able to resize the form with the mouse by dragging on its boarders also a button to resize the form to the maximum window size and then minimize it.
If you can tell me all of this I'd be glad.

In no way at all am I trying to put what you did down, but isn't this what XAML and WPF were designed for? So we could get the graphics controls we want without using bitmaps, but vector art? It's a great article, I just think that XAML does away with programatic graphics controls. This would be great for an App that requirements say that it must run on a platform not supported by 3.0.

Since the form now doesn't have a title bar, we need to make it such that the user is able to left click anywhere in the form and drag it. The solution used for the MFC version is to handle WM_LBUTTONDOWN and doing a PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x,point.y)) to fool windows into thinking that we clicked on the caption.

Not good - better way is to handle the WM_NCHITTEST message and return HTCAPTION!

it's Very nice work ..., Exciting,
just i have some little points ,
1) it dosent work perfict in VS2005 ,
like the "Hi, I'm pop "bubble it flash then disable part ..
while the action on your demo set it visable until next click,
2) while draging over the form .., the mouse moves from it's orgenal place on the form where i started to drag ..,
lets say i want to mouse down on the Middle of the form then drag righ and left ... then see the mouse and the form .. i see the mouse on the far right on the screen and the form in the far left

3)while lettle drag .. the processor usage increase very fast,
4) while drage it always flicker the screen , it seems it re drow all the screen again ...,

I really liked the idea but it was a little slow when you resize a form and the function had to re-do the form region. I was wonder why you did not use scanlines to do this, and I had never used scanlines in C#. It is a little different to what I was used to in C++ but it gets the job done.

I thought this might help someone, This site has helped me alot...

If any one finds a better (faster) way of doing this please let me know.