Topic : Common Controls
Author : Unknown
Page : << Previous 5  Next >>
Go to page :


the edit box of a drop-down or simple combo box, then hit the RETURN key to add the input to list item. However, in a dialog box, the RETURN key (also the ESC key) is used to exit the application by default. Even if we add message handler for combo box to trap RETURN keystrokes, it still can not receive this message because after the message reaches the dialog box, the application will exit. The message has no chance to be further routed to the child windows of a dialog box.

If we want to process RETURN keystroke events, we need to intercept the message before it is processed by the dialog box. In MFC, there is a function CWnd::PreTranslateMessage(...) that can be overridden for this purpose. This function will be called just before a message is about to be processed by the destination window. Since CDialog is derived from CWnd, we can trap any message sent to the dialog box if we override the above function. This function has the following format:

BOOL CWnd::PreTranslateMessage(MSG *pMsg);

Its only parameter is a pointer to MSG type object:

typedef struct tagMSG { // msg

HWND hwnd;

UINT message;

WPARAM wParam;

LPARAM lParam;

DWORD time;

POINT pt;

} MSG;


From this structure, we know which window is going to receive the message (from member hwnd), what kind of message it is (from member message). Also, we can obtain the message parameters from members wParam and lParam. If the message is not the one we want to intercept, we can just forward the message to its original destination by calling the base class version of this function.

Function CWnd::PreTranslateMessage(...)

Sample 9\CCtl demonstrates how to trap RETURN keystrokes for combo box. It is based on sample 8\CCtl. First, function PreTranslateMessage(...) is overridden. This function can be added by using Class Wizard through following steps: 1) Open Class Wizard, click "Message Maps" tab, select "CCCtlDlg" from "Class name" window. 2) Highlight "CCCtlDlg" in window "Object IDs". 3) Locate and highlight "PreTranslateMessage" in window "Messages". 4) Press "Add function" button.

The default member function looks like the following:

BOOL CCCtlDlg::PreTranslateMessage(MSG *pMsg)

{

return CDialog::PreTranslateMessage(pMsg);

}


If we do not want to process the message, we need to call function CDialog:: PreTranslateMessage(...) to let the dialog box process it as usual. Otherwise we need to return a TRUE value to give the operating system an impression that the message has been processed properly.

In the overridden function, first we need to check if the message is WM_KEYDOWN and the key being pressed is RETURN:

(Code omitted)

