Advanced MFC Programming
Advanced MFC Programming Advanced MFC Programming
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
- Page 73 and 74: Chapter 3. Splitter Window Chapter
- Page 75 and 76: Chapter 3. Splitter Window function
- Page 77 and 78: Chapter 3. Splitter Window BOOL CSp
- Page 79 and 80: Chapter 3. Splitter Window In the a
- Page 81 and 82: Chapter 3. Splitter Window Sample S
- Page 83 and 84: Chapter 3. Splitter Window WM_LBUTT
- Page 85 and 86: Chapter 4. Button Chapter 4 Buttons
- Page 87 and 88: Chapter 4. Button Use string text a
- Page 89 and 90: Chapter 4. Button function, first t
- Page 91 and 92: Chapter 4. Button Now we can remove
- Page 93 and 94: Chapter 4. Button } ( m_bBmpCheck ?
- Page 95 and 96: Chapter 4. Button using bit-wise AN
- Page 97 and 98: Chapter 4. Button Overriding Functi
- Page 99 and 100: Chapter 4. Button …… First pBit
- Page 101 and 102: Chapter 4. Button Using Class MCBit
- Page 103 and 104: Chapter 4. Button The button’s ID
- Page 105 and 106: Chapter 4. Button However, since me
- Page 107 and 108: Chapter 4. Button Here, we use subc
- Page 109 and 110: Chapter 5. Common Controls command
- Page 111 and 112: Chapter 5. Common Controls ((CSpinB
- Page 113 and 114: Chapter 5. Common Controls } The in
- Page 115 and 116: Chapter 5. Common Controls …… B
- Page 117 and 118: Chapter 5. Common Controls The “S
- Page 119 and 120: Chapter 5. Common Controls button
- Page 121 and 122: Chapter 5. Common Controls In this
- Page 123: Chapter 5. Common Controls (CBN_CLO
- Page 127 and 128: Chapter 5. Common Controls } ptrEdi
- Page 129 and 130: Chapter 5. Common Controls ……
- Page 131 and 132: Chapter 5. Common Controls void CWn
- Page 133 and 134: Chapter 5. Common Controls Five loc
- Page 135 and 136: Chapter 5. Common Controls This fun
- Page 137 and 138: Chapter 5. Common Controls After cr
- Page 139 and 140: Chapter 5. Common Controls typedef
- Page 141 and 142: Chapter 5. Common Controls Using th
- Page 143 and 144: Chapter 5. Common Controls Paramete
- Page 145 and 146: Chapter 5. Common Controls void MCT
- Page 147 and 148: Chapter 5. Common Controls modifica
- Page 149 and 150: Chapter 5. Common Controls In funct
- Page 151 and 152: Chapter 5. Common Controls Changing
- Page 153 and 154: Chapter 5. Common Controls m_tabCtr
- Page 155 and 156: Chapter 5. Common Controls Custom R
- Page 157 and 158: Chapter 6. Dialog Box Chapter 6 Dia
- Page 159 and 160: Chapter 6. Dialog Box void CMLDialo
- Page 161 and 162: Chapter 6. Dialog Box } Function CP
- Page 163 and 164: Chapter 6. Dialog Box Everything is
- Page 165 and 166: Chapter 6. Dialog Box } MINMAXINFO;
- Page 167 and 168: Chapter 6. Dialog Box Sample Sampel
- Page 169 and 170: Chapter 6. Dialog Box } brush=(HBRU
- Page 171 and 172: Chapter 6. Dialog Box wndA.GetWindo
- Page 173 and 174: Chapter 6. Dialog Box rectStaticGrp
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