Adrian
Adrian

Reputation: 10911

How do I create a dynamic menu for a ribbon button?

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.

Upvotes: 0

Views: 1574

Answers (1)

thomiel
thomiel

Reputation: 2937

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.

Upvotes: 1

Related Questions