Why doesn’t the TAB key work on controls I’ve marked as WS_TABSTOP?

A customer had a programming problem regarding tab stops:

I create a parent window (child of main frame) as below

// Create the popup window that holds the toolbar.
if (!CreateEx(
        0, 0, 0, 0,

This window hosts 2 toolbar windows. Each toolbar window has the WS_TABSTOP style set using SetWindowLong.

// Set tab stop for accessibility
DWORD dwStyles = ::GetWindowLong(GetSafeHwnd(), GWL_STYLE);
::SetWindowLong(GetSafeHwnd(), GWL_STYLE, dwStyles | WS_TABSTOP);

MSDN states

WS_EX_CONTROLPARENT Allows the user to navigate among the child windows of the window by using the TAB key.

But I am not able to use TAB to navigate to second toolbar. I tried handling WM_GETDLGCODE and return DLGC_WANTTAB. But this message is not sent to parent.

I can try subclassing the toolbar to handle TAB key, but if I do that, then what’s the point of the WS_TABSTOP and WS_EX_CONTROLPARENT styles?

You already know how to solve this customer’s problem. The quoted documentation comes from the MFC documentation on extended window styles. You may find that the documentation in the Platform SDK to be a bit better. Which is not unexpected, since extended window styles are a Platform SDK feature; MFC is merely surfacing the underlying Win32 functionality in its own framework.

Final clue: Look at this old blog entry, but come to it with a different point of view.

I used my psychic powers to solve this one. A close reading of the description of the problem reveals that the window in question is not part of a dialog box, which means that the standard dialog message loop is not active. Which means that a crucial step is missing.

Did you remember to call IsDialogMessage in your message loop?

The customer confirmed that this was the missing step.

You are right, my window is not a dialog box. Handling IsDialogMessage solved the issue.

Comments (4)
  1. Aaron Ballman says:

    FYI: The link to "this old blog entry" points to beta.blogs, and it doesn't bring me to the actual blog posting.  Removing the "beta." from the link fixes it for me.

    [Fixed, thanks. -Raymond]
  2. Joshua says:

    So that's the explanation for that.

    I always thought putting code in WinMain to detect and handle VK_TAB was weird.

  3. blah says:

    IsDialogMessage() has a huge side effect? This naming convention is as brilliant as the Windows registry. Keys are directories, values are keys, and data are values.

    [IsDialogMessage could be more verbosely named HandleDialogMessage. And the confusing registry nomenclature was covered some time ago. -Raymond]
  4. 640k says:

    The link to "this old blog entry" points to beta.blogs

    How could this happen?

Comments are closed.