Question

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);
Was it helpful?

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

OTHER TIPS

Have a look at the MSOffice2007Demo in the Visual C++ 2008 Feature Pack examples

They use a different technique where they trap a registered message (AFX_WM_ON_BEFORE_SHOW_RIBBON_ITEM_MENU) in this handler they rebuild the undo list dynamically (similar to the old SDK WM_INITMENUPOPUP handling).

The CMFCRibbonUndoButton that provoked the message is passed in the LPARAM of the message.

Using this technique you would maintain your undo list independently of the ribbon controls and use the control as a view into your list.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top