Topic : Buttons
Author : Unknown
Page : << Previous 2  Next >>
Go to page :

checked, we call CButton::SetBitmap(...) to associate it with IDB_BITMAP_CHECK; otherwise we use bitmap IDB_BITMAP_UNCHECK. The following is the implementation of this function:

(Code omitted)

5) Use Class Wizard to implement three WM_COMMAND message handlers for IDC_CHECK, IDC_RADIO_A and IDC_RADIO_B. Within each handler, we call CBtnDlg::SetCheckBitmap(...) to set appropriate bitmaps. Because two radio buttons should be treated as a group (if one is checked, the other one will be unchecked automatically), we need to set both button's bitmaps within each message handler:

(Code omitted)

In step 3, when calling CWnd::GetDlgItem(...), we pass the control's resource ID to the function to obtain a pointer to the control and use it to call function CButton::SetBitmap(...). Because CWnd::GetDlgItem(...) will return a CWnd type pointer, we must first cast it to CButton type pointer before calling the member function of CButton.

Function Cbutton::SetBitmap(...) has an HBITMAP type parameter, which requires a valid bitmap handle. A bitmap handle can be obtained by calling function CBitmap::GetSafeHandle(), of course, the returned handle is valid only after the bitmap is loaded.

In step 4, function CButton::GetCheck() is called to retrieve button's current state (checked or unchecked). The function returns a Boolean type value, if the returned value is TRUE, the button is being checked, otherwise it is not checked.

After these modifications, the bitmap check box and radio buttons will become functional.

3 Subclass

In section 1, we used automatic method to create bitmap buttons. This requires us to create owner-draw buttons with special caption text, which will be used to name the bitmap resource IDs. For simple cases, this is a very convenient method. However, if we implement bitmap buttons this way, it is difficult for us to customize them at runtime.

Implementing Subclass

Class CBitmapButton gives us another member function that can be used to associate bitmaps with an owner-draw button: CBitmapButton::LoadBitmaps(...). This function has two versions, the first version allows us to load bitmaps with string IDs, the second version allows us to load bitmaps with integer IDs.

To use this function, we must first implement subclass for the owner-draw button. "Subclass" is a very powerful technique in Windows? programming. It allows us to write a procedure, attach it to a window, and use it to intercept messages sent to this window then process it. By doing this, we are able to customize the window's behavior within the procedure.

Subclass is supported by class CWnd, so theoretically all windows (including client window, dialog box, dialog common controls...) can be "subclassed". There are two functions to implement subclass, one is CWnd::SubclassWindow(...), which allows us to customize the normal behavior of a window. Here we will use the other one: CWnd::SubclassDlgItem(...), which is specially designed to implement subclass for the common controls contained in a dialog box.

In MFC, implementing subclass is very simple. We don't need to write a special procedure to handle the intercepted messages. All we need to do is designing a class as usual, adding message handlers for the messages we want to process, and implementing the message handlers. Then we can declare a variable using the newly designed class, and call function CWnd::SubclassDlgItem(...) to implement subclass.

Function CWnd::SubclassDlgItem(...) has two parameters:

BOOL CWnd::SubclassDlgItem(UINT nID, CWnd *pParent);

Parameter nID indicates which control we are dealing with, and pParent is the pointer to the control's parent window.

Class CBitmapButton uses subclass to change the default behavior of a button. If we use automatic method to load the bitmaps, the subclass procedure is transparent to the programmer. However, if we want to load the bitmaps by ourselves, we must implement subclass first.

Bitmap Button

Sample 3\Btn demonstrates how to associate bitmaps with an owner draw button by calling function CBitmapButton::LoadBitmaps(...). It is based on sample 2\Btn. There is nothing new in this sample, except that button IDC_PLAY is implemented differently.

In the previous samples, variable CBtnDlg::m_btnPlay is declared as a CBitmapButton type variable. In the new sample, instead of using automatic method to load the bitmaps, we first implement the subclass then load the bitmaps manually in function CBitmapButton::LoadBitmaps(...):

(Code omitted)

Here, function CBitmapButton::AutoLoad(...) is replaced by three new functions. The first function added is CWnd::SubclassDlgItem(...). The second function is CBitmapButton::LoadBitmaps(...). This function has four parameters, which are the bitmap IDs corresponding to button's "Up", "Down", "Focused" and "Disabled" states respectively. They could be either string IDs or integer IDs. The last function is CBitmap::SizeToContent(), which allows us to set bitmap button's size to the size of the associated bitmaps. If we don't call this function, the bitmaps may not fit well into the button.

