跨拆分框架的命令路由





5.00/5 (9投票s)
分割框架中非活动视图的命令路由和 UI 更新。
摘要
本文介绍了三种简单的 WM_COMMAND
消息路由方法,这些方法通过分割框架窗口中的多个视图进行路由。这简化了非活动视图的命令路由和 UI 更新的处理。
问题介绍
标准框架路由不包括非活动视图,这会导致工具栏按钮和菜单在其母视图停用时变灰。 用户感到困惑。 我提出了三种简单的方法来让他们重新快乐。 :) 所有解决方案都基于覆盖框架类中的 CCmdTarget::OnCmdMsg
函数。 我假设这个类直接派生自 CFrameWnd
(SDI 情况),但这些方法也可以与 MDI 子窗口一起使用。
在每种情况下,被覆盖的函数都会浏览视图列表,并为每个视图调用 CCmdTarget::OnCmdMsg
,传递接收到的参数。 如果返回 TRUE,我们可以返回 - 该消息无疑已被该视图处理,无需进一步处理。 当然,活动视图不包括在此调用中,因为它将由基本处理程序处理 - 这是默认情况。 如果您希望消息主要由活动视图、框架本身或 CWinApp
派生对象成功处理,您可以将被覆盖的函数体开头的基本函数调用放置,因为这三个调用是在基本 CFrameWnd::OnCmdMsg
实现中进行的。 只有当基本实现返回 FALSE 时,才应执行我们的自定义例程。
经典的文档/视图案例
第一个非常明显的方法是使用 CDocument
类中可用的视图列表,并通过 GetFirstViewPosition
/ GetNextView
辅助函数对访问。 除了活动视图之外,此方法还浏览所有非活动视图,但您可以根据应用程序的需要过滤该列表。
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);
}
分割窗口案例
如果我们根本不喜欢使用任何 CDocument
派生类对象怎么办? 这可能是这种情况,那么第一种方法将毫无用处。 然而,为了实现路由目标,我们不需要文档,因为我们只需要访问处理消息的窗口,即分割窗格。 如果您有一个显式的分割器对象(无论是作为指针还是作为框架类中的成员,我认为这很常见),那就没有麻烦了。 只需使用 CSplitterWnd::GetPane
并完成它! Samuel Chow 之前也暗示过这种情况。
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); }
一种可能通用的案例
一个年轻而热切的头脑然后想要一个通用的处理程序,独立于成员分割器的存在,甚至是一个文档。 在 MFC 源代码(即 CView
和 CSplitterWnd
类)中寻找灵感时,我注意到他们使用标准窗格 ID(参见 afxres.h
)来访问框架中的非活动视图。 并且他们肯定既不使用文档指针,也不直接使用分割器对象! 现在是圣杯的时刻了
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);
}
与第一种方法一样,不必为所有视图调用处理程序,因为可以自由过滤调用。 甚至可以动态地完成,例如,取决于 nID
或 nCode
值,但我担心这会使路由过于复杂。 应该仔细考虑这种迂回的解决方案 - 有许多直接的方法可以在现有命令目标之间分配消息处理,但情况并非如此。
这些代码片段不可能解决您在 MFC 命令消息路由方面的所有麻烦,但它可能会让您更接近或只是给您一点线索。 欢迎任何评论和建议。