CWnd的派生类-4、 对话框的命令路由
如果给对话框附加了一个菜单资源,进入ClassWizard向导,就能够发现,不仅该对话框能够映射该菜单命令,而且主框架窗口、视图、文档、应用类也可以。如果一个对话框(无论弹出和重叠,不分模态与非模态)的父窗口是主框架或视图,该对话框的菜单命令就可以在多个对象中处理,它的处理路由是:对话框→视图→文档对象→主框架→应用类。一旦命令消息被处理,将不再继续传递。由此可知,对话框的命令处理路由与主框架的命令处理路由非常接近,只是增加了一个最高的优先处理对象:对话框本身。不难推测,在对话框的命令消息处理函数CDialog::OnCmdMsg()中,首先搜寻对象本身的消息映射,如果没有找到相应的处理函数,则将控制权交给主框架窗口(如果是它的父窗口)。下面就一起来阅读这段代码,证实这一推断。
BOOL CDialog::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{ //首先搜寻该对象的消息映射
if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
//该对象没有处理这个命令消息
/*如果该命令ID在[0x8000,0xF000)范围内,则向下传递,否则终止处理。
命令范围[8000 ,FFFF]:全局命令,包括菜单、加速键、系统命令等
命令范围[F000 , FFFF]:Windows系统命令(不被传递)*/
if ((nCode != CN_COMMAND && nCode != CN_UPDATE_COMMAND_UI) ||
!IS_COMMAND_ID(nID) || nID >= 0xf000)
{
return FALSE; // not routed any further
}
//如果是全局的菜单、加速键等命令,则将处理权转交给父窗口
CWnd* pOwner = GetParent();
if (pOwner != NULL)
{
ASSERT(pOwner != this);
//由父窗口负责命令路由
if (pOwner->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
}
/*如果父窗口也没有处理该命令,则将处理权交给应用类。在父窗口不是主框架窗口时,以下代码是有意义的*/
CWinThread* pThread = AfxGetThread();
if (pThread != NULL)
{ if (pThread->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
}
return FALSE;
}
从以上代码可知,只有菜单、加速键等全局命令,才可能被对话框传送给父窗口。至于我们最关心的对话框按钮命令,则不被传递。如果父窗口不是主框架,应用类得到最后的处理机会;如果父窗口是主框架,最后关于应用类(UI线程)的代码是多余的。那么如果在对话框创建时,指定了视图作为父窗口会怎样呢?读者应该注意到,视图是主框架的子窗口,而对话框是弹出或重叠窗口,所以对话框不能被视图控制,而最终由主框架接管。