在MFC开发的程序中,菜单、工具条按钮等都会产生WM_COMMAND消息。而在MFC的Document/View框架中,有很多类可以响应WM_COMMAND消息,分别是框架类:CFrameWnd、CMDIChildWnd、CMDIFrameWnd;应用程序类CWinApp;文档类CDocument;以及视图类CView。
当应用程序主菜单发送了一个WM_COMMAND消息时,WM_COMMAND消息将会按一定顺序被交这些类的实例,并调用第一个发现的响应函数。以多文档视图框架应用程序为例,我们可以分析MFC中这些类的源代码,并一步一步找出WM_COMMAND消息的响应顺序。
因为框架窗口是菜单的父窗口,所以消息首先发到CMDIFrameWnd类的实例(也就是主框架窗口,通常为CMainFrame)。CMDIFrameWnd类的OnCmdMsg函数如下:
BOOL CMDIFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
...{
CMDIChildWnd* pActiveChild = MDIGetActive();
// pump through active child FIRST
if (pActiveChild != NULL)
...{
CPushRoutingFrame push(this);
if (pActiveChild->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
}
// then pump through normal frame
return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
主框架窗口CMDIFrameWnd先查找当前活动的子框架窗口CMDIChildWnd,让其优先访问,然后调用基本框架窗口CFrameWnd的响应函数。所以响应优先级:
子框架窗口CMDIChildWnd > 主框架窗口CMDIFrameWnd
CMDIChildWnd窗口没有重载OnCmdMsg函数,所以使用基本框架窗口CFrameWnd的OnCmdMsg函数:
BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
...{
CPushRoutingFrame push(this);
// pump through current view FIRST
CView* pView = GetActiveView();
if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// then pump through frame
if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// last but not least, pump through app
CWinApp* pApp = AfxGetApp();
if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
return FALSE;
}
基本框架窗口先让当前活动视图CView响应函数,然后再查找自己的消息响应表(调用CWnd::OnCmdMsg),最后让应用程序处理该消息。所以得出顺序:
存在打开的视图时,视图CView > 子框架CFrame > 应用程序CWinApp >主框架窗口CMDIFrameWnd
没有打开视图时,主框架窗口CMDIFrameWnd > 应用程序CWinApp
再看视图CView类对OnCmdMsg的处理:
BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
...{
// first pump through pane
if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// then pump through document
if (m_pDocument != NULL)
...{
// special state for saving view before routing to document
CPushRoutingView push(this);
return m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
return FALSE;
}
视图类首先查找自己的消息响应表(调用CWnd::OnCmdMsg),然后让视图对应的CDocument类处理消息。所以可以得出如下顺:
视图CView > 文档CDocument > 子框架CFrame > 应用程序CWinApp >主框架窗口CMDIFrameWnd
再查看CDocument的OnCmdMsg的实现代码:
BOOL CDocument::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
...{
if (CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// otherwise check template
if (m_pDocTemplate != NULL &&
m_pDocTemplate->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
return FALSE;
}
文档类CDocument首先查找自己的响应函数(调用CCmdTarget::OnCmdMsg),然后让其成员m_pDocTemplate来处理。m_pDocTemplate是文档模板类CDocTemplate的指针,MFC框架使用文档模板来实现自动化的文档管理。在应用程序开发过程中,没有针对文档模板类的编程。查看CDocTemplate类中OnCmdMsg的实现:
BOOL CDocTemplate::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
...{
BOOL bReturn;
CCmdTarget* pFactory = DYNAMIC_DOWNCAST(CCmdTarget, m_pAttachedFactory);
if (nCode == CN_OLE_UNREGISTER && pFactory != NULL)
bReturn = pFactory->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
else
bReturn = CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
return bReturn;
}
因为应用程序中并没有开发直接从CDocTemplate继承的类,所以应用程序无法使用CDocTemplate的子类定义WM_COMMAND消息的响应函数。CDocTemplate类在OnCmdMsg函数实现里调用的CCmdTarget::OnCmdMsg实现不了任何消息的传递。
事实上在多文档框架中,系统自动生成的代码使用的是CDocTemplate的子类CMultiDocTemplate。在应用程序类(多文档应用框架)的InitInstance()里可以找到类似下面的代码:
// 注册应用程序的文档模板。文档模板
// 将用作文档、框架窗口和视图之间的连接
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_test3TYPE,
RUNTIME_CLASS(Ctest3Doc),
RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
RUNTIME_CLASS(Ctest3View));
if (!pDocTemplate)
return FALSE;
而CMultiDocTemplate类并没有重写OnCmdMsg函数,所以依然不影响WM_COMMAND消息的响应顺序。
通过分析MFC的源代码,我们可以得到WM_COMMAND的消息响应顺序如下:
多文档框架中,有打开的文档时:视图 > 文档 > 子框架窗口 > 应用程序 >主框架窗口
多文档框架在没有打开文档时,应用程序和主框架窗口的顺序相反:主框架窗口 > 应用程序
在单文档框架应用程序中,因为没有子框架窗口,所以顺序应该是:视图 > 文档 >主框架窗口> 应用程序。无论有没有打开文档,主框架窗口都比应用程序类更优先。
作者:苏林
分享到:
相关推荐
MFC对话框程序中添加工具栏及工具栏上的Button响应UPDATE_COMMAND_UI消息
详细讲解了在vc++/mfc中如何找到WM_SYSCOMMAND消息
MFC 全面解读WM_NOTIFY
包括了MFC应用程序中处理消息的顺序,MFC应用程序创建窗口的过程,MFC应用程序关闭窗口的顺序(非模态窗口) ,MFC应用程序中打开模式对话框的函数调用顺序 ,MFC应用程序中关闭模式对话框的顺序 ,打开无模式对话框的...
两个MFC应用程序使用WM_COPYDATA消息进行进程间的通讯的示例代码,分别叫客户端与服务器端。
1、使用WM_DRAWITEM和WM_MEASUREITEM消息在父窗口实现按钮自绘; 2、使用WM_DRAWITEM和WM_MEASUREITEM消息在父窗口实现列表控件自绘; 3、使用WM_DRAWITEM和WM_MEASUREITEM消息在父窗口实现下拉控件自绘; 4、通过...
MFC消息响应顺序,从本质讲解消息的发收,响应
MFC消息映射机制的剖析,讲述如何运用ClassWizard,,理解发送给窗口的消息是如何被MFC框架通过窗口句柄映射表和消息映射表来用窗口类的函数进行响应的
MFC动态画直线(响应WM_MOUSEMOVE)
MFC框架文档视图_源码,rar格式,不错的资料,需要的下载
MFC_OPENJL框架 , 标准框架 MFC_OPENJL框架 , 标准框架
本代码实现了WM_COPYDATA数据的传递及参数 (WPARAM) 与 (LPARAM)的接收,尤其是如何接收WPARAM消息,本示例给出了接收代码。源码分为消息发送端及接收端。
4.OnNcCreate() 该函数也是一个消息响应函数,响应WM_NCCREATE消息,发送消息以告诉窗口的客户区即将被创建 5.OnNcCalcSize() 该函数也是消息响应函数,响应WM_NCCALCSIZE消息,作用是允许改变窗口客户区大小 6....
该MFC参考含盖了Microsoft基本类库中的类、全局函数、全局变量和宏的内容。参考中“类层次结构图”是为了方便查找某个类... · 结构、风格、回调函数和消息映射 详细解说MFC库中的各个结构、风格、回调函数和消息映射。
这是个MFC程序,检测Windows窗口程序发送消息WM_PAINT的时机,根据检测到WM_PAINT的次数绘制椭圆,从而确定发送消息WM_PAINT的时机。 经过实验,发送WM_PAINT的时机是窗口创建时、改变窗口大小时。 窗口的坐标系为...
源对话框发送WM_COPYDATA消息到目标对话框,目标对话框接受WM_COPYDATA消息,实现进程间消息的传递。该结构也可以传递一个结构体,但一定是“值类”,里面不能含有指针。
一步一步向您展现,mfc框架的实现过程,达内老师经典之作!
AppWizard是一个源代码生成工具,是计算机辅助程序设计工具,WinMain在MFC程序中是如何从源程序中被隐藏的,theApp全局变量是如何被分配的,MFC框架中的几个类的作用与相互关系,MFC框架窗口是如何产生和销毁的,对...
关于MFC windows 应用程序设计中键盘响应