A customer had a modeless dialog box with custom accelerators.
If their window had been a modeless dialog box without custom accelerators, then their message dispatch would be
if (!IsDialogMessage(hdlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); }
On the other hand, if their window with accelerators had been a plain window rather than a dialog box, then their message dispatch would be
if (!TranslateAccelerator(hwnd, hacc, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); }
But since they have both, the question arises: Which should I do first, the IsDialogMessage
or the TranslateAccelerator
?
The customer experimented and found that they had to call TranslateAccelerator
first:
if (!TranslateAccelerator(hwnd, hacc, &msg) && !IsDialogMessage(hdlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); }
If they flipped the order, they found that accelerators were not being translated:
// Code in italics is wrong. if (!IsDialogMessage(hdlg, &msg) && !TranslateAccelerator(hwnd, hacc, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); }
The customer empirically determined that you have to translate the accelerator first. (Or they could have read my article on the subject of custom accelerators in dialog boxes and seen the correct order.) But why is that the correct order?
The answer goes back to the return values of the two function. The TranslateAccelerator
function returns a nonzero value if it recognized the message as an accelerator (and posted a WM_COMMAND
message). The IsDialogMessage
function returns a nonzero value if it recognized the message as a message for the dialog (and dispatched it).
Now look at what happens if you have a message that is both a message for the dialog and an accelerator. For example, focus is on a button control in the dialog box and you press, say, Alt+F2.
Let’s say you call IsDialogMessage
first. The IsDialogMessage
function says, “Why yes, this message is for the dialog box, so I dispatched it to the button control. Mission accomplished. I’m so awesome!” The IsDialogMessage
function returns a non-zero value, and the TranslateAccelerator
never gets a chance to run.
On the other hand, if you call TranslateAccelerator
first, then the TranslateAccelerator
function sees the accelerator key and posts the WM_COMMAND
function to the dialog window, then it returns a non-zero value to say “Why yes, this message is an accelerator, so I posted a WM_COMMAND
message. Mission accomplished. I’m so awesome!” The TranslateAccelerator
function returns a non-zero value, and the IsDialogMessage
never gets a chance to run.
The question of which one to call first is therefore a matter of priority. If the user presses the accelerator key while focus is on a control in the dialog box, which is more important to you: The fact that it is an accelerator, or the fact that it is a message that targets the dialog box? Whichever is more important to you goes first.
But wait, that’s not the end of the story. Note that the above code calls TranslateAccelerator
unconditionally, which means that the accelerator keys are active even if focus is not on the dialog box at all. For example, focus may be on another window on the same thread (say, the owner of the modeless dialog box). You probably don’t want the modeless dialog box stealing accelerators from the owner. To avoid this problem, you need to translate accelerators for your dialog box only if the focus is somewhere in your dialog box.
if (!((hdlg == msg.hwnd || IsChild(hdlg, msg.hwnd)) && !TranslateAccelerator(hdlg, hacc, &msg)) && !IsDialogMessage(hdlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); }
This code should look familiar, since I copied it from my original article.
0 comments