Advanced MFC Programming

Advanced MFC Programming Advanced MFC Programming

math.hcmuns.edu.vn
from math.hcmuns.edu.vn More from this publisher
11.04.2014 Views

Chapter 5. Common Controls 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 5.9\CCtl demonstrates how to trap RETURN keystrokes for combo box. It is based on sample 5.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: BOOL CCCtlDlg::PreTranslateMessage(MSG *pMsg) { CString str; HWND hwndDlg; HWND hwndFocus; HWND hwndEdit; CEdit *ptrEdit; int nVirtKey; char szClassName[256]; …… if(pMsg->message == WM_KEYDOWN) { nVirtKey=(int)pMsg->wParam; if(nVirtKey == VK_RETURN) { } } 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 108

Chapter 5. Common Controls 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: …… if(nVirtKey == VK_RETURN) { hwndFocus=GetFocus()->GetSafeHwnd(); if(hwndFocus == NULL)return CDialog::PreTranslateMessage(pMsg); ptrEdit=(CEdit *)m_cbDropDown.GetWindow(GW_CHILD); hwndEdit=ptrEdit->GetSafeHwnd(); ::GetClassName(hwndEdit, szClassName, 256); if(memcmp(szClassName, "Edit", sizeof("Edit"))) { ptrEdit=(CEdit *)ptrEdit->GetWindow(GW_HWNDNEXT); hwndEdit=ptrEdit->GetSafeHwnd(); 109

Chapter 5. Common Controls<br />

compare the handle obtained from function CWnd::GetFocus() with the handles of combo boxes. If there is<br />

a hit, we could update the content of that combo box.<br />

Unfortunately, since a combo box is made up of two controls: an edit box and a list box, if we are<br />

trying to input characters into the combo box, it is the edit box that has the current focus. Thus if we call<br />

CWnd::GetFocus() to obtain handle of the window that has the current focus, we will actually get the<br />

handle of the edit box. The edit box is the child window of the combo box window, and it has a different<br />

handle with its parent. So comparing the handle of the edit box with the handles of the combo boxes will<br />

never result in any hit. The correct step would be: for each combo box, obtaining the handle of its edit box,<br />

then comparing it with the handle of the focused window. This will eventually result in a hit.<br />

Class CWnd has a member function that can be used to find a window’s child windows:<br />

CWnd *CWnd::GetWindow(UINT nCmd);<br />

Here nCmd specifies what kind of window is being looked for. To enumerate all the child windows, we<br />

need to call this function using GW_CHILD flag to find the first child window, then, use GW_HWNDNEXT to call<br />

the same function repeatedly until it returns a NULL value. This will enumerate all the sibling windows of<br />

the first child window.<br />

There are still problems here: function CWnd::GetWindow(…) returns a CWnd type pointer, we can not<br />

obtain further information about that window (i.e. is it an edit box or a list box?). Since a combo box has<br />

two child windows, although we can access both of them with the above-mentioned method, we do not<br />

know which one is the edit box.<br />

In Windows, before a new type of window is created, it must register a special class name to the<br />

system. Every window has its own class name, which could be used to tell the window’s type. In the case<br />

of combo box, its edit box’s class name is “Edit” and its list box’s class name is “ComboLBox”. Please<br />

note that this class name has nothing to do with <strong>MFC</strong> classes. It is used by the operating system to identify<br />

the window types rather than a programming implementation.<br />

In <strong>MFC</strong>, the procedure of creating windows is handled automatically, so we never bother to register<br />

class names for the windows being created, therefore, we seldom need to know the class names of our<br />

windows.<br />

A window’s class name can be retrieved from its handle by calling an API function:<br />

int ::GetClassName(HWND hWnd, LPTSTR lpClassName, int nMaxCount);<br />

The first parameter hWnd is the handle of window whose class name is being retrieved; the second<br />

parameter lpClassName is the pointer to a buffer where the class name string can be put; the third<br />

parameter nMaxCount specifies the length of this buffer.<br />

We can access the first child window of the combo box, see if its class name is “Edit”. If not, the other<br />

child window must be the edit box. This is because a combo box has only two child windows.<br />

A window’s handle can be obtained by calling function CWnd::GetSafeHwnd(). If the window that has<br />

the current focus is the edit box of a combo box when RETURN is pressed, we need to notify the parent<br />

window about this event. In the sample, a user defined message is used to implement this notification:<br />

#define WM_COMBO_RETURN<br />

WM_USER+1000<br />

The following portion of function CCCtlDlg::PreTranslateMessage(…) shows how to retrieve the<br />

handles of the edit boxes and compare them with the handle of the focused window:<br />

……<br />

if(nVirtKey == VK_RETURN)<br />

{<br />

hwndFocus=GetFocus()->GetSafeHwnd();<br />

if(hwndFocus == NULL)return CDialog::PreTranslateMessage(pMsg);<br />

ptrEdit=(CEdit *)m_cbDropDown.GetWindow(GW_CHILD);<br />

hwndEdit=ptrEdit->GetSafeHwnd();<br />

::GetClassName(hwndEdit, szClassName, 256);<br />

if(memcmp(szClassName, "Edit", sizeof("Edit")))<br />

{<br />

ptrEdit=(CEdit *)ptrEdit->GetWindow(GW_HWNDNEXT);<br />

hwndEdit=ptrEdit->GetSafeHwnd();<br />

109

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!