Introduction
The August 2006 issue of MSDN carried two good questions about combo boxes. Here is the link to the article in MSDN.
Let me repeat the question for the sake of clarity.
Q I have a dialog with a combo box. If I press the Cancel button, the dialog closes immediately, although the focus is on the combo box. However, if I start auto-completion by typing a letter or opening the dropdown list, and then press the Cancel button, the dropdown closes but the dialog box does not. This is somewhat nasty for the user. In Microsoft® Word, the file open menu manages to close the dropdown list and cancel the dialog. If I open the dropdown list and press Cancel, the file dialog closes. How can I make Cancel close my dialog immediately?
Tahir Helvaci
Q I am trying to make Ctrl+A select all of the items in a list box. How can I accomplish this?
Mark Fowler
As usual, Paul DiLascia does a wonderful job of solving these problems. In fact, it actually prompted me to think if there is any way to accomplish the same without using hooks. Please bear in mind that he uses three hooks to accomplish the above tasks, namely a WH_GETMESSAGE
, a WH_MOUSE
, and a WH_CALLWNDPROC
. As he himself cautions, hook writers have to be extremely careful, like very quickly do whatever is intended inside the hook procedure and get away.
The solution I present here is without hooks, and I also go one step more in providing a simple feature not present in MS Word. The feature that is not present is: When the dropdown of a combo box is visible, a mouse left-button-down on the non-client area only pulls up the dropdown part of the combo box, disabling the dragging feature of the window. If the user wishes to drag the dialog, he should once again do a mouse left-button on the non-client area. The window does not close if one clicks on the Close button while the combo box is expanded. In the code snippet I mention below, I solve this problem too.
Combo boxes come in three styles, one with an edit box, one without an edit box, and one simple type that always has the drop down visible. The entire logic is handled in the PreTranslateMessage(…)
of the dialog application. First, a check like the one shown below is done:
if(pMsg->message == WM_LBUTTONDOWN )
{
--------------
}
So if the message is WM_LBUTTONDOWN
, another check to see if the message is generated inside the combo box is done. If that is so, just call the base class PreTranslateMessage(…)
and return. If not, then using the GetFocus(..)
API, the current window where the focus is and its parent’s (if it turns out to be the combo box without the edit box) or its parent’s (if it turns out to be the combo box with the edit box) handle is obtained. Then, using the ChildWindowFromPoint(…)
API, a check is made if the left-button down message is not in the combo box, if so then the left-button down is on a window other than the combo box, so a call to the base class PreTranslateMessage(…)
(this actually pulls up the drop down part of the combo box) is made, and using the child window got from the call to the ChildWindowFromPoint(…)
API, a WM_LBUTTONDOWN
message is posted to the child window. This is enough for the child window to take over from here. Since this message needs to be posted only once, a static variable s
is used for this purpose. This variable is reset to 0 on a WM_LBUTTONUP
message inside the PreTranslateMessage(…)
of the dialog.
Next, for the drag and close feature, I override the OnNcMouseMove(…)
message of the dialog.
While the drop down of the combo box is visible and when mouse down message is generated on the non-client area of the dialog, the code jumps to the if(!wndChild)
condition in the PreTranslateMessage(…)
, so I just set the nHap
variable to TRUE
and the rest is handled in the OnNcMouseMove(…)
message.
Note: A free gift due to the above mentioned technique is that the dialog will also respond to minimize, maximize, and resize messages automatically.
Now to the second question. Surprisingly, the answer is more simpler than expected. As above, I override the PreTranslateMessage(…)
. First, a check is made to see if the Control key is pressed . If so, a flag is set to true
. Then, if the character ‘a’ is pressed, and if the focus is on one of the list boxes, and if the nFlag
is set, a call to SetSel(-1)
selects all the elements.
Note: The above mentioned technique works only for list boxes with the multiple-select option enabled.