MFC Ribbon: RemoveAllSubItems() in CView::OnUpdate

2019-08-24 18:46发布

问题:

To reproduce the behavior, start a new MFC Outlook style project with Ribbon (CMyView as the view class name).

Let's say I want to modify a menu of a CMFCRibbonButton, for example the subitems of the Print command, and for this, I want to RemoveAllSubItems() first.

Add this as a public member in MainFrm.h:

CMFCRibbonButton *m_pBtnPrint;

Add this in the InitializeRibbon() member funcion in MainFrm.cpp:

CMFCRibbonButton* pBtnPrint = new CMFCRibbonButton(ID_FILE_PRINT, strTemp, 6, 6);
// store the pointer to the button to be used in CMyView class
m_pBtnPrint = pBtnPrint;   

In CMyView::OnUpdate() add this code:

CMFCRibbonButton *pBtnPrint = ((CMainFrame*)AfxGetMainWnd())->m_pBtnPrint;
if (pBtnPrint) pBtnPrint->RemoveAllSubItems();  

My best guess is that something goes wrong with the frameworks internal replication of the Print command, e.g. for the Quick Access Toolbar. I wonder if I'm missing something here. Is it somehow forbidden to modify menus after the ribbon bar has been created in CMainFrame?

Edit: It obviously has nothing to do with m_pBtnPrint being invalidated e.g. after LoadFrame(). If you retreive pBtnPrint dynamically using CMFCRibbonBar::FindByID(), it will crash as well:

CMFCRibbonBar *pRibbon = ((CMDIFrameWndEx*) AfxGetMainWnd())->GetRibbonBar();
CMFCRibbonButton *pBtnPrint = DYNAMIC_DOWNCAST(CMFCRibbonButton, pRibbon->FindByID(ID_FILE_PRINT)); 
if (pBtnPrint) pBtnPrint->RemoveAllSubItems();  

Edit: I have started a support query on the Microsoft Connect website, but no meaningful response since.

回答1:

CmainFrame::LoadFrame() overrides your pointer. If you have registry values saved from the last time you ran and closed this program, then LoadFrame will serialize the old state of the MFC buttons. The memory locations change during this time, and your saved pointers during Initialize will be invalid. You must find a way to grab the pointer to the button AFTER the LoadFrame has been ran.

I created a setup function on all my MFCToolbars just to grab these custom button pointers after the new buttons have been created from registry. I call this Setup function in my CWinAppEx::InitInstance after the call to CMainFrame::LoadFrame()

You should be able to run fine if you delete the registry values saved by this program, but upon second run it will crash.