0

I made a little app in C++ and I have two pairs of menus; one for the main window, and one for the tray icon I put there. I'm trying to put a checkmark next to a specific menu item in the tray icon context menu, but I can't get this to work.

if (!isCheckmarked)
{
    CheckMenuItem(cSubMenu, IDC_STARTWIN, MF_CHECKED);
    OutputDebugString(_T("Checkmarked!\n"));
    _RPT2(_CRT_WARN, "Menu Handles: %i, %i\n", cMenu, cSubMenu);

    isCheckmarked = TRUE;
}
else
{
    //CheckMenuItem(hMenu, IDC_STARTWIN, MF_UNCHECKED);
    OutputDebugString(_T("Uncheckmarked!\n"));
    //_RPT2(_CRT_WARN, "Menu Handles: %i, %i\n", cMenu, cSubMenu);

    isCheckmarked = FALSE;
}
break;

It works fine, if I use the menu handle of the main window to set a checkmark on one of those items, but I can't get it to work on the tray icon context menu.

void ShowContextMenu(HWND hWnd, POINT pt)
{
    cMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDC_CONTEXT_MENU));
    if (cMenu)
    {
        cSubMenu = GetSubMenu(cMenu, 0);
        if (cSubMenu)
        {
            // our window must be foreground before calling TrackPopupMenu or the menu will not disappear when the user clicks away
            SetForegroundWindow(hWnd);

            // respect menu drop alignment
            UINT uFlags = TPM_RIGHTBUTTON;
            if (GetSystemMetrics(SM_MENUDROPALIGNMENT) != 0)
            {
                uFlags |= TPM_RIGHTALIGN;
            }
            else
            {
                uFlags |= TPM_LEFTALIGN;
            }

            TrackPopupMenuEx(cSubMenu, uFlags, pt.x, pt.y, hWnd, NULL);
        }

        DestroyMenu(cMenu);
    }
  • I am not clear about what is "*tray icon*" you are talking about. Could you show some snapshots like what you have got and what you expected? – Rita Han Jul 06 '20 at 02:39
  • @RitaHan-MSFT My application uses Shell_NotifyIcon() to iconify to the system tray. This iconified icon has a context menu and on one of the menu items, I want to set a check mark. (I'm using the NotificationIcon sample (from the Window 7 SDK samples). I'm having problems with setting the check mark. (doesn't show up). – Kees Spierings Jul 07 '20 at 12:33
  • @RitaHan-MSFT Context menu image: [link](https://i.stack.imgur.com/iAaDw.png) – Kees Spierings Jul 07 '20 at 12:45
  • ShowContextMenu never calls CheckMenuItem so it's not surprising that nothing is checked. – Raymond Chen Jul 07 '20 at 14:20
  • @KeesSpierings Does the answer work for you? – Rita Han Jul 09 '20 at 06:30
  • @RitaHan-MSFT Just tried it, works perfectly! – Kees Spierings Jul 11 '20 at 12:11
  • @RaymondChen I thought I could catch the message and do my processing from there, as long as I had the menu handle.Doesn't work that way, it seems. – Kees Spierings Jul 11 '20 at 12:14
  • I think what you're failing to recognize is that when you call `LoadMenu`, you get a brand new menu. The brand new menu takes its initial check box state from your menu resource. If you want to show your window's menu (with its custom checkbox state), then use `GetMenu`, not `LoadMenu`. – Raymond Chen Jul 11 '20 at 13:49

1 Answers1

0

Based on the official NotificationIcon Sample modify ShowContextMenu function as below work for me. You can have a try.

    BOOL mOptionsChecked = FALSE;
    
    void ShowContextMenu(HWND hwnd, POINT pt)
    {
        UINT menuItemId = 0;
    
        HMENU hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDC_CONTEXTMENU));
        if (hMenu)
        {
            HMENU hSubMenu = GetSubMenu(hMenu, 0);
            if (hSubMenu)
            {
                // our window must be foreground before calling TrackPopupMenu or the menu will not disappear when the user clicks away
                SetForegroundWindow(hwnd);
    
                // If the menu item has checked last time set its state to checked before the menu window shows up.
                if (mOptionsChecked)
                {
                    //CheckMenuItem(hSubMenu, IDM_OPTIONS, MF_BYCOMMAND | MF_CHECKED);

                    MENUITEMINFO mi = { 0 };
                    mi.cbSize = sizeof(MENUITEMINFO);
                    mi.fMask = MIIM_STATE;
                    mi.fState = MF_CHECKED;
                    SetMenuItemInfo(hSubMenu, IDM_OPTIONS, FALSE, &mi);
                }
    
                // respect menu drop alignment
                UINT uFlags = TPM_RIGHTBUTTON;
                if (GetSystemMetrics(SM_MENUDROPALIGNMENT) != 0)
                {
                    uFlags |= TPM_RIGHTALIGN;
                }
                else
                {
                    uFlags |= TPM_LEFTALIGN;
                }
    
                // Use TPM_RETURNCMD flag let TrackPopupMenuEx function return the menu item identifier of the user's selection in the return value.
                uFlags |= TPM_RETURNCMD;
                menuItemId = TrackPopupMenuEx(hSubMenu, uFlags, pt.x, pt.y, hwnd, NULL);
    
                // Toggle the menu item state. 
                if (IDM_OPTIONS == menuItemId)
                {
                    if(mOptionsChecked)
                        mOptionsChecked = FALSE;
                    else
                        mOptionsChecked = TRUE;
                }
    
            }
            DestroyMenu(hMenu);
        }
    }

Result:

enter image description here

Tips:

  1. Set menu item state before the menu window shows up.
  2. CheckMenuItem may be altered or unavailable in subsequent versions. Instead, use SetMenuItemInfo.
Rita Han
  • 9,574
  • 1
  • 11
  • 24