Now we can remove or modify bitmap button IDC_PLAY's caption text "Play. Actually, it doesn't matter if the button has caption text or not. By compiling the application and executing it at this point, we will see that the bitmap button implemented here is exactly the same as the one implemented in the previous sample.

4 Bitmap Check Box and Radio Button: Method 2

In sample 2\Btn, although we can represent the checked and unchecked states of a check box or a radio button using different bitmaps, we could not customize their "focused" state. When a button has the current focus, a rectangle with dashed border will always be put over button's face (Figure 4-5). This is because we use CButton instead of CBitmapButton to implement buttons, this allows only one bitmap to be associated with a button.

(Figure omitted)

To improve this, we can use class CBitmapButton to create both check box and radio button. By doing this, the button's focused state will be implemented using the bitmap image provided by the programmer instead of drawing a rectangle with dashed border over button's face. Since class CBitmapButton supports only push button implementation, we need to change the bitmap by ourselves to imitate check box and radio button.

Sample 4\Btn is based on sample 3\Btn. In this sample, three new buttons are added to the dialog template: one will be implemented as a check box; the other two will be implemented as radio buttons. All of them will be based on class CBitmapButton.

First, we need a Boolean type variable for each check box and radio button to represent its current state. This variable toggles between TRUE and FALSE, indicating if the button is currently checked or unchecked. When the state of a button changes, we re-associate the button with an alternate bitmap and paint the bitmap button again.

Since we use push button to implement check box and radio button, we can not call function CButton::GetCheck(...) to examine if the button is currently checked or not. This is because a push button will automatically resume to the "up" state after the mouse is released.

In the sample application, three new buttons are added to the dialog template IDD_BTN_DIALOG, and their corresponding IDs are IDC_BMP_CHECK, IDC_BMP_RADIO_A, IDC_BMP_RADIO_B respectively. Also, they all have a "Owner draw" style. Besides the new controls, two new bitmap resources are also added to the application, which will be used to implement button's "Checked" and "Unchecked" states. The IDs of the new bitmap resources are IDB_BITMAP_BTNCHECK and IDB_BITMAP_BTNUNCHECK. The difference between the new bitmaps and two old ones (whose IDs are IDB_BITMAP_CHECK and IDB_BITMAP_UNCHECK) is that the new bitmaps have a 3-D effect. In sample 2\Btn, the check box and radio buttons are implemented using class CButton, which automatically adds 3-D effect to the controls. Since we want the controls to be implemented solely by programmer-provided bitmaps, we need to add 3-D effect by ourselves.

In the sample application, three new CBitmapButton type variables are declared in class CBtnDlg. Also, a new member function SetRadioBitmap() is added to associate bitmaps with the two radio buttons. This function will be called when one of the radio buttons is clicked by mouse. For the check box, associating bitmap with it is relatively simple, so it is implemented within the message handler. Besides this, a new Boolean type variable CBtnDlg::m_bBmpCheck is declared to indicate the current state of the check box, and an unsigned integer CBtnDlg::m_uBmpRadio is declared to indicate which radio button is being selected. For each button, WM_COMMAND message handler is added through using Class Wizard. These message handlers are CBtnDlg::OnBmpCheck(), CBtnDlg::OnBmpRadioA() and CBtnDlg::OnBmpRadioB() respectively. The following code fragment shows the new members added to the class:

(Code omitted)

In the constructor of CBtnDlg, variable CBtnDlg::m_bBmpCheck is initialized to FALSE and CBtnDlg::m_uBmpRadio is initialized to zero. This means the original state of the check box is unchecked, and no radio button is selected:

CBtnDlg::CBtnDlg(CWnd* pParent /*=NULL*/)

: CDialog(CBtnDlg::IDD, pParent)






In the dialog initialization stage, subclass is implemented for buttons, then the corresponding bitmaps are loaded:

(Code omitted)

For each button, first we implement subclass for it, then we load the bitmap by calling function CBitmapButton::LoadBitmaps(...). Because we provide only one bitmap for each control, state transition of these buttons (e.g., normal state to focused state) will not be reflected to the interface unless we add extra code to handle it.

For check box IDC_BMP_CHECK, when it is clicked by the mouse's left button, we need to toggle the value of variable CBtnDlg::m_bCheck, load the corresponding bitmap and paint the button again. The following is the WM_DOMMAND message handler for check box:

(Code omitted)

Radio buttons are slightly

Page : << Previous 2  Next >>