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


are obtained by suffixing 'U', 'D', 'F', 'X' or 'M' to button's caption text. With the resource IDs, function MCBitmapButton:: LoadBitmaps(...) is called to load the relevant bitmaps:

(Code omitted)

The caption text of the button can be retrieved by calling function CWnd::GetWindwoText(...). Actually, every window can have a caption text, which can be an empty string, or any text. We can use this function to retrieve the caption text of any window, for example, a title tar.

We need to call functions CWnd::SubclassDlgItem(...) and CBitmapButton::SizeToContent() to change the button's default properties. Actually, the above two functions are also called in function CBitmapButton::AutoLoad(...).

Overriding Function CBitmapButton::DrawItem(...)

Then we need to implement MCBitmapButton::DrawItem(...). In this member function, we need to check the current state of button, and choose appropriate bitmaps for painting button's face.

Class CBitmapButton has four CBitmap type variables: m_bitmap, m_bitmapSel, m_bitmapFocus and m_bitmapDisabled, which are used to store the standard four bitmaps. Because they are not declared as private members, we can access them from the derived classes and use them to paint the button.

The only parameter of function CBitmapButton::DrawItem(...) is a pointer to a DRAWITEMSTRUCT type object. When overriding this function, we need to use four members contained in DRAWITEMSTRUCT: itemState, which indicates the current state of the button; hDC, which is the handle to the target device context; rcItem, which is the rectangle specifies the position and size of the output area. Besides these, we also need to check the following bits of member itemState: ODS_SELECTED, ODS_FOCUS, ODS_DISABLED, which indicate if the current state of button is "selected", "focused" or "disabled".

At the beginning of the overridden function, we need to declare several CBitmap and CDC type variables or pointers, all of which are used for bitmap drawing:

(Code omitted)

Three DCs are declared here. To draw a bitmap, we must create a memory DC, select the bitmap into it and copy the bitmap from the memory DC to the target DC. The target could be either a device or a memory block (we could copy bitmap between two memory DCs). A DC can select only one bitmap at any time.

When there is no mask bitmap, variable memDC is used to perform normal bitmap drawing: it is used to select the normal bitmap, and copy it directly to the target DC. When there is a mask bitmap, memDC will be used along with memDCMask to implement transparent background drawing.

Variable memDCImage is used to act as the target memory DC and implement transparent background drawing. It will be used in conjunction with bmpImage, which will be selected into memDCImage. To draw the bitmap, first we need to copy the image pattern from the target device to the memory bitmap, then copy the source bitmap to the memory bitmap (perform AND and XOR drawings). Finally, we can output the result from the memory bitmap to the target device.

Variable bmpImage is used to create bitmap in memory.

Variable memDCMask is used to select mask bitmap image.

Pointer pDC will be used to store the pointer of the target device context that is created from hDC member of structure DRAWITEMSTRUCT.

Pointers pBitmap and pBitmapMask will be used to store the pointers to the normal bitmap (could be one of the bitmaps indicating the four states of the button) and the mask bitmap respectively.

The other three CBitmap pointers pOld, pOldMask and pOldImage are used to select the bitmaps out of the DCs (When the bitmaps are being selected into the DCs, these pointers are used to store the bitmaps selected out of the DCs. After bitmap drawing is finished, we can select old bitmaps back into the DCs, this will select our bitmaps out of the DCs automatically).

Variable state is used to store the current state of button.

The following portion of function MCBitmapButton::DrawItem(...) shows how to choose appropriate bitmaps:

(Code omitted)

First pBitmap is assigned the address of variable m_bitmap, which holds the default bitmap. Then we check if the mask bitmap exists, if so, we assign its address to pBitmapMask. The current state of the button is read into variable state, whose ODS_SELECTED, ODS_FOCUS and ODS_DISABLED bits are examined in turn. If any of them is set, the corresponding bitmap's address will be stored in pBitmap.

The following portion of this function creates the memory DCs and selects relative bitmaps into different DCs:

(Code omitted)

