Я работаю над приложением MFC MDI. Я хотел бы выборочно включать или отключать некоторые команды контекстного меню, вызываемые правой кнопкой мыши, но команды меню остаются активными после того, как я реализовал функциональность, используя ON_UPDATE_COMMAND_UI для отключения определенных команд.
Похоже, что сообщение ON_UPDATE_COMMAND_UI не запускается илиобрабатываются в соответствующее время.
Цель состоит в том, чтобы реализовать контекстное меню CTreeCtrl, которое зависит от выбранного элемента дерева .
Моя структура приложения
Основной CWinAppEx запускает CMDIFrameWndEx. Объект CMDIFrameWndEx содержит обычные дочерние кадры MDI, но он также содержит CDockablePane, который сам содержит CTreeCtrl, предназначенный для аналогичного использования в виде дерева проекта или решения в Visual Studio.
В моем классе реализованы обработчики сообщений ON_UPDATE_COMMAND_UI, унаследованные от CTreeCtrl. Эти обработчики вызываются, когда я нажимаю на сами пункты меню , но это слишком поздно;они должны были быть вызваны до открытия меню.
Я подозреваю, что это связано с маршрутизацией команд (или сообщений);Поиск в Google указывает, что некоторые элементы управления или классы окон не получают сообщений ON_COMMAND_UPDATE_UI, потому что они обрабатываются на уровне CFrameWnd. Тем не менее, обходные пути или решения, представленные в этих обсуждениях, четко не сформулированы. Я хочу придерживаться распространенных идиом MFC / MDI, поэтому я надеюсь получить несколько исчерпывающее объяснение этой проблемы для начинающих.
Не предназначены ли окна CDockablePane (или элементы управления CTreeCtrl) для взаимодействия с ON_COMMAND_UPDATE_UI? Почему это? Я что-то упускаю?
Мой инстинкт состоит в том, что это должно быть возможно без связывания множества событий самостоятельно, потому что инфраструктура MFC должна доставлять сообщения любым классам, которые могут их обрабатывать. Я начинаю думать, что в этом поведении есть некоторая тонкость, которую мне не хватает.
Код
Из моего класса, наследующего CTreeCtrl (CLCPViewTree):
BEGIN_MESSAGE_MAP(CLCPViewTree, CTreeCtrl)
ON_NOTIFY_REFLECT(NM_RCLICK, OnRClick)
ON_COMMAND(ID_LCPGATEWAYCONTEXTMENU_SAVE_SESSION, SaveSession)
ON_COMMAND(ID_LCPGATEWAYCONTEXTMENU_LOAD_SESSION, LoadSession)
ON_COMMAND(ID_LCPGATEWAYCONTEXTMENU_SAVE, SaveDocument)
ON_COMMAND(ID_LCPGATEWAYCONTEXTMENU_LOAD, LoadDocument)
ON_UPDATE_COMMAND_UI(ID_LCPGATEWAYCONTEXTMENU_LOAD, OnUpdateMenuLoadSingle)
ON_UPDATE_COMMAND_UI(ID_LCPGATEWAYCONTEXTMENU_SAVE, OnUpdateMenuLoadSingle)
ON_UPDATE_COMMAND_UI(ID_LCPGATEWAYCONTEXTMENU_LOAD_SESSION, OnUpdateMenuLoadSession)
ON_UPDATE_COMMAND_UI(ID_LCPGATEWAYCONTEXTMENU_SAVE_SESSION, OnUpdateMenuSaveSession)
ON_WM_CONTEXTMENU()
END_MESSAGE_MAP()
afx_msg void CLCPViewTree::OnUpdateMenuLoadSingle(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bShowSingleInstanceMenu);
}
afx_msg void CLCPViewTree::OnUpdateMenuSaveSingle(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bShowSingleInstanceMenu);
}
afx_msg void CLCPViewTree::OnUpdateMenuLoadSession(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bShowSessionMenu);
}
afx_msg void CLCPViewTree::OnUpdateMenuSaveSession(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_bShowSessionMenu);
}
void CLCPViewTree::OnRClick(NMHDR* pNMHDR, LRESULT* pResult)
{
TRACE0("CLCPViewTree::OnRClick()\r\n");
HTREEITEM hItem = GetSelectedItem();
if(!hItem)
{
return;
}
CString text = GetItemText(hItem);
TRACE0(text);
//To get your element:
SelectorReference* ref = (SelectorReference *) (GetItemData(hItem));
if(ref == nullptr)
{
m_bShowSessionMenu = false;
m_bShowSingleInstanceMenu = false;
}
else if(ref->is_program)
{
m_bShowSessionMenu = false;
m_bShowSingleInstanceMenu = true;
// Send WM_CONTEXTMENU to self
CString path = CString(ref->p_pd->ProgramPath) + "sim";
TRACE0("Controller path:\r\n");
TRACE0(path + "\r\n");
SelectedControllerPath = path;
SelectedLCPGatewayView = ref->view;
SendMessage(WM_CONTEXTMENU, (WPARAM) m_hWnd, GetMessagePos());
}
else
{
m_bShowSessionMenu = false;
m_bShowSingleInstanceMenu = false;
}
// Mark message as handled and suppress default handling
*pResult = 1;
}
void CLCPViewTree::OnContextMenu(CWnd* pWnd, CPoint ptMousePos)
{
// if Shift-F10
if (ptMousePos.x == -1 && ptMousePos.y == -1)
ptMousePos = (CPoint) GetMessagePos();
ScreenToClient(&ptMousePos);
CMenu menu;
CMenu* pPopup;
// the font popup is stored in a resource
menu.LoadMenu(IDR_PROGRAM_MENU);
pPopup = menu.GetSubMenu(0);
ClientToScreen(&ptMousePos);
pPopup->TrackPopupMenu( TPM_LEFTALIGN, ptMousePos.x, ptMousePos.y, this );
}
Новый код
С тех пор я добавил переопределения для OnCmdMsg в мой класс, унаследованный от CMDIFrameWndEx (CMainFrame), и в мой класс, унаследованный от CDockablePane (LCPSelector). Намерение состоит в том, чтобы передавать командные сообщения до объекта CTreeCtrl на случай, если они могут быть обработаны.
Я добавил этот код на основе обсуждения маршрутизации команд здесь:
https://docs.microsoft.com/en-us/cpp/mfc/command-routing?view=vs-2019
Тем не менее, получаю те же результаты. Может быть, это неправильное направление, или, может быть, я тоже что-то упускаю.
Для справки: CMainFrame наследуется от CMDIFrameWndEx, а LCPSelector наследуется от CDockablePane.
BOOL CMainFrame::OnCmdMsg(UINT id,int code , void *pExtra,AFX_CMDHANDLERINFO* pHandler)
{
//route cmd first to registered dockable pane
if(m_wndSelector.OnCmdMsg(id,code,pExtra,pHandler))
{
return TRUE;
}
return CMDIFrameWndEx::OnCmdMsg(id,code,pExtra,pHandler);
}
BOOL LCPSelector::OnCmdMsg(UINT id,int code , void *pExtra,AFX_CMDHANDLERINFO* pHandler)
{
//route cmd first to registered dockable pane
if(m_ctrlLCPViewTree.OnCmdMsg(id,code,pExtra,pHandler))
{
return TRUE;
}
return CDockablePane::OnCmdMsg(id,code,pExtra,pHandler);
}