Bitmap Buttons

 

Overview

A bitmap button is a button that display a picture or a picture and text on its face. This is usually used the button a little explicit. There are two ways you can create a bitmap button: with or without an existing resource identifier.

A bitmap button is created using the CBitmapButton class, which is a child of CButton, which in turn is a descendent of the CWnd class. This means that it can use many of the method of CWnd.

Because the bitmap button is in fact a customized version of a button, you must first declare a variable or pointer of it:

CBitmapButton *btnMap = new CBitmapButton;

Using the variable or pointer, you can call the Create() method to formally create the button. Here is an example:

BOOL CDialog6Dlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	. . .
	
	// TODO: Add extra initialization here
	CBitmapButton *btnMap = new CBitmapButton;

	btnMap->Create(NULL, WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 
		      CRect(10,10,100,100), this, IDC_BTN_NEW);

	return TRUE;  // return TRUE  unless you set the focus to a control
}

Bitmap Button Implementation

Creating a bitmap button without an identifier is considered creating it from scratch. Creating a bitmap button is more practical because you can easily use the messages of such a button and refer to it in code. Therefore, before creating a bitmap button, you can first add a button to a dialog box and specify its identifier. For a button to display a picture, it must have the BS_OWNERDRAW style. This means that you should check or set to True the Owner Draw option.

Visually, the most appealing aspect of a bitmap button is the picture it displays. The bitmap can be designed following the same tools and instructions we reviewed for the icons. To give a 3-D or realistic appearance to the button, you should mimic the focus effect when designing the bitmaps.

Therefore, you should create one to four bitmaps for the control. Here is how they would work:

  • If you create one bitmap, it would be used for all clicks of the button. Since the button would always appear the same, you should avoid using only one picture
  • If you create two bitmaps, the first would display as the regular picture for the button, that is, when the button is not being clicked. The second would be used when the button is clicked and the mouse is down on the button
  • If you create three bitmaps, the first would display when the button is not being accessed and another control has focus. The second picture would display when the user is clicking the button and as long as the mouse is down on the button. The third would be used when the button has focus but it is not being used
  • If you create three bitmaps, the first would display when the button is not being accessed and another control has focus. The second picture would display when the user is clicking the button and as long as the mouse is down on the button. The third would be used when the button has focus but it is not being used. The fourth would be used when the button is disabled

Since these options, to use a bitmap button to its fullest, you should strive to provide four bitmaps. After creating the bitmaps, you can load them into the button. This is done using the LoadBitmaps() method. It comes in two versions as follows:

BOOL LoadBitmaps(LPCTSTR lpszBitmapResource,
		 LPCTSTR lpszBitmapResourceSel = NULL,
                 LPCTSTR lpszBitmapResourceFocus = NULL, 
		 LPCTSTR lpszBitmapResourceDisabled = NULL );
BOOL LoadBitmaps(UINT nIDBitmapResource, 
		 UINT nIDBitmapResourceSel = 0, 
		 UINT nIDBitmapResourceFocus = 0, 
		 UINT nIDBitmapResourceDisabled = 0 );

Each version takes four arguments. The first version uses the bitmaps as string resources while the second version uses resource identifiers.

As stated above, you can one to four bitmaps. As seen on these functions, the first bitmap is required. The first argument must specify the first or default bitmap and is required. The second argument is the name or identifier of the bitmap that would be used when the button is selected. The third is used when the button has focus. The last bitmap is used when the button is disabled. Here is an example:

BOOL CDialog6Dlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	. . .
	
	// TODO: Add extra initialization here
	CBitmapButton *btnMap = new CBitmapButton;

	btnMap->Create(NULL, WS_CHILD|WS_VISIBLE|BS_OWNERDRAW, 
		      CRect(10,10,100,100), this, IDC_BTN_NEW);
	btnMap->LoadBitmaps(IDB_BMP_BTN1, IDB_BMP_BTN2, IDB_BMP_BTN3, IDB_BMP_BTN4);

	return TRUE;  // return TRUE  unless you set the focus to a control
}

It is very likely that you may not be able to design the button on one hand and the bitmaps on the other hand at exactly the same dimensions. To adjust these measures, the CBitmapButton class is equipped with the SizeToContent() method. Its syntax is:

void SizeToContent();

When this method is called on the button, it resizes it to the size of the bitmaps. If one bitmap is larger and/or taller than the others, if you had loaded more than one, this method would resize the button to the larger and taller bitmap. For this reason, always make sure that you design bitmaps that have the same dimension. Here is an example of using this method:

