Putting Bitmaps on Pushbuttons

The MFC library makes it easy to display a bitmap (instead of text) on a pushbutton. If you were to program this from scratch, you would set the Owner Draw property for your button and then write a message handler in your dialog class that would paint a bitmap on the button control's window. If you use the MFC CBitmapButton class instead, you end up doing a lot less work, but you have to follow a kind of "cookbook" procedure. Don't worry too much about how it all works (but be glad that you don't have to write much code!).

There's also another way to put bitmaps on buttons. See Chapter 36, for a description of the CButton::SetBitmap function, which associates a single bitmap with a button.

To make a long story short, you lay out your dialog resource as usual with unique text captions for the buttons you designate for bitmaps. Next you add some bitmap resources to your project, and you identify those resources by name rather than by numeric ID. Finally you add some CBitmapButton data members to your dialog class, and you call the AutoLoad member function for each one, which matches a bitmap name to a button caption. If the button caption is "Copy", you add two bitmaps: "COPYU" for the up state and "COPYD" for the down state. By the way, you must still set the button's Owner Draw property. (This will all make more sense when you write a program).

If you look at the MFC source code for the CBitmapButton class, you'll see that the bitmap is an ordinary GDI bitmap painted with a BitBlt call. Thus, you can't expect any palette support. That's not often a problem because bitmaps for buttons are usually 16-color bitmaps that depend on standard VGA colors.

The EX11D Example

Here are the steps for building EX11D:

  1. Run AppWizard to produce \vcpp32\ex11d\ex11d. Accept all the defaults but three: select Single Document, deselect Printing And Print Preview, and select Context-Sensitive Help. The options and the default class names are shown in the illustration below.

    The Context-Sensitive Help option was selected for one reason only: it causes AppWizard to copy some bitmap files into your project's \hlp subdirectory. These bitmaps are supposed to be bound into your project's help file, but we won't study help files until Chapter 21.

  2. Modify the project's IDD_ABOUTBOX dialog resource. It's too much hassle to create a new dialog resource for a few buttons, so we'll use the About dialog that AppWizard generates for every project. Add three pushbuttons with captions, as shown below, accepting the default IDs IDC_BUTTON1, IDC_BUTTON2, and IDC_BUTTON3. The size of the buttons isn't important because the framework adjusts the button size at runtime to match the bitmap size.

    Select the Owner Draw property for all three buttons.

  3. Import three bitmaps from the project's \hlp subdirectory. Choose Resource from Visual C++'s Insert menu, and then click the Import button. Start with EditCopy.bmp, as shown below.

    Assign the name "COPYU" as shown.

    Be sure to use quotes around the name in order to identify the resource by name rather than by ID. This is now the bitmap for the button's up state. Close the bitmap window and, from the ResourceView window, use the clipboard (or drag and drop) to make a copy of the bitmap. Rename the copy "COPYD" (down state), and then edit this bitmap. Choose Invert Colors from the Image menu. There are other ways of making a variation of the up image, but inversion is the quickest.

    Repeat the steps listed above for the EditCut and EditPast bitmaps. When you're finished, you should have the following bitmap resources in your project.
    Resource NameOriginal FileInvert Colors

  4. Edit the code for the CAboutDlg class. Both the declaration and the implementation for this class are contained in the ex11d.cpp file. First add the three private data members shown here in the class declaration:

    CBitmapButton m_editCopy;
    CBitmapButton m_editCut;
    CBitmapButton m_editPaste;

    Then you use ClassWizard to map the WM_INITDIALOG message in the dialog class. (Be sure that the CAboutDlg class is selected.) The message handler (actually a virtual function) is coded as follows:

    BOOL CAboutDlg::OnInitDialog()
        VERIFY(m_editCopy.AutoLoad(IDC_BUTTON1, this));
        VERIFY(m_editCut.AutoLoad(IDC_BUTTON2, this));
        VERIFY(m_editPaste.AutoLoad(IDC_BUTTON3, this));
        return TRUE;  // return TRUE unless you set the focus to a control
                      // EXCEPTION: OCX Property Pages should return FALSE

    The AutoLoad function connects each button with the two matching resources. The VERIFY macro is an MFC diagnostic aid that displays a message box if you didn't code the bitmap names correctly.

  5. Edit the OnDraw function in ex11dView.cpp. Replace the AppWizard-generated code with the following line:

    pDC->TextOut(0, 0, "Choose About from the Help menu.");

  6. Build and test the application. When the program starts, choose About from the Help menu and observe the button behavior. The image below shows the CUT button in the down state.

    Note that bitmap buttons send BN_CLICKED notification messages just as ordinary buttons do. ClassWizard can, of course, map those messages in your dialog class.

Going Further with Bitmap Buttons

You've seen bitmaps for the buttons' up and down states. The CBitmapButton class also supports bitmaps for the focused and disabled states. For the Copy button, the focused bitmap name would be "COPYF", and the disabled bitmap name would be "COPYX". If you want to test the disabled option, make a "COPYX" bitmap, possibly with a red line through it, and then add the following line to your program: