Using the TAB key to navigate in non-dialogs, redux


You want to use the TAB key to navigate through a non-dialog, so you call Is­Dialog­Message in your message loop, but it doesn't work!

// code in italics is wrong
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
  if (!msg.hwnd || !IsDialogMessage(msg.hwnd, &msg)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
}

The problem here is that you are passing the wrong window handle to Is­Dialog­Message. The first parameter to Is­Dialog­Message is the dialog-like window you want to be able to navigate through. But the code above passes the window that received the message, so you are basically telling the control to do TAB navigation within itself. And naturally, the result of that is that focus stays where it is, because if you ask a button, "Hey, could you move to your next tab stop?" the button is going to say, "Dude, I'm the only tab stop on my window!"

If you think about it, passing the window the message was already going to be dispatched to isn't very interesting. If that was the design of the function, then all the work of Is­Dialog­Message could've just been put in Def­Window­Proc and we wouldn't have needed a Is­Dialog­Message function in the first place.

And if you think about it, passing the window the message was already going to be dispatched to isn't very interesting. If that was the design of the function, then the parameter isn't necessary. The function could just infer it directly from the MSG structure you passed as the second parameter.

The fix is to pass the main window handle to Is­Dialog­Message:

MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
  if (!msg.hwnd || !IsDialogMessage(hwndFrame, &msg)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
}

Reminder: As noted in the original article, you should not use the WM_USER or WM_USER + 1 messages in your custom window.

Comments (7)
  1. Azarien says:

    Speaking of dialogs, I prefer to just open a window and create all my controls with CreateWindow, rather than use the dialog manager. Am I doing it wrong?

  2. ChrisR says:

    @Azarien:  If you do not implement normal dialog features such as tabbing and default buttons, then yes, you are doing it very wrong.  I can't stand when I have to use a program like that.

  3. Deduplicator says:

    And it seems to be fashionable to ignore the tab-order, accelerators and menu keys as well.

    Even IE10 suffers from Alt+x not opening the proper menu, but a curious mishmash-menu.

    Sorry xpclient, stole your line.

  4. Mike Dimmick says:

    @Deduplicator: If you mean Alt+X (rather than x meaning a wildcard), that is the key combination for opening the Tools menu accessible from the 'gear' icon at the right-hand end of the address bar, as shown in the tooltip for that icon. This is different from the Tools menu on the command bar (Alt+O) and the Tools menu on the menu bar (Alt+T).

    The (IE7) command bar accelerators only work if the command bar is visible; however, the menu bar accelerators temporarily display the menu bar – as does pressing and releasing Alt.

    The IE team have clearly been careful to assign the accelerators so that there isn't a clash between these three sets of UI controls. Still, it's interesting that they've left all the old menus in, though hidden by default, rather than daring to really take them away.

    Tested on Windows 7 with IE10 and IE11 Preview.

  5. Deduplicator says:

    Well, IE10 on Win7 (64Bit, German), the menu bar accelerator for Extras is Alt+X, which brings up the gear-menu instead. Looks like you say that's "just" defective localization. Still bad enough, and worse it is hidden from most redmonders.

    Anway, that's still better than office reassigning all keyboard-shortcuts just for the heck of it, breaking conventions used for microsoft-eons.

  6. John Doe says:

    You can't use WM_USER + 2 either:

    msdn.microsoft.com/…/ms645409(v=vs.85).aspx

  7. Yonghui says:

    Hi Raymond,

    I have an ole control (edit control, does not have DLGC_WANTTAB flag) in my project, and the control is added to a dialog. when I press TAB key, focus never go to this OLE control. I use spyxx to capture message, I find that the OLE control receives KILLFOCUS after I press TAB, then it receives KEYDOWN message and KILLFOCUS message, the focus is killed, I am confused with this behavior,

    could you give some suggestion?

    Thanks,

    Yonghui

Comments are closed.