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

Using WTL's CBitmapButton

0.00/5 (No votes)
31 Mar 2002 1  
How to use the Windows Template Library CBitmapButton

CBitmapButton Sample Image

Introduction

This article describes how to use the bitmap button template in WTL. You can use either the CBitmapButton class, as provided in AtlCtrlx.h, or our small helper class, CBmpBtn, which uses the template directly and fixes an image refresh problem we encountered. The source code for the helper class is available from the link above and is used in the sample project.

Button Images

There are many fine articles about drawing button images on CodeProject.com and elsewhere. Except for a discussion later in this article, we refer you to those other sources.

Button Styles

CBitmapButtonImpl by default sets only the BMPBTN_AUTOSIZE style. Autosize adjusts the size of the button to fit the image you provide. CBmpBtn, the helper class in the sample project adds BMPBTN_AUTO3D_SINGLE to the extended styles set in the button constructor so that 3D borders are drawn around button images.

Also in the sample project is a method that sets the hover button style when a checkbox is clicked. This allows you to see the effects with and without hover style. For regular programs, you would just add BMPBTN_HOVER style to the button constructor.

Button styles may also be set with SetBitmapButtonExtendedStyle(). Other styles, some of which are discussed later in this article, include:

  • BMPBTN_AUTO3D_DOUBLE -- draws a double button border to resemble "classic" Windows buttons
  • BMPBTN_AUTOFIRE -- autofire buttons repeat the button clicked message when they are held down
  • BMPBTN_SHAREIMAGELISTS -- indicates that the button's internal image list is shared with other buttons

Using CBitmapButton

The CBitmapButtonImpl template is unusual in two ways. First, it uses SubclassWindow to attach to a button object instead of just using a simple assignment like most WTL controls do. Second, it will give multiple asserts in Debug builds unless you first create and assign an imagelist and at least one image to the button before it is subclassed.

It is also important to note that CBitmapButtonImpl is a "pure" bitmap button and does not do any Windows text handling. You must draw text in the images you supply. See the section "How To Make Button Images" later in this article for an example.

In the sample project, the CImageList member variable of the OK button is used to hold images for the other buttons. The imagelist is created in the OnInitDialog handler like this:

  CBmpBtn m_ok;
  CBmpBtn m_cancel;

  LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
  { // change button style for the OK button

    DWORD dw = BMPBTN_SHAREIMAGELISTS | BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTOSIZE;
    m_ok.SetBitmapButtonExtendedStyle(dw);

    // create the OK button's imagelist. Use a light gray mask

    m_ok.m_ImageList.Create(IDB_BUTTONS, 64, 0, RGB(192,192,192));

   ...

After the imagelist is created, you need to assign it to the button. The following code, a continuation of the initialize dialog handler started above, shows the steps involved in subclassing the sample dialog's OK button and then using the shared imagelist for the Cancel button. A button must have at least one image and may have up to four for handling different button states.

    // get the OK button's imagelist handle for use with other buttons

    HIMAGELIST hImage = m_ok.GetImageList();

    // OK button: set images, subclass

    m_ok.SetImages(0, 1, 2, 3);
    m_ok.SubclassWindow(GetDlgItem(IDOK));

    // Cancel button: assign imagelist, set images, subclass

    m_cancel.SetImageList(hImage); // imagelist handle from OK button

    m_cancel.SetImages(4, 5, 6, 7);
    m_cancel.SubclassWindow(GetDlgItem(IDCANCEL));

Remember to destroy the imagelist when your program ends. The OnDestroy() handler of the dialog is a good place as shown here:

  LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL&)
  { // destroy OK button imagelist

    m_ok.m_ImageList.Destroy();
    return 0; }

Image Artifacts

When using the DoPaint() message provided by the template, button images overlaid each other in a most alarming fashion. To correct this, we added a background fill and call the inherited method to handle the rest:

  // override of CBitmapButtonImpl DoPaint() in CBmpBtn class

  void DoPaint(CDCHandle dc)
  { RECT rc;
    GetClientRect(&rc);
    dc.FillRect(&rc, (HBRUSH)(COLOR_BTNFACE+1));

    // call inherited DoPaint()

    CBitmapButtonImpl<CBmpBtn>::DoPaint(dc); }

Note that we use FillRect() here to, basically, paint out the previous button state. You may think to use FloodFill() instead, but we discovered that FloodFill() is many times slower than FillRect() and causes sluggish button action.

Autofire Buttons

The bitmap button template provides an autofire style. When an autofire button is held down beyond a timer interval, it sends multiple BN_CLICKED commands, until the button is released. The sample project shows how to handle the clicked messages and use them to increment a CProgressBarCtrl. Autofire buttons are useful in any situation where users would otherwise have to perform multiple button clicks.

How To Make Button Images

Each bitmap button requires at least one image and may have up to four. The images represent button Normal, Pressed, Hover, and Disabled states. If you only provide one image, it will be used for all four states. See the Autofire button for an example of using a single image.

The following image is used for the sample project's OK button. Each quarter (1/4) of the image serves one of the four button states. The segments are 64 pixels wide, which is slightly narrower than a "classic" button, and 24 pixels high.

OK Button Image

To make a Normal image, use an image editor to fill the background with the mask color, add any pictures desired, then place the text. Pressed images are filled with a crosshatch background, i.e. alternating gray and white dots, atop which are placed the same picture and text as Normal. CBitmapButton offsets the image by one pixel over and one pixel down to complete the "pressed" look.

Making a Hover image is simple. Start with a copy of the Normal image and change image outline colors and text colors to your preferred hover color. We use medium blue instead of the traditional navy blue so that the hover (and focus, in non-hover style) stands out clearly.

The final state, Disabled, also starts with a copy of Normal. However, you must: 1) gray out any colors in the image; 2) change the picture outline and text colors to dark gray; 3) draw a white shadow to the right and bottom of each picture and letter.

Terms Of Use

The sample project and bitmap button class available with this article are free. Use them however you wish.

THIS SOFTWARE IS DISTRIBUTED AS-IS, WITHOUT WARRANTIES OF ANY KIND.

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