Previously - Fun with menus - Part I
A few days back I set about trying to get radio items into menus. This is helpful if you want only one of the items in the menu to be active at a time, say, for e.g. a "Select Level" popup menu which has "Level 1", "Level 2" and "Level 3" subitems and only one of them needs to be shown as active or currently selected. You get the idea. No? Here, this pic should help,
A bit of searching led me to the CheckMenuRadioItem API, and I thought, this is simple. I just need to get the handle to the popup menu, and pass the identifier of the subitem I want selected. And done. Turns out 'get the handle to the popup menu' part isn't very straight forward.
For future references in this post, here is the menu bar and the popup menu that I used in the resource script (refer pic above)
the menubar, IDR_MENU SHMENUBAR DISCARDABLE BEGIN IDR_MENU_POPUP, 2, I_IMAGENONE, IDM_OK, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE, IDS_OK, 0, NOMENU, I_IMAGENONE, IDR_MENU_POPUP, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_MENU, 0, 0, END
the popup menu, IDR_MENU_POPUP MENU DISCARDABLE BEGIN POPUP "Menu" BEGIN MENUITEM "Toolbox..", IDM_MENU_TOOLBOX POPUP "Speed" BEGIN MENUITEM "8x", IDM_MENU_8X MENUITEM "16x", IDM_MENU_16X MENUITEM "32x", IDM_MENU_32X END MENUITEM "Settings..", IDM_MENU_SETTINGS MENUITEM SEPARATOR MENUITEM "Exit", IDM_MENU_EXIT END END
What I did wrong
I looked up the functions available with menus and it seemed easy, LoadMenu, GetSubMenu and CheckMenuRadioItem should do the trick, so here is the handler for the menuitem '8x', under WM_COMMAND; the handler tries to set '8x' as the current selection,
case IDM_MENU_8X: { HMENU hMenu = NULL, hSubMenu = NULL, hSubSubMenu = NULL;
//handle to the menu bar hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_MENU_POPUP));
if (hMenu) { TCHAR szText[128] = TEXT(""); int i = -1;
//handle to the menu "Menu" hSubMenu = GetSubMenu(hMenu, 0);
MENUITEMINFO mii;
ZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_TYPE; mii.fType = MFT_STRING; mii.dwTypeData = szText; mii.cch = 127; i = GetMenuItemInfo(hSubMenu, 1, TRUE, &mii); // mii.dwTypeData = "Speed"
ZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_TYPE; mii.fType = MFT_STRING; mii.dwTypeData = szText; mii.cch = 127;
//handle to the menu "Speed" hSubSubMenu = GetSubMenu(hSubMenu, 1);
i = GetMenuItemInfo(hSubSubMenu, 1, TRUE, &mii); //mii.dwTypeData = "16X"
i = CheckMenuRadioItem(hSubSubMenu, IDM_MENU_8X, IDM_MENU_32X, IDM_MENU_8X, MF_BYCOMMAND); } } break;
GetMenuItemInfo api and MENUITEMINFO structure were used while debugging, just to make sure that the menu handles that I was getting were the right ones. All API calls LoadMenu, GetSubMenu and CheckMenuRadioItem were successful, from the return values at least. There was one problem however, the radio item 'dot' never showed up. Then I tried to experiment a bit with getting menu handles by position, the result still the same. When I ran out of tricks to pull, I tried to set the radio menu using SetMenuItemInfo, and a few random variations of GetSubMenu by menu identifier and by position etc. Darn thing never showed up! It all meant one thing only, there must be something wrong with the menu handles themselves, even though they were valid.
The right way
So next I looked up the samples that ship with the SDK and found a CECamera sample that made use of radio menu items. From what I could tell, it looked like LoadMenu isn't the right way to get a handle to the main popup menu. Under WM_CREATE message when you are setting up the application and creating the menu bar using SHCreateMenuBar, you also do the following,
if (!SHCreateMenuBar(&mbi)) { g_hWndMenuBar = NULL; } else { g_hWndMenuBar = mbi.hwndMB;
//get a handle to the main popup menu g_hMainMenu = (HMENU)SendMessage(mbi.hwndMB, SHCMBM_GETSUBMENU, 0, IDR_MENU_POPUP);
//set the default selection to 8x hSubMenu = GetSubMenu(g_hMainMenu, 1); CheckMenuRadioItem(hSubMenu, IDM_MENU_8X, IDM_MENU_32X, IDM_MENU_8X, MF_BYCOMMAND); }
So you send a message to the main menubar window, asking for the popup menu handle using SHCMBM_GETSUBMENU. g_hMainMenu and hSubMenu are both HMENU types. When the application showed up '8x' was selected by default. The handlers for 16x and 32x set themselves as the current selection, so under WM_COMMAND we have,
case IDM_MENU_8X: hSubMenu = GetSubMenu(g_hMainMenu, 1); CheckMenuRadioItem(hSubMenu, IDM_MENU_8X, IDM_MENU_32X, IDM_MENU_8X, MF_BYCOMMAND); break;
case IDM_MENU_16X: hSubMenu = GetSubMenu(g_hMainMenu, 1); CheckMenuRadioItem(hSubMenu, IDM_MENU_8X, IDM_MENU_32X, IDM_MENU_16X, MF_BYCOMMAND); break;
case IDM_MENU_32X: hSubMenu = GetSubMenu(g_hMainMenu, 1); CheckMenuRadioItem(hSubMenu, IDM_MENU_8X, IDM_MENU_32X, IDM_MENU_32X, MF_BYCOMMAND); break;
It worked. Video follows,
