Normally when you hover over a sub-menu (with the little arrow) on a CMenu menu item it delays briefly then shows the sub-menu items. Also, if you click the item before the delay timeout, it shows the sub menu items. I want the delay behavior, but I want a different behavior for the click. That is, I want the sub-menu itself (the one with the arrow) to be a clickable entity too, i.e. it has an ID and results in a WM_COMMAND and menu dismissal.
The idea is, the main sub-menu menu item is a "default", and the sub-menu items are modified versions, e.g. "print->" (defaulting to default printer), and sub-menu items like "print preview" "print to file" etc. Thanks for thoughts/suggestions.
edit:
IDR_MY_MENU MENUEX
BEGIN
POPUP "menu"
BEGIN
MENUITEM "&Something Else", ID_MENU_SOMETHING_ELSE
POPUP "&Print", ID_MENU_PRINT
BEGIN
MENUITEM "Print Pre&view", ID_MENU_PRINT_PREVIEW
MENUITEM "Print to &File", ID_MENU_PRINT_TO_FILE
END
MENUITEM "", -1, MFT_SEPARATOR
MENUITEM "&Bottom", ID_MENU_BOTTOM
MENUITEM "&Done", ID_MENU_DONE
END
END
I dont know if theres a better way as I last did this 2 years ago, but the way I solved the problem had one constraint: that you own the whole menu. If you do own the whole menu what you can do is create two columns (two columns in the menu/submenu that is, not a new submenu) and use the right column as a submenu and the left column as the default.
For future StackOverflowers, here's what I did...
- Added a handler for
OnInitMenuPopup
and corresponding
ON_WM_INITMENUPOPUP()
, although doing this messed up my UPDATE_COMMAND_UI
handler but I was able to resolve that by moving that code into the OnInitMenuPopup
handler.
- In the
OnInitMenuPopup
handler, set a hook for the mouse SetWindowsHookEx(WH_MOUSE,...)
(checking if one isn't already set because sub-menus can cause multiple calls)... and for each menu that initializes during the hook, push the HMENU
onto a linked list.
- In the mouse hook proc, checking the end of the list and working toward the front, verify each
HMENU
is still and active menu via IsMenu
and remove it if it isn't. If there are no menus left, UnhookWindowsHook
.
- If there are menus left in the hook proc, check for
WM_LBUTTONUP
or WM_RBUTTONUP
and see if happened over one of your menu items (because of coordinate mapping of screen versus sub-menu that I couldn't figure out I ended up simply cycling through the menu items via GetMenuItemRect
and checking PtInRect
to determine this).
- If there is a click hit, do
GetMenuItemInfo
on the item, and if there's a hSubMenu
for that item and it has an wID
(and the wID
doesn't match the sub-menu handle), simply post the id as a WM_COMMAND
, post a WM_CANCELMODE
to dismiss the menu, and disable the hook... Bingo!
So, seems to work fine and everything else functions like normal. The only issue at this point is a keyboard handler for selecting the item instead opening the sub-menu but I suspect the same idea works there as well.
Also, I added the main sub-menu text as the first item in the sub-menu list with a separator which added some clarity to what the menu was doing.