Click here to Skip to main content
15,867,871 members
Articles / Multimedia / GDI+
Article

Transparent Bitmap Buttons with Alpha-Blended Regions in .NET

Rate me:
Please Sign up or sign in to vote.
4.73/5 (36 votes)
12 Aug 2004CPOL4 min read 273K   3.1K   158   28
How to create and draw transparent bitmap buttons in .NET Forms.

Sample Image - TransButtonNetDemo.jpg

Introduction

Since I created alpha-blended bitmap buttons for MFC, I thought I would try the same thing in a .NET Windows Forms environment. Even though the concepts behind the coding were very similar, I found implementing the details to be a bit more challenging. I therefore thought it might benefit others to learn some of the pitfalls and workarounds that are required to make this work.

I did not create a very sophisticated demo project for this example. There are simply a number of buttons on the bitmapped background window for display examples, and only the refresh buttons and exit button actually do something. The refresh buttons will change the background bitmap (2 in all) so the transparent buttons have another look.

Creating the Bitmaps

The bitmaps for this button must contain four images, laid out horizontally within the bitmap. The four states represent Normal, Selected, Hover, and Disable, in that order. The four images must be the same size. I used 48x48 round buttons and 75x48 pill buttons for this demo.

The button bitmaps are 32-bit-per-pixel (32bpp) RGB images with an alpha channel that determines transparency. I created these images in Photoshop a bit different than the MFC button images. The tricky part is the alpha channel, because Photoshop is not always very clear about how to get this just right. The steps I took were to first create a new alpha channel by selecting the Channels tab in the Layers-Channels-Paths window, and then select the tiny "Create New Channel" button at the bottom. Once the Alpha Channel is created, I double-clicked this channel to open the Options dialog, and then changed the Opacity to 0% with a Black or White Color and a Masked Area selection. Next, I turned off the RGB channel views (deselect the little eye next to each one), and left the Alpha channel visible. The display should be either black or white at this point depending on the mask color. I then selected the RGB channels by using Ctrl-click on the RGB channel. This should select part or all of the actual button layers. Finally, I painted the opaque areas with a black or white brush to get the level of transparency I want. The display usually looks something like this:

Photoshop Alpha Channel painting

The images must be saved as 32-bit ARGB, so the Alpha layer must be preserved in the Save dialog, and remember that there can only be one Alpha layer.

Loading the Bitmaps from a Resource DLL

One of the most frustrating things I learned about alpha channel bitmaps in GDI+ is that they don't load properly. No matter what I tried, the alpha-channel was always lost, and the bitmaps were converted back to 32bppRGB. I finally found a solution to this problem when I stumbled across a demo by Steve McMahon on vbaccelerator.com. Steve's demo provided a good workaround by loading the bitmaps from a resource DLL and doing some manipulation to keep the alpha data in tact.

