views:

168

answers:

1

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);
+1  A: 

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

ChrisN
Thanks, but when MFC clones the undo button behind-the-scenes, it creates an ordinary CMFCRibbonUndoButton - not the derived class, so it never updates `s_pLastActivated` from the QAT button!
AshleysBrain
Interesting, this code works nicely in my test application, and when MFC clones the button it creates a `CMyMFCRibbonUndoButton` object. MFC calls `GetRuntimeClass()->CreateObject()` on the button, so it should create an object of the right class.
ChrisN
Ah - I must've missed something - I'll try again.
AshleysBrain
That worked! Thanks for your help, enjoy the rep :)
AshleysBrain