MFC窗口创建以及工具条的浮动

在上一篇博客的分析当中,主窗口的创建是在LoadFrame当中完成的。LoadFrame函数主要调用两个函数完成这个工作,第一步向系统注册相应的窗口信息,第二步创建相应的窗口。这正好符合WIN32的窗口创建过程。不过在调用者两个函数之前,LoadFrame会利用相应的资源ID得到窗口的标题,这个资源师string table当中的IDR_MAINFRAME标志的字符串,而主窗口的标题是这个字符串当中的第一个。

窗口的注册是在AfxDeferRegisterClass函数当中完成的,这个函数分成两个部分,第一个部分主要调用AfxRegisterClass,第二部分是通过_AfxInitCommonControls调用InitCommonControls函数完成。前者创建的注册的是窗口在系统当中是在user32当中实现,而后者主要是在COMCTL32实现的控件。由MFC定义的一些窗口类都有相应的窗口名称和属性,所以可以通过调用AfxDeferRegisterClass来完成,如果用户需要注册自定义的类,可以通过AfxRegisterWndClass函数来进行,最主要的是这个函数可以让用户自定义属性。

在注册完成之后,下一步就是创建了。然而创建相对于win32窗口的创建稍微复杂一些。由于在创建的第一步都会调用父类的Create或者CreateEx函数,所以第一个被调用的函数实际上是CWnd类的Create函数或者CreateEx函数,在CWnd::Create函数或者CWndCreateEx函数当中,首先会PreCreateWindow函数,由于这个函数是一个虚函数,实际上这是MFC给程序员一个自己定义窗口属性的机会。然后再创建过程会调用AfxHookWindowCreate函数挂载一个钩子函数_AfxCbtFilterHook。这个函数主要是切换掉系统默认的消息处理循环,那么为什么需要这个钩子函数呢?因为整个窗口的创建过程当中会发一些消息,而此时后续的消息循环并没有构建起来,可以考虑一下,到目前为止view视图还没有创建呢。然而,有一些消息需要得到处理,所以这里需要系统默认的消息处理函数DefWndProc对消息进行处理。但是当钩子函数被调用的时候,消息循环需要被切换到MFC构建的消息循环当中,不然的话我们的消息拦截函数怎么被调用呢?所以在_AfxCbtFilterHook函数当中,仅仅拦截WH_CHTCREATEWND钩子消息,切换DefWndProc消息处理函数,当然DefWndProc函数地址会被保存以便于调用系统默认的消息处理函数。当然,由于这个缘故,所有的通过CWnd::Create或者CWnd::CreateEx创建的窗口的消息处理循环都是AfxWndProc,然后通过这个函数将所有的消息都分发到MFC相应类的消息处理函数当中去。这个消息处理循环的切换有一个专用的学名——子类化。

			pWndInit->Attach(hWnd);
			pWndInit->PreSubclassWindow();

			WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr();
			{
				WNDPROC afxWndProc = AfxGetAfxWndProc();
				oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC,
					(DWORD)afxWndProc);
				ASSERT(oldWndProc != NULL);
				if (oldWndProc != afxWndProc)
					*pOldWndProc = oldWndProc;
			}
_AfxCbtFilterHook函数的主要处理步骤有上面三个,第一步,将句柄和相应的类关联起来,这一步通过Attach完成,实际上窗口类的指针和句柄对是保存在一个全局的MAP当中,而PreSubclassWindow函数是一个虚拟函数,这也是为了给用户一个处理的接口,默认的处理是空。最后是通过SetWindowLong函数设置新的窗口消息处理循环,同时将原来的消息处理函数保存起来。当然,还有一些窗口类别不需要这么高级的处理,尤其是在OnCreate消息处理当中创建的窗口。为了迎合这一部分需求,MFC特意将子类化给整合到一个函数当中,这个函数是在CWnd当中实现,所有的窗口类都可以调用这个函数。

BOOL CWnd::SubclassWindow(HWND hWnd)
{
	if (!Attach(hWnd))
		return FALSE;

	PreSubclassWindow();

	WNDPROC* lplpfn = GetSuperWndProcAddr();
	WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC,
		(DWORD)AfxGetAfxWndProc());

	if (*lplpfn == NULL)
		*lplpfn = oldWndProc;   // the first control of that type created

	return TRUE;
}

工具条的浮动是由CControlBar这个类支持的,也就是说所有继承自CControlBar的类都具备浮动功能。在创建完相应的类之后,首先调用CControl类的EnableDocking函数为窗口浮动创建一个CDockContext类用于保存相应的浮动窗口信息,然后通过调用主窗口的EnableDocking函数打开主窗口的窗口浮动功能,这个函数创建四个CDockBar分别对应窗口上下左右四个方位的浮动。最后的窗口浮动是在DockControlBar当中完成。由于这个函数的搜索过程是从0到3,而第0个CDockBar刚好对应的是顶端的CDockBar,所以在系统默认的显示是在顶端。而在DockControlBar函数当中仅仅只是找到CDockBar然后调用CDockBar的DockControlBar函数,在CDockBar的DockControlBar函数里面完成相应的功能。

CDockBar类的DockControlBar函数可以分为三个步骤进行,第一个步骤设置相应的属性,包括CToolBar和CDockBar的属性;第二,调用CToolBar的SetWindowPos函数设置CToolBar窗口类所在的位置。然后将CToolBar存放到CDockBar的一个队列当中。最后调用主窗口的DelayRecalcLayout函数延迟窗口的布局计算,直到需要的时候才会将窗口进行显示出来。不过窗口布局的计算可以通过ShowControlBar函数窥见。ShowControlBar函数可以实现浮动窗口的显示和隐藏。

你可能感兴趣的:(Win32,mfc)