C#
public static Bitmap DibToBitmap(
    IntPtr hDib
    )
{
    BITMAP tBM = new BITMAP();
    GetObjectBitmap(hDib, Marshal.SizeOf(tBM), ref tBM);
    Bitmap bm = new Bitmap(tBM.bmWidth, tBM.bmHeight);

    // set the bitmap's data to the data from
    // the DIB:
    if (tBM.bmBitsPixel == 32)
    {
        // Bizarre but true: you *must* clone the newly created
        // bitmap to get one with the correct pixel format, even
        // if you attempted to create the original one with the 
        // correct format...
        bm = bm.Clone(new Rectangle(0, 0, tBM.bmWidth, tBM.bmHeight),
            PixelFormat.Format32bppArgb);

        // Lock the bitmap bits
        BitmapData destData = bm.LockBits(
            new Rectangle(0, 0, bm.Width, bm.Height),
            ImageLockMode.ReadWrite,
            PixelFormat.Format32bppArgb);
        int destWidth = destData.Stride;
        IntPtr destScan0 = destData.Scan0;

        unsafe
        {
            byte * pDest = (byte *) (void *) destScan0;
            // The DIB is upside down compared to a GDI+ bitmap
            pDest += ((bm.Width * 4) * (bm.Height - 1));
            byte * pSrc = (byte *) (void *) tBM.bmBits;

            for (int y = 0; y < bm.Height; ++y)
            {
                for (int x = 0; x < bm.Width; ++x)
                {
                    pDest[0] = pSrc[0]; // blue
                    pDest[1] = pSrc[1]; // green
                    pDest[2] = pSrc[2]; // red
                    pDest[3] = pSrc[3]; // alpha
                    
                    // Move to next BGRA
                    pDest += 4;
                    pSrc += 4;
                }
                pDest -= (bm.Width * 8);
            }
        }

        bm.UnlockBits(destData);
    }
    else
    {
        // Easier to just copy src -> dst using GDI.

        // Put the DIB into a DC:
        IntPtr hWndDesktop = GetDesktopWindow();
        IntPtr hDCComp = GetDC(hWndDesktop);
        IntPtr hDCSrc = CreateCompatibleDC(hDCComp);
        ReleaseDC(hWndDesktop, hDCComp);
        IntPtr hBmpOld = SelectObject(hDCSrc, hDib);

        Graphics gfx = Graphics.FromImage(bm);
        IntPtr hDCDest = gfx.GetHdc();
        BitBlt(hDCDest, 0, 0, tBM.bmWidth, tBM.bmHeight, hDCSrc, 0, 0, SRCCOPY);
        gfx.ReleaseHdc(hDCDest);

        SelectObject(hDCSrc, hBmpOld);
        DeleteDC(hDCSrc);
    }

    return bm;

}

Using Steve's idea, I created an MFC DLL, loaded it with all the 32bppARGB bitmaps, and used his ResourceLibrary and ImageUtility classes to create GDI+ Bitmap objects.

C#
// Resource file created as MFC dll to store 32bpp
// alpha channel bitmaps.
string ResourceFileName = "TransButtonResources.dll";

if (File.Exists(ResourceFileName))
{
    using (ResourceLibrary lib = new ResourceLibrary())
    {
        lib.Filename = ResourceFileName;
        if (!lib.Handle.Equals(IntPtr.Zero))
        {
            // Load bitmaps from resource file with specified ids
            bgImage1 = LoadBitmapResource(lib, 2000);
            bgImage2 = LoadBitmapResource(lib, 2006);
            purpleButton  = LoadBitmapResource(lib, 2001);
            redButton = LoadBitmapResource(lib, 2002);
            whiteButton = LoadBitmapResource(lib, 2003);
            openButton = LoadBitmapResource(lib, 2004);
            saveButton = LoadBitmapResource(lib, 2005);
        }
    }
}

The Button Class

My TransButton class here is nothing very fancy. I actually borrowed the control state code from the XP button example. I added functions to set the bitmap and region, and painted the correct image from a horizontal offset within the 4-state bitmap.

C#
if (ButtonImages != null)
{
    Rectangle destRect = new Rectangle( 0, 0,
        pea.ClipRectangle.Width, pea.ClipRectangle.Height);
    Rectangle srcRect = new Rectangle( xOffset, yOffset,
        pea.ClipRectangle.Width, pea.ClipRectangle.Height);
    GraphicsUnit units = GraphicsUnit.Pixel;
    pea.Graphics.DrawImage(ButtonImages, destRect, srcRect, units);
}

The very nice thing about GDI+ is that I no longer have to do pixel-by-pixel alpha-blending.

Using the Code

If you create a new project and use the classes here, remember that there is unmanaged code within these classes, so your project settings must be changed to allow unsafe code.

Follow these steps to create and display your transparent buttons:

  1. Create the buttons with 32-bit ARGB format.
  2. Create a resource DLL and import the bitmaps.
  3. Move the resource DLL to your .NET project directory, or provide a reference path.
  4. Create buttons on your .NET project's Form that have transparent background color.
  5. Change the buttons class type to TransButton.TransButton.
  6. Extract the bitmaps from DLL using ResourceLibrary class code.
  7. Set the Bitmap and Region of buttons after InitializeComponent.