First, the address of the target DC is obtained from the DC handle by calling function CDC:: FromHandle(...). Then the memory DC that will select source image is created by calling function CDC::CreateCompatibleDC(...). Since the bitmap could be copied only between compatible DCs, each time we create a memory DC, we need to make sure that it is compatible with the target DC. Next, if the mask bitmap exists, we create three DCs: memDC for normal bitmap, memDCMask for mask bitmap and memDCImage for memory target bitmap (It will act as temparory target device DC). In this case, we also create a memory bitmap using variable bmpImage, which is selected into memDCImage (This bitmap must also be compatible with the DC that will select it). In the above implementation, we call function CBitmap::GetBitmap(...) to obtain the dimension information of a bitmap and call function CBitmap::CreateCompatibleBitmap(...) to create compatible memory bitmap). The mask bitmap is selected into memDCMask. The normal bitmap is always selected into memDC.

The following portion of function MCBitmapButton::DrawItem(...) draws the bitmap by copying normal and mask bitmaps among different DCs:

(Code omitted)

Bitmap copy is implemented by calling function CDC::BitBlt(...). This function will copy the selected bitmap from one DC to another. If there is no mask bitmap, we copy the normal bitmap (selected by memDC) directly to the target DC (pointed by pDC). Otherwise, first we copy the image pattern from the target device (pDC) to memory bitmap (selected by memDCImage). Then we copy normal bitmap and mask bitmap (selected by memDC and memDCMask) to this memory bitmap three times, using different operation modes, and copy the final result to the target DC (pDC). At last, we select the bitmaps out of DCs.

Using Class MCBitmapButton

In sample 5\Btn, automatic method is used to load the mask bitmap. There are two differences between this sample and sample 4\Btn. First, in the new sample, a new bitmap resource "PLAYM" is added to the application that is used as the mask bitmap. Second, variable CBtnDlg::m_btnPlay is declared using MCBitmapButton instead of CBitmapButton (Also, we need to include the header file of MCBitmapButton). No other change is needed.

With the above implementation, the button will have a transparent background. We can test this by re-configuring the system colors.

6 Making Button Aware of Mouse Position

By now no button we've made could provide us with information of mouse position when it is pressed. Although most of the time it is not necessary to know the exact coordinates of the mouse cursor, it may help us create more powerful buttons if we have this information. For example, we can create a bitmap button with four arrows pointing to different directions. When the user clicks mouse on different arrows, we can let different commands be executed if we know the current cursor position.

Since class CButton is derived from CWnd, and CWnd handles different types of mouse events, we should be able to trap mouse related messages into the member functions of CButton. Actually, all classes derived from CWnd can trap mouse events such as left button down, left button up, left button double click, etc. In order to implement the button described above, we need to trap left button up message, which is defined as WM_LBUTTONUP under Windows.

Sample 6\Btn demonstrates how to handle mouse-related message for a button. It is based on sample 6\Btn, with a new button added to the application. The new button has four arrows pointing to different directions, if the user clicks on any of the arrows, a message box will pop up displaying a different message (Figure 4-8).

(Figure omitted)

Trapping Message WM_LBUTTONUP within Button

In the sample, WM_LBUTTONUP message handler is added to class MCBitmapButton. Like trapping any other type of message, in order to handle WM_LBUTTONUP, we need to declare an afx_msg type function and add message mapping macros. If the class is created by Class Wizard, this procedure could be very easy. Otherwise, we must add everything manually.

First we need to declare an afx_msg type member function in the class:

class MCBitmapButton : public CBitmapButton

{

......

protected:

......

afx_msg void OnLButtonUp(UINT, CPoint);

......

DECLARE_MESSAGE_MAP()

};


There must be a DECLARE_MESSAGE_MAP macro in the class in order to let it support message mapping.

The message mapping macros are added to the implementation file as follows:

BEGIN_MESSAGE_MAP(MCBitmapButton, CBitmapButton)

ON_WM_LBUTTONUP()

END_MESSAGE_MAP()


Message WM_LBUTTONUP will be trapped to member function MCBitmapButton::OnLButtonUp(...), which has the following format:

void MCBitmapButton::OnLButtonUp(UINT nFlags, CPoint point)
{

}


We see that the mouse position is passed to parameter point of this function.

Because the commands are generally handled in the parent window of the button, we need to resend mouse clicking information from the button to class CBtnDlg. In the sample application, this is implemented through sending a user- defined message.

User-Defined Message

User defined messages can be treated the same with other standard Windows messages: they can be sent

Page : << Previous 4  Next >>