How can I make a CMenu TrackPopupMenu SubMenu menu

2019-09-04 03:33发布

问题:

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

回答1:

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.



回答2:

For future StackOverflowers, here's what I did...

  1. 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.
  2. 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.
  3. 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.
  4. 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).
  5. 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.



标签: windows mfc