That's about it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
Software architect and developer with over 20 years of experience, specializing in GUI designs and intranet systems in MFC/C++ and ASP.Net using C#.

Comments and Discussions

 
Generalin VB 6.0 Pin
Member 258282019-Aug-10 1:46
Member 258282019-Aug-10 1:46 
GeneralMFC Code Pin
Elmue7-Oct-09 9:51
Elmue7-Oct-09 9:51 
Hello

The link above to
"alpha-blended bitmap buttons for MFC"
is dead.

Does the page still exist ?

Elmü
GeneralRe: MFC Code Pin
Bob Carboni8-Oct-09 14:50
Bob Carboni8-Oct-09 14:50 
GeneralI have problem to downloading your application Pin
Naveed72728-Aug-07 2:55
Naveed72728-Aug-07 2:55 
GeneralControl behind TransButton Pin
CVertex20-Oct-05 20:08
CVertex20-Oct-05 20:08 
GeneralRe: Control behind TransButton Pin
inshua19-Mar-06 19:54
inshua19-Mar-06 19:54 
GeneralRe: Control behind TransButton Pin
LatencyXXX24-Dec-07 6:48
LatencyXXX24-Dec-07 6:48 
QuestionInclude the bmp files? Pin
Richard Cunday30-Sep-05 7:29
Richard Cunday30-Sep-05 7:29 
GeneralPhotoshop Pin
Anonymous22-Jun-05 12:49
Anonymous22-Jun-05 12:49 
GeneralRe: Photoshop Pin
Anonymous23-Jun-05 4:11
Anonymous23-Jun-05 4:11 
GeneralPhotoshop Pin
Anonymous22-Jun-05 12:46
Anonymous22-Jun-05 12:46 
Generalbug fixing Pin
Ali Demirkaya4-Apr-05 3:46
Ali Demirkaya4-Apr-05 3:46 
GeneralRe: bug fixing Pin
Anonymous23-Jun-05 4:12
Anonymous23-Jun-05 4:12 
GeneralRe: bug fixing Pin
Lu Tai Phong16-Mar-10 15:56
Lu Tai Phong16-Mar-10 15:56 
QuestionTried PNGs? Pin
ip25518-Aug-04 13:29
ip25518-Aug-04 13:29 
AnswerRe: Tried PNGs? Pin
BillWoodruff18-Aug-04 13:47
professionalBillWoodruff18-Aug-04 13:47 
GeneralRe: Tried PNGs? Pin
ip25519-Aug-04 3:02
ip25519-Aug-04 3:02 
GeneralRe: Tried PNGs? Pin
radialronnie4-Apr-08 12:52
radialronnie4-Apr-08 12:52 
AnswerRe: Tried PNGs? - Issues with PNGs Pin
Bob Carboni18-Aug-04 16:04
Bob Carboni18-Aug-04 16:04 
GeneralRe: Tried PNGs? - Issues with PNGs Pin
Mathew Hall18-Aug-04 18:34
Mathew Hall18-Aug-04 18:34 
GeneralRe: Tried PNGs? - Issues with PNGs Pin
Bob Carboni19-Aug-04 4:41
Bob Carboni19-Aug-04 4:41 
GeneralRe: Tried PNGs? - Issues with PNGs Pin
Mathew Hall19-Aug-04 22:59
Mathew Hall19-Aug-04 22:59 
GeneralRe: Tried PNGs? - Issues with PNGs Pin
ip25519-Aug-04 3:33
ip25519-Aug-04 3:33 
GeneralRe: Tried PNGs? - Issues with PNGs Pin
Bob Carboni19-Aug-04 4:56
Bob Carboni19-Aug-04 4:56 
AnswerRe: Tried PNGs? Pin
Mathew Hall18-Aug-04 18:43
Mathew Hall18-Aug-04 18:43 

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

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