Message WM_KEYDOWN is a standard Windows( message for non-system key strokes, and VK_RETURN is a standard virtue key code defined for RETUN key (For a list of virtual key codes, see appendix A). Some local variables are declared at the beginning. They will be used throughout this function.

Accessing the Edit Box of a Combo Box

We need to find out which combo box has the current focus in order to decide if we should process this message. If the item that has the current focus is either IDC_COMBO_DROPDOWN or IDC_COMBO_SIMPLE, we will update the corresponding list items.

In Windows( operating system, windows are managed through using handles. Like menu and bitmap resources, a window handle is also a number which could be used to identify a window. Each window's handle has a different value. As a programmer, we do not need to know the exact value of the handle, however, we can use handle to access or identify a window.

In MFC, there is a function CWnd::GetFocus(), which can be used to obtain a pointer to the child window that has the current focus. From this pointer, we can obtain that window's handle. Then we can compare the handle obtained from function CWnd::GetFocus() with the handles of combo boxes. If there is a hit, we could update the content of that combo box.

Unfortunately, since a combo box is made up of two controls: an edit box and a list box, if we are trying to input characters into the combo box, it is the edit box that has the current focus. Thus if we call CWnd::GetFocus() to obtain handle of the window that has the current focus, we will actually get the handle of the edit box. The edit box is the child window of the combo box window, and it has a different handle with its parent. So comparing the handle of the edit box with the handles of the combo boxes will never result in any hit. The correct step would be: for each combo box, obtaining the handle of its edit box, then comparing it with the handle of the focused window. This will eventually result in a hit.

Class CWnd has a member function that can be used to find a window's child windows:

CWnd *CWnd::GetWindow(UINT nCmd);

Here nCmd specifies what kind of window is being looked for. To enumerate all the child windows, we need to call this function using GW_CHILD flag to find the first child window, then, use GW_HWNDNEXT to call the same function repeatedly until it returns a NULL value. This will enumerate all the sibling windows of the first child window.

There are still problems here: function CWnd::GetWindow(...) returns a CWnd type pointer, we can not obtain further information about that window (i.e. is it an edit box or a list box?). Since a combo box has two child windows, although we can access both of them with the above-mentioned method, we do not know which one is the edit box.

In Windows(, before a new type of window is created, it must register a special class name to the system. Every window has its own class name, which could be used to tell the window's type. In the case of combo box, its edit box's class name is "Edit" and its list box's class name is "ComboLBox". Please note that this class name has nothing to do with MFC classes. It is used by the operating system to identify the window types rather than a programming implementation.

In MFC, the procedure of creating windows is handled automatically, so we never bother to register class names for the windows being created, therefore, we seldom need to know the class names of our windows.

A window's class name can be retrieved from its handle by calling an API function:

int ::GetClassName(HWND hWnd, LPTSTR lpClassName, int nMaxCount);

The first parameter hWnd is the handle of window whose class name is being retrieved; the second parameter lpClassName is the pointer to a buffer where the class name string can be put; the third parameter nMaxCount specifies the length of this buffer.

We can access the first child window of the combo box, see if its class name is "Edit". If not, the other child window must be the edit box. This is because a combo box has only two child windows.

A window's handle can be obtained by calling function CWnd::GetSafeHwnd(). If the window that has the current focus is the edit box of a combo box when RETURN is pressed, we need to notify the parent window about this event. In the sample, a user defined message is used to implement this notification:

#define WM_COMBO_RETURN WM_USER+1000

The following portion of function CCCtlDlg::PreTranslateMessage(...) shows how to retrieve the handles of the edit boxes and compare them with the handle of the focused window:

(Code omitted)

First the handle of currently focused window is stored in variable hwndFocus. If it is a valid window handle, we use m_cbDropDown to get the first child window of IDC_COMBO_DROPDOWN. Then this child window's class name is retrieved by calling function ::GetClassName(...). If the class name is "Edit", we compare its handle with the focused window handle. Otherwise we need to get the handle of the other child window before doing the comparison. This will assure that the handle being compared is the handle of the edit box. If the edit box has the current focus, we post the user defined message WM_COMBO_RETURN, whose WPARAM parameter is assigned the ID of combo box. Finally a TRUE value is returned to prevent the dialog box from further processing this message.

Message WM_COMBO_RETURN is processed in class CCCtlDlg. The member function used to trap this message is CCCtlDlg::OnComboReturn(...). The following code fragment shows how this function is declared and message mapping is implemented:

Function declaration:

class CCCtlDlg : public CDialog

{

......

protected:

......

afx_msg LONG OnComboReturn(UINT, LONG);

DECLARE_MESSAGE_MAP()

};

Message mapping macros:

BEGIN_MESSAGE_MAP(CCCtlDlg, CDialog)

......

ON_MESSAGE(WM_COMBO_RETURN, OnComboReturn)

END_MESSAGE_MAP()


Function implementation:

(Code omitted)

In this message handler, we first obtain a pointer to the combo box using the ID passed through WPARAP message parameter. Then we use above-mentioned method to get the pointer to the edit box (a child window of combo box), and assign it to variable ptrEdit. Then we use this pointer to call function CWnd:: GetWindowText(...) to retrieve the text contained in the edit box window. If the edit box is not empty (this is checked by calling function CString::IsEmpty()), we select all the text in the edit box by calling function CEdit::SetSel(...), which has the following format:

void CEdit::SetSel(int nStartChar, int nEndChar, BOOL bNoScroll = FALSE);

The first two parameters of this function allow us to

Page : << Previous 5  Next >>