65.9K
CodeProject 正在变化。 阅读更多。
Home

跨拆分框架的命令路由

starIconstarIconstarIconstarIconstarIcon

5.00/5 (9投票s)

2002年7月17日

CPOL

3分钟阅读

viewsIcon

99166

分割框架中非活动视图的命令路由和 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 源代码(即 CViewCSplitterWnd 类)中寻找灵感时,我注意到他们使用标准窗格 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);
}

与第一种方法一样,不必为所有视图调用处理程序,因为可以自由过滤调用。 甚至可以动态地完成,例如,取决于 nIDnCode 值,但我担心这会使路由过于复杂。 应该仔细考虑这种迂回的解决方案 - 有许多直接的方法可以在现有命令目标之间分配消息处理,但情况并非如此。

这些代码片段不可能解决您在 MFC 命令消息路由方面的所有麻烦,但它可能会让您更接近或只是给您一点线索。 欢迎任何评论和建议。

© . All rights reserved.