MFC框架中命令消息路由的优化方法

MFC(Microsoft Foundation Classes)框架中,命令消息路由是一个关键的机制,它负责将用户界面操作(如点击按钮或选择菜单项)映射到相应的处理函数。然而,标准的框架路由并不包括非活动视图,这会导致当视图被停用时,工具栏按钮和菜单项变灰,从而引起用户的困惑。本文将介绍三种简单的方法来解决这个问题,使用户的体验更加流畅。所有这些解决方案都基于重写框架类中的CCmdTarget::OnCmdMsg函数。

问题介绍

在标准框架中,当视图被停用时,相关的命令消息无法正确路由,导致用户界面响应性下降。为了解决这个问题,提出了三种方法,这些方法都基于重写CCmdTarget::OnCmdMsg函数。假设这个类是从CFrameWnd派生的(单文档界面,SDI),但这些方法同样适用于多文档界面(MDI)子窗口。

经典文档/视图案例

第一种方法是利用CDocument类中可用的视图列表,通过GetFirstViewPositionGetNextView辅助函数对视图进行遍历。除了活动视图之外,这个方法还会遍历所有非活动视图,但可以根据应用程序需求对列表进行过滤。

以下是C++代码示例:

BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { CDocument *pDoc = GetActiveDocument(); if (pDoc) { POSITION pos = pDoc->GetFirstViewPosition(); CView *pView = NULL; while (pView = pDoc->GetNextView(pos)) { if (pView != GetActiveView() && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; } } return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); }

在这个例子中,首先获取当前活动的文档,然后遍历所有视图,并对每个视图调用OnCmdMsg函数。如果返回值为TRUE,表示消息已被视图处理,无需进一步处理。

分割窗口案例

如果不想使用任何从CDocument派生的类对象,那么第一种方法就无法使用。但是,为了实现消息路由的目标,不需要文档,只需要访问处理消息的窗口,即分割窗口。如果有一个明确的分割对象,无论是作为指针还是作为框架类中的成员,这都是一个常见的情况。简单地使用CSplitterWnd::GetPane就可以实现。

以下是C++代码示例:

BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { if (m_wndSplitter.GetSafeHwnd()) { int rc = m_wndSplitter.GetRowCount(), cc = m_wndSplitter.GetColumnCount(); for (int r = 0; r < rc; r++) { for (int c = 0; c < cc; c++) { CWnd *pWnd = m_wndSplitter.GetPane(r, c); if (pWnd != m_wndSplitter.GetActivePane() && pWnd->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; } } } return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); }

在这个例子中,遍历分割窗口的所有窗格,并对每个窗格调用OnCmdMsg函数。如果返回值为TRUE,表示消息已被窗格处理。

通用案例

一个有抱负的年轻开发者可能会想要一个通用的处理程序,它不依赖于成员分割器或文档的存在。在MFC源代码中,特别是在CViewCSplitterWnd类中,注意到它们使用标准的窗格ID(见afxres.h)来访问框架中的非活动视图。它们既不使用文档指针,也不直接使用分割对象!现在找到了圣杯:

以下是C++代码示例:

BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { for (UINT id = AFX_IDW_PANE_FIRST; id <= AFX_IDW_PANE_LAST; id++) { CWnd *pWnd = GetDescendantWindow(id, TRUE); if (pWnd && pWnd != GetActiveView() && pWnd->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; } return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485