Search code examples
c++mfcribbonmfc-feature-pack

MFC Ribbon - get base element clicked from command


I have a CMFCRibbonUndoButton on the ribbon of an MFC application. I have a handler for when its ID is clicked (ON_COMMAND(ID_EDIT_UNDO, ...)). However, when the button is also in the quick access toolbar (QAT), there are apparently two CMFCRubbonUndoButtons which each keep their own state. In the command handler, I don't know how to tell which got clicked, and if you call GetActionNumber() on the wrong one, you get the wrong number of undo actions returned.

Is there a way in my ON_COMMAND handler to get the CMFCRibbonBaseElement* that fired the event?

Edit: the answer is important to me, the question is a bit obscure but I'm putting a bounty up!

Edit: here's how it's added to the QAT:

CList<UINT, UINT> lstQATCmds;
lstQATCmds.AddTail(ID_EDIT_UNDO);
m_RibbonBar.SetQuickAccessCommands(lstQATCmds);

Solution

  • Is there a way in my ON_COMMAND handler to get the CMFCRibbonBaseElement* that fired the event?

    Not directly, no. The WM_COMMAND message is sent from CMFCRibbonBaseElement::NotifyCommand, and this message doesn't include the pointer in its parameters.

    To be able to tell which Undo button was clicked from the ON_COMMAND handler, I wrote this class, which inherits CMFCRibbonUndoButton. What this code does is store a pointer to the last activated Undo button each time one of the buttons is clicked, or the popup menu activated.

    // CMyMFCRibbonUndoButton.h
    
    class CMyMFCRibbonUndoButton : public CMFCRibbonUndoButton
    {
        DECLARE_DYNCREATE(CMyMFCRibbonUndoButton)
    
    public:
        CMyMFCRibbonUndoButton();
        CMyMFCRibbonUndoButton(UINT nID, LPCTSTR lpszText,
            int nSmallImageIndex = -1, int nLargeImageIndex = -1);
    
        virtual void OnClick(CPoint point);
        virtual void OnShowPopupMenu();
    
        static CMyMFCRibbonUndoButton* GetLastActivated();
    
    private:
        static CMyMFCRibbonUndoButton* s_pLastActivated;
    };
    
    // CMyMFCRibbonUndoButton.cpp
    
    IMPLEMENT_DYNCREATE(CMyMFCRibbonUndoButton, CMFCRibbonUndoButton)
    
    CMyMFCRibbonUndoButton* CMyMFCRibbonUndoButton::s_pLastActivated = NULL;
    
    CMyMFCRibbonUndoButton::CMyMFCRibbonUndoButton()
    {
    }
    
    CMyMFCRibbonUndoButton::CMyMFCRibbonUndoButton(UINT nID, LPCTSTR lpszText,
        int nSmallImageIndex, int nLargeImageIndex) :
        CMFCRibbonUndoButton(nID, lpszText, nSmallImageIndex, nLargeImageIndex)
    {
    }
    
    void CMyMFCRibbonUndoButton::OnClick(CPoint point)
    {
        s_pLastActivated = this;
        CMFCRibbonUndoButton::OnClick(point);
    }
    
    void CMyMFCRibbonUndoButton::OnShowPopupMenu()
    {
        s_pLastActivated = this;
        CMFCRibbonUndoButton::OnShowPopupMenu();
    }
    
    CMyMFCRibbonUndoButton* CMyMFCRibbonUndoButton::GetLastActivated()
    {
        return s_pLastActivated;
    }
    

    Use this class in place of CMFCRibbonUndoButton when initialising your ribbon bar. In your handler function, call GetLastActivated() to retrieve this pointer, for example:

    void CMyTestDoc::OnEditUndo()
    {
        CMyMFCRibbonUndoButton* pUndoButton =
            CMyMFCRibbonUndoButton::GetLastActivated();
    
        ASSERT_VALID(pUndoButton);
    
        if (pUndoButton != NULL)
        {
            int ActionNumber = pUndoButton->GetActionNumber();
            // etc.
        }
    }
    

    This is a bit of a hack, certainly, but it was about the only way I could find to solve the problem.

    Anyway, I hope this helps,

    Chris