Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating Bitmap Regions for Forms and Buttons

0.00/5 (No votes)
8 Feb 2004 1  
This article describes on how to create bitmap regions for WinForms and buttons.

Sample Image - bmprgnform.jpg

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.

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.

A list of licenses authors might use can be found here