Creating Bitmap Regions for Forms and Buttons






4.97/5 (65 votes)
Feb 9, 2004
2 min read

331499

12004
This article describes on how to create bitmap regions for WinForms and buttons.
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 control
public static void CreateControlRegion(Control control, Bitmap bitmap)
{
// Return if control and bitmap are null
if(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 here
if(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 here
else if(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.
private static 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 behind
if(bitmap.GetPixel(col, row) != colorTransparent)
{
// Opaque pixel found, mark current position
colOpaquePixel = col;
// Create another variable to set the current pixel position
int 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 reached
for(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 path
return 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.
public class Form1 : System.Windows.Forms.Form
{
// Load your bitmap for the form
private Bitmap bmpFrmBack = new Bitmap(typeof(Form1), "back.bmp");
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
// Make our bitmap region for the form
BitmapRegion.CreateControlRegion(this, bmpFrmBack);
}
}
Creating our bitmap region for Buttons
Creating bitmap region for buttons is the same for forms. Also, you need not do anything to the style of the button.
public class Form1 : System.Windows.Forms.Form
{
// Load your bitmap for the form
private Bitmap bmpFrmBack = new Bitmap(typeof(Form1), "back.bmp");
// Load your bitmap for the button
private Bitmap bmpBob = new Bitmap(typeof(Form1), "bob.bmp");
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
// Make our bitmap region for the form
BitmapRegion.CreateControlRegion(this, bmpFrmBack);
// Make our bitmap regions for the buttons
BitmapRegion.CreateControlRegion(button1, bmpBob);
}
}
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.
private void button1_MouseEnter(object sender, System.EventArgs e)
{
// Make bitmap region for button
BitmapRegion.CreateControlRegion(button1, bmpBobSay);
}
private void button1_MouseLeave(object sender, System.EventArgs e)
{
// Make bitmap region for button
BitmapRegion.CreateControlRegion(button1, bmpBob);
}
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.
private void Form1_MouseMove(object sender,
System.Windows.Forms.MouseEventArgs e)
{
// Check if dragging of the form has occurred
if(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 form
if(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 mean
if(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 flag
else
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.