BOOL CDialog6Dlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	. . .
	
	// TODO: Add extra initialization here
	CBitmapButton *btnMap = new CBitmapButton;

	btnMap->Create(NULL, WS_CHILD|WS_VISIBLE|BS_OWNERDRAW, 
		      CRect(10,10,100,100), this, IDC_BTN_NEW);
	btnMap->LoadBitmaps(IDB_BMP_BTN1, IDB_BMP_BTN2, IDB_BMP_BTN3, IDB_BMP_BTN4);
	btnMap->SizeToContent();

	return TRUE;  // return TRUE  unless you set the focus to a control
}

As you may realize if you create a bitmap button strictly using this approach, it would not work. The suggestion is to "subclass" your button class so the messages sent to the bitmap button would be applied effectively. The function used to do this is the CWnd::SubclassDlgItem() method and its syntax is:

BOOL SubclassDlgItem( UINT nID, CWnd* pParent );

The first argument is the resource ID of the existing button. The second argument is the control that is hosting the button; this is usually the dialog box but it can be another control container.

  1. Open the Geometry1 application
  2. On the main menu, click Insert -> Resource... Click Bitmap and click New
  3. Using the Proeprties window, change the bitmaps ID to IDB_CANCEL_DISABLED and change its size to Width = 75, Height = 23
  4. Design it as follows:
     
  5. In the same way add new bitmaps. Change their IDs and design them as follows:
     
    IDB_CANCEL_FOCUS
     
    IDB_CANCEL_NRM
     
    IDB_CANCEL_SELECTED
     
    IDB_HELP_DISABLED
     
    IDB_HELP_FOCUS
     
    IDB_HELP_NORMAL
     
    IDB_HELP_SELECTED
     
    IDB_OK_DEFAULT
     
    IDB_OK_DISABLED
     
    IDB_OK_FOCUS
     
    IDB_OK_SELECTED
  6. In the header of the CGeomeSheet class, declare three CBitmapButton variables as follows:
     
    private:
    	CBitmapButton btnBmpOK, btnBmpCancel, btnBmpHelp;
    };
  7. Use the constructor to load the bitmaps of each button:
     
    CGeomeSheet::CGeomeSheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
    	:CPropertySheet(pszCaption, pParentWnd, iSelectPage)
    {
    	m_psh.dwFlags |= PSH_NOAPPLYNOW;
    
    	AddPage(&Quad);
    	AddPage(&Circ);
    	AddPage(&G3D);
    
    	btnBmpOK.LoadBitmaps(IDB_OK_DEFAULT, IDB_OK_SELECTED,
    		                 IDB_OK_FOCUS, IDB_OK_DISABLED);
    	btnBmpCancel.LoadBitmaps(IDB_CANCEL_NORMAL, IDB_CANCEL_SELECTED,
                                 IDB_CANCEL_FOCUS, IDB_CANCEL_DISABLED);
    	btnBmpHelp.LoadBitmaps(IDB_HELP_NORMAL, IDB_HELP_SELECTED,
                               IDB_HELP_FOCUS, IDB_HELP_DISABLED);
    }
  8. To customize the existing buttons, implement the OnInitDialog event of the CGeomeSheet class as follows (most of the code is used because the buttons were already created by default; this means that we have to manually change their style):
     
    BOOL CGeomeSheet::OnInitDialog()
    {
    	BOOL bResult = CPropertySheet::OnInitDialog();
    
    	// TODO: Add your specialized code here
    	// We need a handle to each
    	CButton *btnOK, *btnCancel, *btnHelp;
    
    	// Get a handle to each of the existing buttons
    	btnOK = reinterpret_cast<CButton *>(GetDlgItem(IDOK));
    	btnCancel = reinterpret_cast<CButton *>(GetDlgItem(IDCANCEL));
    	btnHelp = reinterpret_cast<CButton *>(GetDlgItem(IDHELP));
    
    	// Get the style of the button(s)
    	LONG GWLOK = GetWindowLong(btnOK->m_hWnd, GWL_STYLE);
    	LONG GWLCancel = GetWindowLong(btnCancel->m_hWnd, GWL_STYLE);
    	LONG GWLHelp = GetWindowLong(btnHelp->m_hWnd, GWL_STYLE);
    
    	// Change the button's style to BS_OWNERDRAW
    	SetWindowLong(btnOK->m_hWnd, GWL_STYLE, GWLOK | BS_OWNERDRAW);
    	SetWindowLong(btnCancel->m_hWnd, GWL_STYLE, GWLCancel | BS_OWNERDRAW);
    	SetWindowLong(btnHelp->m_hWnd, GWL_STYLE, GWLHelp | BS_OWNERDRAW);
    
    	// Subclass each button
    	btnBmpOK.SubclassDlgItem(IDOK, this);
    	btnBmpCancel.SubclassDlgItem(IDCANCEL, this);
    	btnBmpHelp.SubclassDlgItem(IDHELP, this);
    
    	return bResult;
    }
  9. Test the application:
     
  10. After using it, close it and return to MSVC
 

Copyright © 2003-2010 FunctionX, Inc.