MFC可停靠菜单栏的创建过程

来源:互联网 发布:js sdk fail 编辑:程序博客网 时间:2024/06/02 23:38

VS2010创建的MFC单文档工程中,菜单栏也是可停靠,本文简单分析一下停靠菜单的创建过程。

首先呢,给出一下类图结构

 

CMainFrame有一个 CMFCMenuBar m_wndMenuBar 成员,从名字中可以推断出它代表了菜单。

从继承路径上可以看出,CMFCMenuBar是从CPane继承而来。CPane提供了停靠的功能,所以这个m_wndMenuBar所代表的窗口也具有了停靠功能。

m_wndMenuBar所指代的窗口不是跟主框架窗口不是同一个窗口,它是主框架的子窗口,那么必定是在某个地方它将主框架的菜单栏“搬到”了自己内部,答案得从CMainFrame::OnCreate函数找起:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct){if (CFrameWndEx::OnCreate(lpCreateStruct) == -1)return -1;BOOL bNameValid;// 基于持久值设置视觉管理器和样式OnApplicationLook(theApp.m_nAppLook);if (!m_wndMenuBar.Create(this)){TRACE0("未能创建菜单栏\n");return -1;      // 未能创建}// 此处略去n行return 0;}


OnCreate函数是响应WM_CREATE消息,这个函数的LPCREATESTRUCT类型参数中包含了hMenu成员,它是主框架窗口的菜单。

但是,这个句柄并没有在CMainFrame::OnCreate函数中用过,也就可以推断出菜单的替换不是直接在这个函数中做的,而是在它的某个调用路径中。

最有可能执行替换的地方应该是m_wndMenuBar.Create(this)这一句了,因为这一句才开始创建停靠菜单栏窗口。

BOOL CMFCMenuBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID){m_pParentWnd = pParentWnd;return CMFCToolBar::Create(pParentWnd, dwStyle, nID);}

通过调试可以看到整个CMFCMenuBar::Create函数调用序列,如下所示: 

CMFCMenuBar::Create  CMFCToolBar::Create    CMFCToolBar::CreateEx      CMFCBaseToolBar::Create        CPane::CreateEx          CBasePane::CreateEx            CWnd::CreateEx

需要注意的是,在CMFCMenuBar::Create中赋值了一个成员变量m_pParentWnd,它指向了主框架窗口对象CMainFrame。

CWnd::CreateEx创建窗口的过程中,会发出WM_CREATE消息。消息路由之后,它调用CMFCMenuBar::OnCreate函数:

int CMFCMenuBar::OnCreate(LPCREATESTRUCT lpCreateStruct){if (CMFCToolBar::OnCreate(lpCreateStruct) == -1)return -1;        // 代码有删减...CFrameWndEx* pWndParentFrame = DYNAMIC_DOWNCAST(CFrameWndEx, m_pParentWnd);if (pWndParentFrame != NULL) {pWndParentFrame->m_Impl.SetMenuBar(this);}// 代码有删减...return 0;}

它将m_pParentWnd动态转型为CFrameWndEx,由于之前存的是CMainFrame的指针,CMainFrame是CFrameWndEx的子类,所以这个DYNAMIC_DOWNCAST转型的指针将不为NULL。

接着,对SetMenuBar函数的调用才开始了真正的替换过程,它传进的参数this指向的是CMainFrame中的m_wndMenuBar对象。

void CFrameImpl::SetMenuBar(CMFCMenuBar* pMenuBar){ASSERT_VALID(m_pFrame);ASSERT_VALID(pMenuBar);ENSURE(m_pMenuBar == NULL); // Method should be called once!m_pMenuBar = pMenuBar;m_hDefaultMenu=*m_pFrame->GetMenu();// Support for dynamic menum_pMenuBar->OnDefaultMenuLoaded(m_hDefaultMenu);m_pMenuBar->CreateFromMenu(m_hDefaultMenu, TRUE /* Default menu */);m_pFrame->SetMenu(NULL);m_pMenuBar->SetDefaultMenuResId(m_nIDDefaultResource);}

在这个函数中CreateFromeMenu这一句创建了可停靠的“菜单”。但这个菜单并非真正意义上的菜单,而是一个工具条。每个“菜单项”是一个工具栏按钮,按下这个按钮,会弹出一个子菜单,看起来很像原来的菜单。

SetMenu(NULL)这一句清除了框架窗口原有的菜单。

至此,整个菜单的替换过程就结束了。



0 0
原创粉丝点击