How to setup multiple radio button groups for proper tab-order and keyboard interaction (WIN32)?
Asked Answered
H

2

8

First of all this is not MFC.
Here is a cropped version of the GUI I have been working on:

enter image description here

As you can see I have (attempted) to create two different groups, Icon and Button, using the code:

    index->hAddT.hwndIndex[2] = CreateWindowEx(NULL,L"BUTTON",L"Icon",WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
    200,135,120,170,WINDOWHANDLE,(HMENU)IDC_RADIOGROUP,(HINSTANCE)GetWindowLong(WINDOWHANDLE,GWL_HINSTANCE),NULL);

The issue I have, and what you can probably see, is that there is only one radio button for the window. This means that the user couldn't possibly select one radio button from the Icon group, and one from the Button group. I have initialised each Radio button as so:

    index->hAddT.hwndIndex[3] = CreateWindowEx(NULL,L"BUTTON",L"Information",WS_CHILD | BS_AUTORADIOBUTTON | WS_VISIBLE,
    205,155,100,20,WINDOWHANDLE,(HMENU)IDC_RADIO1,(HINSTANCE)GetWindowLong(WINDOWHANDLE,GWL_HINSTANCE),NULL);       

I would like to, somehow, have the "Icon" group of radio buttons seperate from the "Button" group of radio buttons, if that makes sense, and therefore will have one radio button available to each group. How will this be possible, will it require me to make a new window and a new callback procedure just to have an extra radio button. There must be another way to group child items like so.

2 separate groups of radio buttons in the same form WINAPI (No MFC) The link was not of any use for my purpose.

I have Programming For Windows Fifth Edition, by Charles Petzold beside me as reference, and he states in the Group Boxes section "Group boxes are often used to enclose other button controls", yet there is not real example of this.

Hagan answered 25/11, 2012 at 10:17 Comment(0)
H
17

Contrary to popular opinion, you do NOT need an groupbox control, or any other such outer "container" (which a groupbox isn't anyway, its just a button artifact). The following describes how you can do this with no requirement of a group-box. If you want a group box that functionally assists in the layout described here, forward down to the EDIT portion of this answer, where I explain how the OP's specific desires can be achieved.

Auto-Radio button "banks" work by using two key window style attributes, WS_GROUP, and WS_TABSTOP. Do the following for your two "banks" which I will affectionately called Bank1 and Bank2:

  1. Bank 1: the first radio button should have both WS_GROUP | WS_TABSTOP in the control style. the remaining radio buttons should have neither of those, and must be in sibling order (meaning in DIALOG script they immediately follow each other; in dynamic creation they are created sequentially).

  2. The first child control after your last radio button in Bank1 should have at least WS_GROUP style, and WS_GROUP | WS_TABSTOP if it is a tab-stopped control.

  3. Bank 2: the first radio button should have both WS_GROUP | WS_TABSTOP in the control style. the remaining radio buttons should have neither of those, and must be in sibling order (meaning in DIALOG script they immediately follow each other; in dynamic creation they are created sequentially).

  4. The first child control after your last radio button in Bank2 should have at least WS_GROUP style, and WS_GROUP | WS_TABSTOP if it is a tab-stopped control.

Layout like the above allows you to "tab" to a radio button bank, and use the arrow keys to switch selections. You then "tab" again to leave that bank and head to the next tab-stop. Remember, the dialog manager will always move to the next WS_TABTOP child control when you hit Tab (or prior with Shift-Tab). If the control being hopped to is an auto-type the control selected will be the 'selected' control within the most recent WS_GROUP.

If it helps, grab a sketch pad, draw it on paper, and stick a "T" on the tab-stops and a "G" on the group attributes as described above. It will probably be much clearer once visualized. Look at a dialog resource script to see how these work together for more insight.

Notes: If you want to use group boxes surrounding these you can. The dialog manager works by associating controls to groups based on the last control that was flagged with WS_GROUP, and the first control thereafter that has WS_TABSTOP is considered the tab-jump-in point for that group. Inserting a Groupbox first (which can't be a tabstop) followed by the radio button controls with WS_TABSTOP on the first radio button (no WS_GROUP this time), will also work. I generally find it easier to just arrange my radio buttons without dependence on groupboxes.

EDIT A Picture Speaks a Thousand Words

For your picture I would probably create the following children in the following order:

  1. "Icon" groupbox, including WS_GROUP style.
  2. "Information" auto-radio button, including WS_TABSTOP
  3. All other "Icon" group radio buttons. Do NOT include WS_TABSTOP or WS_GROUP.
  4. "Button" groupbox, including WS_GROUP style. This closes the current control group and start the next.
  5. "AbortretryIgnore" auto-radio button, including WS_TABSTOP
  6. All other "Button" group radio buttons. Do NOT include WS_TABSTOP or WS_GROUP.
  7. The next control after the "Button" radio buttons must include WS_GROUP. This closes the current control group and starts the next.

Obviously all the other child control styles, visibility, etc need to be used correctly as well, and of course the children should all have unique ids. I'm assuming you already have the rest of that covered.

Husserl answered 25/11, 2012 at 10:33 Comment(3)
Bonus points if you could point to MSDN documentation describing this.Operant
@Operant A literal text describing how WS_GROUP works can be found briefly here, but a "better" (and I use that term loosely) description from MS is here. At least the latter presents the "arrow key" concept with some reasonable clarity.Husserl
Duh. sorry I missed this one as well, which is probably the best of all of them. The overall document has a ton of info on efffective dialog programming.Husserl
E
0

Note this works also with a group of checkboxes, which is a valid use case.

Note in case the first radio button may be disabled (e.g. by a runtime condition) you must make the first enabled one have the WS_TABSTOP style. Here's some lines of code assuring this:

    // Set WS_TABSTOP to the group's first enabled item
    for (HWND hwnd = GetDlgItem(IDC_FIRSTITEM) /*the first*/; hwnd != NULL && !::IsWindowEnabled(hwnd); hwnd = ::GetWindow(hwnd, GW_HWNDNEXT))
    {
        if ((GetWindowLong(hwnd, GWL_STYLE) & WS_GROUP) != 0)
            hwnd = NULL;
    }
    if (hwnd != NULL)
        ModifyStyle(hwnd, 0, WS_TABSTOP, 0);
Elouise answered 15/4, 2020 at 11:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.