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.
public static void CreateControlRegion(Control control, Bitmap bitmap)
{
if(control == null || bitmap == null)
return;
control.Width = bitmap.Width;
control.Height = bitmap.Height;
if(control is System.Windows.Forms.Form)
{
Form form = (Form)control;
form.Width += 15;
form.Height += 35;
form.FormBorderStyle = FormBorderStyle.None;
form.BackgroundImage = bitmap;
GraphicsPath graphicsPath = CalculateControlGraphicsPath(bitmap);
form.Region = new Region(graphicsPath);
}
else if(control is System.Windows.Forms.Button)
{
Button button = (Button)control;
button.Text = "";
button.Cursor = Cursors.Hand;
button.BackgroundImage = bitmap;
GraphicsPath graphicsPath = CalculateControlGraphicsPath(bitmap);
button.Region = new Region(graphicsPath);
}
}
private static GraphicsPath CalculateControlGraphicsPath(Bitmap bitmap)
{
GraphicsPath graphicsPath = new GraphicsPath();
Color colorTransparent = bitmap.GetPixel(0, 0);
int colOpaquePixel = 0;
for(int row = 0; row < bitmap.Height; row ++)
{
colOpaquePixel = 0;
for(int col = 0; col < bitmap.Width; col ++)
{
if(bitmap.GetPixel(col, row) != colorTransparent)
{
colOpaquePixel = col;
int colNext = col;
for(colNext=colOpaquePixel; colNext<bitmap.Width; colNext++)
if(bitmap.GetPixel(colNext, row) == colorTransparent)
break;
graphicsPath.AddRectangle(new Rectangle(colOpaquePixel,
row, colNext - colOpaquePixel, 1));
col = colNext;
}
}
}
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
{
private Bitmap bmpFrmBack = new Bitmap(typeof(Form1), "back.bmp");
public Form1()
{
InitializeComponent();
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
{
private Bitmap bmpFrmBack = new Bitmap(typeof(Form1), "back.bmp");
private Bitmap bmpBob = new Bitmap(typeof(Form1), "bob.bmp");
public Form1()
{
InitializeComponent();
BitmapRegion.CreateControlRegion(this, bmpFrmBack);
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)
{
BitmapRegion.CreateControlRegion(button1, bmpBobSay);
}
private void button1_MouseLeave(object sender, System.EventArgs e)
{
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)
{
if(e.Button == MouseButtons.Left)
{
if(isFirst == true)
{
prevLeftClick = new Point(e.X, e.Y);
isFirst = false;
}
else
{
if(toBlock == false)
this.Location = new Point(this.Location.X + e.X -
prevLeftClick.X, this.Location.Y + e.Y - prevLeftClick.Y);
prevLeftClick = new Point(e.X, e.Y);
toBlock = !toBlock;
}
}
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.