这个问题很好解决,首先你要理解MFC的命令传送机制是通过框架窗口的OnCmdMsg函数实现的,它将诸如菜单、工具栏等命令按以下顺序进行传送:
活动视图、文档、文档模板、框架窗口、应用程序对象,如果其中一级具有该命令的处理,则传送在此终止。
因为你的程序有两个视图,所以非活动视图将接收不到传送的命令,所以要自定义命令传送过程:
覆盖框架窗口的OnCmdMsg函数,先调用缺省的OnCmdMsg函数(即上面的传送过程),如果命令没有被处理(缺省的OnCmdMsg返回FALSE),则获取当前所有视图指针,并调用每个视图的OnCmdMsg函数,将命令消息传送给每个视图就可以了。
BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
if ( CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo) )
return TRUE;
CWandererDoc* pDoc = ( CWandererDoc* ) GetActiveDocument ();
if ( pDoc != NULL )
POSITION pos = pDoc->GetFirstViewPosition ();
while ( pos != NULL )
{
CView* pNextView = pDoc->GetNextView ( pos );
if ( pNextView != GetActiveView() )
{
if ( pNextView->OnCmdMsg ( nID, nCode, pExtra, pHandlerInfo ) )
return TRUE;
}
}
return FALSE;
}
回楼上:
1、我的方法中,不是把所有的命令都映射在frame class中,而是和平常一样,哪个视图需要就在哪个视图中映射。
2、MFC中如果有两个以上的地方处理同一个命令ID,则在命令传送过程中处于前面的对象才能处理到,比如,在框架窗口和视图类中都处理了同一个ID,则只有视图类能处理到,所以要将各命令ID分工,在合适的地方进行处理。这样做要比把所有ID放在同一个地方处理更加科学、清晰,试想一下:如果把与视图操作有关的ID处理和与文档操作有关的ID处理都放在框架窗口中,数量一多,就搞不清哪是哪了。
3、在覆盖的OnCmdMsg函数中,首先调用的是基类的版本,已经包含了标准的命令传送过程,即已经优先考虑活动视图了,然后再考虑其它非活动视图。
while ( pos != NULL )
{
CView* pNextView = pDoc->GetNextView ( pos );
其执行顺序是永远一样的,无论第二个view是否active,只要第一个view有message handler,第二个view 就执行不到了。没有冲突document 的message 是没有必要放在frame class 下面,但是两个以上的view同时处理一个message的时候这方法就有点前灵活了。
while ( pos != NULL )
{
CView* pNextView = pDoc->GetNextView ( pos );
其执行顺序是永远一样的,无论第二个view是否active,只要第一个view有message handler,第二个view 就执行不到了。没有冲突document 的message 是没有必要放在frame class 下面,但是两个以上的view同时处理一个message的时候这方法就有点前灵活了。
举个例子:
假如ID_AAA的handler定义在CView1中而不在CView2中, 如果不覆盖OnCmdMsg,这时程序运行后,如CView1是活动的,则ID_AAA对应的菜单项可用;如CView2是活动的,则ID_AAA对应的菜单项就灰化了。
我上面的代码所做的工作是:先在标准的传送过程中查找,如果找不到handler(活动视图也不处理),则在其它非活动视图中查找handler。这样的目的是,当CView2处于活动状态时,也可以确保CView1中的handler被找到。
命令传送过程中是有优先权的,如果优先级高的对象具有该命令ID的handler,则位于传送顺序后面的对象就处理不到了,MFC的特点就是这样,不要把同一个ID映射到多个地方去处理。当然也可以把所有ID都放在框架窗口中处理,那样就不需要覆盖OnCmdMsg了,但想想看,哪种方法更科学、合理。
下面引用Jeff Prosise在<MFC Windows程序设计>(第2版)中有关命令传送的叙述:
“文档/视图体系结构中最引人注目的特性是应用程序几乎可以在任何地方处理命令消息......命令传送使得您可以将命令处理程序放在最合适的地方,避免把它们都堆在框架窗口中.......”
看看MFC向导生成的应用程序,File菜单中的New、Open和Exit命令由CWinApp处理,因为它们是与应用程序相关的;而File菜单中的Save、Save As命令由CDocument来处理,因为它们是与文档相关的。
总之,把命令ID放在合适的地方处理,这种方法才更有效,如果象楼上讲的两个视图都要响应同一个ID时,当然要把ID的handler放在框架窗口中、由框架窗口来操作两个视图了。