Click here to Skip to main content
Click here to Skip to main content
Go to top

Creating Bitmap Regions for Forms and Buttons

, 8 Feb 2004
Rate this:
Please Sign up or sign in to vote.
This article describes on how to create bitmap regions for WinForms and buttons.
<!-- Article image -->

Sample Image - bmprgnform.jpg

<!-- Add the rest of your HTML here -->

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

Share

About the Author

Weiye Chen
Other
Singapore Singapore
No Biography provided

Comments and Discussions

 
QuestionColor ? Pinmemberwwardaww12-Aug-14 23:06 
GeneralThanks!!!!! PinmemberMember 157966222-Oct-10 6:45 
GeneralForm Resize Issue Pinmembercooldude28-Apr-08 20:40 
QuestionGreat but Slow PinmemberMember 46198954-Apr-08 22:08 
GeneralRe: Great but Slow PinmemberNick Alexeev6-Feb-11 8:47 
QuestionI have Prolem ...??? Pinmembertaixiu16-Jan-08 23:03 
AnswerRe: I have Prolem ...??? Pinmemberhitechntv29-Dec-08 22:24 
QuestionIsn't this what XAML was designed for? PinmemberJohn Wolfe12-Jan-07 10:00 
AnswerRe: Isn't this what XAML was designed for? PinmemberHeywood31-Aug-07 5:36 
GeneralMuch faster routine using LocksBit with Marshal Pinmemberbcodebcode10-Jan-07 17:09 
GeneralRe: Much faster routine using LocksBit with Marshal PinmemberGaryJForeman9-Apr-07 8:29 
GeneralRe: Much faster routine using LocksBit with Marshal PinmemberGaryJForeman9-Apr-07 8:34 
GeneralRe: Much faster routine using LocksBit with Marshal Pinmemberbcodebcode12-Apr-07 10:32 
QuestionMouse Move Problem Pinmemberaslim_sahal2-Nov-06 4:25 
GeneralForm dragging PinmemberSergei Prognimak11-Sep-06 7:22 
AnswerRe: Form dragging Pinmemberqwerty@sh25-Jan-07 5:06 
Generalbut .... PinmembertareqGamal10-Aug-06 3:00 
GeneralGood code PinmemberQuaKx11-Jan-06 2:55 
GeneralCorrection to W2000 shifting issue Pinmemberbouzard30-Nov-05 22:22 
GeneralRe: Correction to W2000 shifting issue Pinmembermrmubi24-Dec-05 1:30 
QuestionRe: Correction to W2000 shifting issue PinmemberFrank Meng7-Nov-07 7:41 
GeneralGreat work! - I have made it faster for you Pinmemberspirit1238-Nov-05 5:54 
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.
 

//==========================================================================
private static GraphicsPath QuickCalculateGraphicsPath(Bitmap bitmap, Color TransparentColor)
{
GraphicsPath graphicsPath = new GraphicsPath();
int StartRegionArea = -1;
Color PixelColor = Color.Empty;
BitmapData bData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
IntPtr ScanL = bData.Scan0;
int YOffset = bData.Stride - bitmap.Width * 3;
unsafe
{
byte* p = (byte*)(void*)ScanL;
for (int y = 0; y <= bitmap.Height-1; y++)
{
for (int x = 0; x <= bitmap.Width-1; x++)
{
int B = (int)p[0];
int G = (int)p[1];
int R = (int)p[2];
PixelColor = Color.FromArgb(R, G, B);
if (PixelColor == TransparentColor && StartRegi>
GeneralRe: Great work! - I have made it faster for you PinmemberMiguel Lopes13-Feb-06 3:46 
GeneralRe: Great work! - I have made it faster for you Pinmembermotasus7-Oct-06 0:13 
GeneralRe: Great work! - I have made it faster for you PinmemberFreewareFire14-Oct-06 16:40 
GeneralRe: Great work! - I have made it faster for you PinmemberGee66630-May-07 23:47 
GeneralRe: Great work! - I have made it faster for you Pinmembertam2405793-Sep-07 20:13 
GeneralRe: Great work! - I have made it faster for you PinmemberGee6664-Sep-07 23:06 
GeneralRe: Great work! - I have made it faster for you Pinmembernarfman175-Feb-09 2:12 
Generaluse transparent (alpha) pics Pinmembertr425-Jul-05 1:40 
GeneralError with custom bmp's PinsussRushi Vishavadia4-Jul-05 22:13 
GeneralRe: Error with custom bmp's PinmemberRushi Vishavadia4-Jul-05 22:16 
GeneralRe: Error with custom bmp's PinmemberWeiye Chen5-Jul-05 2:27 
GeneralRe: Error with custom bmp's PinmemberRushi Vishavadia5-Jul-05 2:39 
GeneralRe: Error with custom bmp's Pinmembereastrino14-Jul-10 21:10 
GeneralSome problems on lower resolutions... PinmemberDarth_Sulfur1-Jul-05 20:00 
GeneralRe: Some problems on lower resolutions... PinmemberDarth_Sulfur1-Jul-05 20:18 
GeneralRe: Some problems on lower resolutions... PinmemberWeiye Chen3-Jul-05 2:05 
GeneralRe: Some problems on lower resolutions... PinmemberDarth_Sulfur3-Jul-05 12:53 
GeneralRe: Some problems on lower resolutions... PinmemberWeiye Chen4-Jul-05 19:12 
GeneralRe: Some problems on lower resolutions... PinmemberDarth_Sulfur5-Jul-05 15:11 
GeneralRe: Some problems on lower resolutions... PinmemberWeiye Chen5-Jul-05 18:49 
GeneralRe: Some problems on lower resolutions... PinmemberDarth_Sulfur6-Jul-05 14:02 
GeneralRe: Some problems on lower resolutions... Pinmembercharchabil0330-Jan-07 23:33 
Generalon window 2003 sever and XP Pinsussvbman122520-Jun-05 23:23 
GeneralRe: on window 2003 sever and XP PinmemberWeiye Chen21-Jun-05 1:47 
GeneralRe: on window 2003 sever and XP PinmemberDarth_Sulfur1-Jul-05 20:55 
Generalon win2003 Pinmemberpansiom21-Mar-05 6:52 
Generalregion shifted on 2k Pinmemberjirong zhou19-Sep-04 18:58 
GeneralRe: region shifted on 2k PinmemberWeiye Chen20-Sep-04 2:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140922.1 | Last Updated 9 Feb 2004
Article Copyright 2004 by Weiye Chen
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid