0

I know how to generate a static menu item for a button with the following code:

CBCGPRibbonCategory *pCategory = ...;
CBCGPRibbonPanel *pPanel = pCategory->AddPanel(_T("Panel Name"), hPanelIcon);

HMENU hMenu = CreateMenu();
AppendMenu(hMenu, MF_STRING, ID_ITEM_1, _T("Item 1"));
AppendMenu(hMenu, MF_STRING, ID_ITEM_2, _T("Item 2"));

CBCGPRibbonButton * pButton = new CBCGPRibbonButton(ID_BUTTON_WITH_MENU, _T(""))
pButton->SetMenu(hMenu, TRUE); // for dynamic
pPanel->Add(pButton);

But if I want the menu to be dynamic, what do I do? I tried to modify the original HMENU by overriding the OnShowPopupMenu() and changing the HMENU that I attached to that button, but that didn't work.

I was able to force a menu under the button by creating a menu and using the TrackPopupMenu() function but the style is wrong. (Looks grey instead of white and some other differences).

EDIT

To solve this, I did the following:

class CDynamicMenuButton : public CBCGPRibbonButton
{
public:
    CDynamicMenuButton(
        UINT    nID,
        LPCTSTR lpszText,
        int     nSmallImageIndex = -1,
        int     nLargeImageIndex = -1,
        BOOL    bAlwaysShowDescription = FALSE)
        : CBCGPRibbonButton(nID, lpszText, nSmallImageIndex, nLargeImageIndex, bAlwaysShowDescription)
    {
        SetMenu(CreatePopupMenu());
    }

    void OnShowPopupMenu() override
    {
        // legacy code to generate menu
        CMenu newMenu;
        populateMenu(&newMenu);

        // sets the new menu
        SetMenu(newMenu);

        // Pops up the new menu
        CBCGPRibbonButton::OnShowPopupMenu();
    }
};

Adding this button to a panel will then generate the dynamic menu that I want.

Adrian
  • 10,246
  • 4
  • 44
  • 110

1 Answers1

1

The ribbon seems to only use the hMenu as a template to build it's own structure from it, so modifying the hMenu is vain. Better work with the existing Ribbon Menu Button:

    pBtn->RemoveAllSubItems(); // add a dummy hMenu when creating the menu button in CMainFrame!

    std::auto_ptr<CMFCRibbonButtonEx> apBtn3(new CMFCRibbonButtonEx(ID_DYNAMIC_MENU_ITEM_3, "Item 3", -1, -1, true));
    pBtn->AddSubItem(apBtn.release());

    std::auto_ptr<CMFCRibbonButtonEx> apBtn4(new CMFCRibbonButtonEx(ID_DYNAMIC_MENU_ITEM_4, "Item 4", -1, -1, true));
    pBtn->AddSubItem(apBtn.release());

But make sure to update the menu at the right place in your code. Changing the menu in CMyView::OnUpdate() proved to be not such a good idea (see here). If you need to modify the menu when opening a mdi document, consider OnInitialUpdate(). I haven't tried OnCmdMsg() yet.

Maybe its sufficient to get pBtn via CMFCRibbonBar::FindByID() but maybe its the right thing to iterate through CMFCRibbonBar::GetElementsByID and change each menu button you find (i.e. to also modify the quick access toolbar?)... I found the documentation is not very specific about that but modifying via ´FindByID´ seems to be sufficient in my code.

If you have any further revelations about dynamic ribbon menus, please leave me a comment.

Community
  • 1
  • 1
thomiel
  • 2,467
  • 22
  • 37
  • Thanks. What I need it a lazy dynamic menu. I.e. when the menu is called for, I want to generate it. I was going to try and generate it in the `OnShowPopupMenu()` override, but this prevents the menu from popping up. How do I force the menu to come up? – Adrian Sep 09 '14 at 14:14
  • Ah, got it. I just call the parent member function `OnShowPopupMenu()`. Thanks again. – Adrian Sep 09 '14 at 14:32
  • Force will not get you anywhere here. OnShowPopupMenu refers to the cannibalized hMenu of you C(MDI)FrameWndEx. It's dead and necromancy won't help you. You need to deal with `CMFCRibbonBar` now, which had to abandon good old CMenu in order to make all the nice features of the ribbon possible. Sorry, there is no lazy way to build the menu as far as I knoe. If you find one, let me know. – thomiel Sep 09 '14 at 14:36
  • I just built the menu in the `OnShowPopupMenu()` override (in my case, it was an CMenu which I used `SetMenu()` on due to legacy code). I then called the base class's `OnShowPopupMenu()` and volia! It popped up the newly generated menu! :D – Adrian Sep 09 '14 at 14:40
  • But this isn't going to be a ribbon menu, isn't it? – thomiel Sep 09 '14 at 14:42
  • The `SetMenu()` call did probably what you said, translated it to its internal rep. Then calling the base function did the rest of the popping up. – Adrian Sep 09 '14 at 14:44
  • Sometimes Microsoft surprises me. :) Maybe you should add your own answer with a little bit of code... – thomiel Sep 09 '14 at 14:53
  • Added it to the question. Although your answer isn't the one I was looking for, it helped me think this though. Thanks. :) – Adrian Sep 09 '14 at 14:54