Making TAB key work on Windows dialog - windows

I'm working on a Windows project with a simple dialog created with CreateWindowEx() it contains multiple panes loaded with CreateDialog() to load the layout from a resource file. On the child panes there are a number of controls including text boxes and buttons which I would like to use TAB to navigate around but all I get is the Windows 'bing' telling me that the key does not do anything. My message loop looks like this:
while( PeekMessage(&msg, 0, 0, 0, PM_REMOVE) )
{
if( !IsDialogMessage(0, &msg) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
And each control window has WS_TABSTOP set in the style as well as the owner pane having WS_EX_CONTROLPARENT set.
Is there anything else I need to do to make the tab key work?
Thanks,
J

Try this:
http://support.microsoft.com/kb/71450 (How To Use One IsDialogMessage() Call for Many Modeless Dialogs)
You panes are modeless dialogs, and IsDialogMessage is responsible for handling Tab keys. I hope that this article exactly matches your case.

The WS_TABSTOP Style
The WS_TABSTOP style specifies the controls to which the user can move by pressing the TAB key or SHIFT+TAB keys.
When the user presses TAB or SHIFT+TAB, the system first determines whether these keys are processed by the control that currently has the input focus. It sends the control a WM_GETDLGCODE message, and if the control returns DLGC_WANTTAB, the system passes the keys to the control. Otherwise, the system uses the GetNextDlgTabItem function to locate the next control that is visible, not disabled, and that has the WS_TABSTOP style. The search starts with the control currently having the input focus and proceeds in the order in which the controls were createdthat is, the order in which they are defined in the dialog box template. When the system locates a control having the required characteristics, the system moves the input focus to it.
If the search for the next control with the WS_TABSTOP style encounters a window with the WS_EX_CONTROLPARENT style, the system recursively searches the window's children.
An application can also use GetNextDlgTabItem to locate controls having the WS_TABSTOP style. The function retrieves the window handle of the next or previous control having the WS_TABSTOP style without moving the input focus.
Source: MSDN.

if( !IsDialogMessage(0, &msg) )
The first argument should not be NULL, it must be the handle of a dialog. Painful here of course.

Related

WS_GROUP, any secret protocol between dialogbox-manager-WndProc and standard control?

I'm reading Charles Petzold Programming Windows 5th-ed, Chapter 11, "Tabs Stops and Groups" section. I have a big question now.
The book says, when some controls belong to the same group, you can use left/right arrow key to switch focus between them, and this feature is used most often with a group of radio boxes. But what about other type of controls?
I tried having 3 button controls grouped together(A,B,C, only A has WS_GROUP, B and C don't). Then, I can confirm left/right arrow can switch focus between A,B and C.
Observing it more carefully, I see difference between radio box and button [P1]:
For a radio box group, pressing left/right-arrow repeatedly will cycle focus among all radio boxes in that group.
For button group, pressing right-arrow repeatedly will have focus move and stop at button C, the same left-arrow has it stop at A, no cycle behavior.
The case for "edit" control [P2]: If I make 3 edit boxes in one group, pressing left/right-arrow will NEVER switch the focus, which is not the same behavior as a button group.
So, my question boils down to: Does windows internal dialog box mananger WndProc (just call it DefDlgProc) treats some type of controls specially(like "edit")? For example, if DefDlgProc finds that a WM_KEYDOWN message with VK_RIGHT is destined for a "edit" control, it will never take the focus-switch action but pass the message to "edit" control honestly.
Is that special treatment done in a hard-coded way or some generic, configurable way? I need to know it because, if I write my own custom editbox control, I need a way to have DefDlgProc treat arrow keys specially for my control, right?
Sample code: For the 3-edit experiment, I use .rc statement like this:
ABOUTBOX DIALOGEX 32, 32, 180, 100
STYLE DS_MODALFRAME | WS_POPUP
EXSTYLE WS_EX_STATICEDGE
FONT 8, "Tahoma"
BEGIN
EDITTEXT IDC_EDIT0,40,7,40,14, ES_AUTOHSCROLL| WS_GROUP ,WS_EX_CLIENTEDGE
EDITTEXT IDC_EDIT1,90,7,40,14, ES_AUTOHSCROLL ,WS_EX_CLIENTEDGE
EDITTEXT IDC_EDIT2,133,7,40,14,ES_AUTOHSCROLL
CONTROL "OOKK",IDOK,"EllipPush",WS_GROUP | WS_TABSTOP,7,63,166, 30
ICON "ABOUT3",IDC_STATIC,7,7,20,20
END
Doing my experiment on Windows 7.
Your question doesn't quite make sense. You wouldn't expect pressing the left or right cursor key in an edit control to shift the focus to another control, because the edit control itself consumes that keypress in order to move the cursor around.
Internally the dialog manager uses GetNextDlgGroupItem() to shift the focus to the next or previous control in the group. This doesn't distinguish between control types - it only looks at the WS_GROUP style. However, the dialog manager only calls this function if the control itself doesn't consume the key, and this is determined by the control's response to the WM_GETDLGCODE message.

oddity with edit controls in property sheet

I have a property sheet with several pages. Most of the pages have one or more edit controls.
Most controls are initialized not from the page dialogs but from the dialog that created the property sheet; some however are initialized in the page dialogs and they behave the same.
Everything starts out fine. One can move between the pages. None of the controls have the input focus.
If one clicks on one of the edit controls in a property sheet page establishing input focus one can modify the control. Again all seems in order.
If one then moves to a different property page, the first edit control in that page gets the input focus AND all the text in that control gets selected! This behavior applies to all the pages except one having an edit control with read only style. After that one can move back to other pages and the initial nothing selected no input focus behavior is restored.
All of the pages handle the PSN_QUERYINITIALFOCUS notification and return zero through the SetWindowLong mechanism.
Is this expected behavior?
And why isn't some control given focus initially?
My primary interest here is to somehow kill the selection. I have tried killing the selection with EM_SETSEL in the PSN_SETACTIVE notification to no avail.
The MSDN says the following under PSN_QUERYINITIALFOCUS "Otherwise, return zero and focus will go to the default control." How do I go about setting a control as default?
I find the the actions described above bizarre! I would still like to know
if they are normal.
why no control receives the focus initially.
I was able to kill the selection by adding code to the property sheet pages to handle the WM_COMMAND/EN_SETFOCUS message for any edit controls. I do not know if other controls
send EN_SETFOCUS messages.
case EN_SETFOCUS:
{
char cn[16];
HWND H = (HWND) lParam;
GetClassName (H, cn, 15);
if (strcmp (cn,"Edit") == 0)
{
SendMessage (H, EM_SETSEL, -1, 0);
}
return true;
}
I presume it would be possible to save any selection in an EN_KILLFOCUS handler and restore it
in the EN_SETFOCUS handler but doing so for an unknown number of controls would be tedious.

Group dialog items to a single "Group" (Visual Studio)

I want to create a dialog window for change settings of an application. Below is a screenshot of Adobe Reader. After using Spy++, I guess that:
On the right side, all the control (buttons, combo boxes...ect) are belonged to a GroupBox.
For each category in the TreeView Control on the left side, there is a corresponding GroupBox which groups all the controls related to this category.
When users choose between different categories, it hides one GroupBox and shows another one GroupBox.
But in the source code (xxx.rc, resource file) below, I didn't see anywhere where I can specify the "parent" of a dialog item.
Even I open xxx.rc with "Resource View" (not viewed as codes), I can't find any option to specify the parent of a dialog item in its property page.
I would like to know how to assign a parent (which is a GroupBox in my case) to a dialog item, or group dialog items to a single group,in the .rc file, i.e when one create the dialog items. (I guess one can do so by modifying the .rc file.)
GROUPBOX "View",IDC_SECTION_VIEW,101,6,228,88
LTEXT "Default &Layout:",IDC_DEFAULT_LAYOUT_LABEL,107,19,108,9
COMBOBOX IDC_DEFAULT_LAYOUT,215,17,108,64,CBS_DROPDOWNLIST | WS_TABSTOP
LTEXT "Default &Zoom:",IDC_DEFAULT_ZOOM_LABEL,107,36,108,9
COMBOBOX IDC_DEFAULT_ZOOM,215,34,108,149,CBS_DROPDOWN | WS_TABSTOP
CONTROL "Show the &bookmarks sidebar when available",IDC_DEFAULT_SHOW_TOC,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,107,53,216,9
...
...
...
I would like to know how to assign a parent...
SetParent Windows API. You supply HWND of your control and the handle of the supposed new parent.
In resource script, the controls will be children of the dialog itself, but on runtime you are free to change this and group them into a hierarchy of your interest.
You might also want to consider putting the supposed child groups into separate dialog template and have it as "composite control" - to be instantiated separately and be a child of a higher level dialog.
UPD. Have a look at this simple project (C++/ATL): AtlChildDialog. In particular, at main dialog's WM_INITIDIALOG handler:
ATLVERIFY(m_ChildDialog.Create(m_hWnd, (LPARAM) this));
ATLVERIFY(m_ChildDialog.MoveWindow(50, 50, 200, 150));
m_ChildDialog.m_EditWindow.SetWindowText(_T("Some Text"));
m_ChildDialog.ShowWindow(SW_SHOWNORMAL);
m_ChildDialog.SetFocus();
All together on runtime:

Detect application windows

I use CBT Windows Hook to detect window creation/deletion/min-max/move-size events.
I works well, but I need to filter whose events coming from normal widgets. Practically I need to being notified by CBT hook only for those windows that the user consider windows.
The problem that I'm facing make me mad, since I continuosly get spurious events even if I filter window as follow:
BOOL FilterWindowHandle(HWND hwnd)
{
// Filtered window having WS_CHILDWINDOW style
if ((GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILDWINDOW) != 0)
return (TRUE);
// Filtered window not having WS_CAPTION style
if ((GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CAPTION) == 0)
return (TRUE);
// Not filtered
return (FALSE);
}
Those spurious events comes from shadow effects, menus and everything displayed on screen.
Is there a robust method to filter real windows from its children?
I avoid the test of WS_BORDER or similar, since some applications could create their main window without border... or am I wrong?
A good fit for "things the user considers windows" is the set of windows displayed in the Alt-Tab list (or on the Taskbar).
This OldNewThing article explains the rules (although the rules are not fixed or guaranteed to remain the same):
Which windows appear in the Alt+Tab list?
The general rule is:
For each visible window, walk up its
owner chain until you find the root
owner. Then walk back down the visible
last active popup chain until you find
a visible window. If you're back to
where you're started, then put the
window in the Alt+Tab list.
This can be overridden with explicit window styles:
A window with the WS_EX_TOOLWINDOW
extended style is treated as if it
weren't visible, even if it is. A
window with the WS_EX_APPWINDOW
extended style is treated as if it has
no owner, even if it does.
See the full OldNewThing post which those two quotes come from for more detail.
A useful criteria that I've used in the past is to test whether the window is a top-level window, i.e. its parent is NULL.

How do you programmatically change the tab order in a Win32 dialog?

Often time I need to add a control to a dialog after the dialog has been generated via dialog template and CreateDialogIndirect. In these cases the tab order is set by the dialog template and there is no obvious way to change the tab order by including a newly created control.
I recently discovered that you can use SetWindowPos to accomplish this. Determine which control after which you want to insert the new control in the tab order then use SetWindowPos like this:
SetWindowPos(hNewControl, hOldControl, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
This changes the z-order of controls which, in turn, establishes the tab order.
I know this is an old question but here is how to do it at compile time (which is preferable in the vast majority of cases):
http://msdn.microsoft.com/en-us/library/7039hzb0(v=vs.80).aspx
My favourite method:
From the View menu, choose Tab Order.
Choose Assign Interactively.
Double-click the tab order box beside the control you want to be the
first control in the tab order.
Click the tab order box for each of the other controls.
Click anywhere on the form to save your changes and exit Tab Order
mode, or press ESC to exit Tab Order mode without saving your
changes.

Resources