一、框架窗口
框架窗口在其大小被改变的时候会收到WM_SIZE消息,这个消息的处理函数是CFrameWnd::OnSize,此函数接着调用RecalcLayout来重新安置各子窗口,它的主体代码如下:
if(GetStyle() & FWS_SNAPTOBARS)
{
CRect rect(0, 0, 32767, 32767);
RepositionBars(0, 0xffff, AFX_IDW_PANE_FIRST, reposQuery, &rect, &rect, FALSE);
RepositionBars(0, 0xffff, AFX_IDW_PANE_FIRST, reposExtra, &m_rectBorder, &rect, TRUE);
CalcWindowRect(&rect);
SetWindowPos(NULL,0,0,rect.Width(),rect.Height(),SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
}
else
{
RepositionBars(0, 0xffff, AFX_IDW_PANE_FIRST, reposExtra, &m_rectBorder);
}
这里有两个小的地方要注意,第一是FWS_SNAPTOBARS风格。一般来说,都是框架窗口主动改变大小,子窗口随之要修改自己来适应框架窗口的改变,但是这个FWS_SNAPTOBARS风格却相反,是让框架窗口改变大小来适应它的子窗口,但我一路跟踪下来没有发现有哪个框架窗口有这个风格,都是走else分支的(事实上这个风格是为CMiniDockFrameWnd准备的,这个框架窗口的大小是根据它内部的控制条来定的);第二是要注意RecalcLayout是不可重入的,MFC防止重入的方法虽然非常的简单有效,但是它的方法要不能防止多线程的重入——话说回来MFC本身就不是一个线程安全的库J
好了现在我们进入了整个重布局动作的主体函数RepositionBars,让我们仔细分析一下它都干了些啥见不得人的“勾当”(这个函数在MSDN里有文档记载,关于它的几个参数的含义就不在这里赘述了):
首先,它创建一个AFX_SIZEPARENTPARAMS结构,并填写好它的成员变量,最主要的就是两个:bStretch和rect,前一个是BOOL型变量,表明子窗口是否需要拉伸(拉伸到和客户区同宽或同高),后一个是当前客户区大小。
接着,框架窗口按照Z-order,从最上面的子窗口开始,依次向它的所有子窗口发送WM_SIZEPARENT消息(ID为nIDLeftOver的子窗口除外),以通知它们,按照新的客户区,重新计算自己的大小和位置,并从AFX_SIZEPARENTPARAMS::rect中将自己所占的那一块rectangle扣除,这样所有的子窗口计算完毕后框架窗口就可以知道剩余客户区的大小(PS:到底都发送给了谁?又是按照什么次序?以MDIDemo为例,该例子创建了一个CToolBar、一个CThumbnailListCtrlBar,在都是Dock状态下,跟踪记录下的所发送的窗口的ID,依次是0xE801à0xE81Bà0xE81Eà0xE81Cà0xE81D,察看各子窗口ID的定义得知次序是状态栏à上方的Dock Barà下方的Dock Barà左边的Dock Barà右边的Dock Bar,这里要注意的是,所找到的第一个子窗口的ID是0xE900(0xE900被定义成AFX_IDW_PANE_FIRST,在这个例子里它是View的窗口ID,与这个ID对应的还有另一个ID叫AFX_IDW_PANE_LAST,SDI的View窗口,MDI的MDIClient窗口,分隔条窗口等的ID都要介于上面两个ID值之间),等于nIDLeftOver,所以WM_SIZEPARENT消息是不发给它的。那么为什么Tool bar和Thumbnail bar也收不到消息呢?因为Dock Bar是它的父窗口,消息发给Dock Bar了,Dock bar会计算它内部停靠的全部子窗口的大小,就不需要框架窗口操心了,除此之外,浮动的控制条也是不接受WM_SIZEPARENT消息的,原因很简单,浮动的控制条不会跟随主框架窗口的大小改变而改变)。
发送完毕之后(这里有个细节,框架窗口发送WM_SIZEPARENT消息用的是SendMessage,这意味着只有子窗口处理完该消息后,SendMessage才返回,框架窗口才会接着发送消息给下一个子窗口,全部发送完毕也就意味了所有的子窗口都已经从AFX_SIZEPARENTPARAMS::rect中把自己要的那一块rectangle给拿掉了),剩下的就是最后可用的客户区了,根据nFlags的值,执行不同的返回动作。其中reposQuery表示只查询,不做实际的重布局动作,把最后剩下的客户区,拷贝到lpRectParam就返回了,如果不是reposQuery那就要做重布局了,对reposDefault,我们要把ID为nIDLeftOver的子窗口的大小和位置调整到被其他子窗口切剩后剩下的可用客户区内,使这个子窗口正好完全覆盖最后的可用区域(也就是说所有的子窗口把客户区全部挤满了)。而当nFlag等于reposExtra时,在调整 nIDLeftOver子窗口的大小和位置前,用 lpRectParam来对最后剩下的可用区域做修正,具体来说就是把AFX_SIZEPARENTPARAMS::rect向里缩,缩的距离由lpRectParam指定,这样就使最后剩下的客户区不被nIDLeftOver子窗口占满,而是空出一些地方。修正完毕后最后一次性重布局所有的子窗口。
至此,框架窗口所做的动作全部完成。
二、控制条子窗口
分析完了框架窗口,接着分析控制条这边所要做的响应动作。根据前面的跟踪我们知道除了CStatusBar和CDockBar,从CControlBar继承下来的控制条诸如CToolBar、CDialogBar等,是收不到WM_SIZEPARENT消息的,它们的父窗口CDockBar代替它们接收这个消息。因此整个重布局过程的起点是CDockBar对WM_SIZEPARENT消息的处理函数CDockBar::OnSizeParent(对CStatusBar而言,其起点是CControlBar::OnSizeParent,这里不打算对它作进一步分析,有兴趣可以自己完成)。第一步让我们来分析这个函数所做的动作,这个函数不长,把完整代码列出:
LRESULT CDockBar::OnSizeParent(WPARAM wParam, LPARAM lParam)
{
AFX_SIZEPARENTPARAMS* lpLayout = (AFX_SIZEPARENTPARAMS*)lParam;
// set m_bLayoutQuery to TRUE if lpLayout->hDWP == NULL
BOOL bLayoutQuery = m_bLayoutQuery;
CRect rectLayout = m_rectLayout;
m_bLayoutQuery = (lpLayout->hDWP == NULL);
m_rectLayout = lpLayout->rect;
LRESULT lResult = CControlBar::OnSizeParent(wParam, lParam);
// restore m_bLayoutQuery
m_bLayoutQuery = bLayoutQuery;
m_rectLayout = rectLayout;
return lResult;
}
如前所述,WM_SIZEPARENT消息传递一个AFX_SIZEPARENTPARAMS结构体的指针作为参数,在这里我们先取出这个结构体,然后判断AFX_SIZEPARENTPARAMS::hDWP是否为空,是的话说明父窗口仅仅是想查询,并不要真的进行重布局动作(回到RepositionBars,当nFlags为reposQuery时,并不调用BeginDeferWindowPos,故而AFX_SIZEPARENTPARAMS::hDWP就一定是NULL),完成必要的变量保护后,进入父类CControlBar的OnSizeParent,在此,根据控制条窗口的风格,决定如何计算控制条的尺寸,具体是这样的;
DWORD dwMode = lpLayout->bStretch ? LM_STRETCH : 0; //拉伸否?
if((m_dwStyle & CBRS_SIZE_DYNAMIC) && m_dwStyle & CBRS_FLOATING) //浮动,形状可变
{
dwMode |= LM_HORZ | LM_MRUWIDTH;//计算水平状态常用尺寸
}
else if(dwStyle & CBRS_ORIENT_HORZ) //水平停靠
{
dwMode |= LM_HORZ | LM_HORZDOCK;//计算水平停靠状态尺寸
}
else
{
dwMode |= LM_VERTDOCK; //计算垂直停靠状态尺寸
}
CSize size = CalcDynamicLayout(-1, dwMode);
要注意的是最后一行调用,CalcDynamicLayout,这个函数是一个虚函数,先被调用的是CControlBar:: CalcDynamicLayout,这个函数调用了CalcFixedLayout(也是一个虚函数),注意到CDockBar对此函数进行了重载,所以转了一圈我们又回到了CDockBar中。
分享到:
相关推荐
这通常通过在资源编辑器中设计窗口布局,然后在代码中实例化`CSplitterWnd`来实现。在创建`CSplitterWnd`对象时,需要指定窗口的行数和列数,以及每个子窗口的初始视图。 至于"通道菜单按钮",这是指在应用程序的...
在Microsoft Foundation Classes (MFC)库中,动态分割窗口(Dynamic Splitter Window)是一种功能强大的技术,它允许用户在运行时动态地调整窗口布局。这种技术对于开发具有多个视图或者面板的应用程序非常有用,...
理解CWnd类及其派生类的工作原理对于构建MFC窗口程序至关重要。 接着,我们讨论“第04章 视图窗口”。在MFC中,视图(View)是用户界面的主要部分,它通常显示数据并接收用户的输入。视图类通常是CView的派生类,它...
首先,理解MFC调用QT页面的核心原理:通过创建一个动态链接库(DLL)来实现MFC与QT的交互。QT工程被封装到DLL中,然后在MFC应用中通过接口调用这些DLL中的函数,显示和控制QT界面。 1. **创建QT工程** - 首先,你...
通过调用DockControlBar()或DockControl()函数,你可以控制窗口的浮动和停靠状态。 5. **处理消息**:重写On_WM_NCHITTEST()消息处理函数,以响应用户的鼠标操作,实现窗口的拖放功能。同时,可能还需要处理其他...
这个实例详细演示了如何通过MFC手动实现窗口分割,并通过视图类间的交互来传递数据和控制信息,为用户提供了自定义窗口布局的灵活性和可扩展性。 总的来说,MFC提供的窗口分割功能使得开发者能够轻松构建复杂的应用...
#### MFC控制绘图任务 MFC通过设备上下文(Device Context,DC)来控制绘图任务。DC是MFC用来在屏幕上绘制图形的对象,MFC提供了各种绘图函数,如线条、文本、填充形状等,通过这些函数,开发者可以实现复杂的绘图...
**Visual C++ MFC**是微软提供的一种强大的应用程序开发环境,...本教程的目标是引导读者逐步了解MFC的基本概念,通过实例操作,掌握创建和定制MFC控制,以及处理事件的方法,从而具备独立开发Windows应用程序的能力。
在MFC(Microsoft ...这个过程涉及到对Windows消息的处理,滚动条的控制,以及对话框布局的管理,是MFC编程中一个较为复杂但实用的技巧。对于需要显示大量信息或者需要动态调整内容的对话框,这种技术尤其有用。
在MFC(Microsoft Foundation Class)库中,开发者经常利用...为了更深入地理解和定制MFC应用,你需要熟悉Windows API和ActiveX控件的使用,以及MFC类库的工作原理。希望这些信息能帮助你在开发过程中实现预期的效果。
通常,你会在资源文件中找到窗口布局定义,然后在代码中创建并配置CSplitterWnd对象。 6. **注意事项** - 分割窗口的子窗口需要在父窗口(即分割窗口)创建后才能创建,因为它们依赖于父窗口的存在。 - 用户拖动...
开发者通过这个项目可以深入理解MFC的工作原理,同时也能掌握音频播放的基本技术。通过不断的实践和优化,这个简单的播放器可以进一步增强功能,例如支持更多音频格式、实现音效调节、增加播放列表等。
这些文件包含了窗口布局、图标、字符串等信息,通过RC编辑器可以直观地进行设计。 2. **对话框类**:对话框在MFC中由CDialog派生的类表示,通过DoModal()或Create()方法创建。时钟程序的主界面可能就是一个对话框,...
1. **MFC框架**:MFC是基于面向对象编程的,它将Windows的消息机制、窗口、控件等抽象成类,提供了丰富的基类。开发者可以通过继承这些基类并重写特定方法来实现自己的功能。 2. **CWinApp类**:这是MFC应用程序的...
可以使用`DockControlBar()`和`UndockPane()`函数来控制窗口的停靠和浮动状态。 7. **布局管理**:MFC提供了一些布局管理类,如`CMFCDockingManager`和`CMFCBasePane`,它们可以帮助你管理浮动窗口和其他控件的位置...
6. **CControlBar**: MFC中的控制栏类,如工具栏、状态栏和对话框,用于增强用户界面。 7. **CMDIChildWnd**: 多文档接口(MDI)应用程序中的子窗口类,用于显示和编辑多个文档。 8. **ON_COMMAND()和ON_MESSAGE()宏...
MFC是微软提供的一种C++类库,它为Windows应用程序开发提供了丰富的功能,包括窗口、对话框、控件等组件的封装。 1. **MFC简介** MFC是Microsoft的一个开源类库,用于简化Windows应用程序的开发。它基于面向对象的...
在本文中,我们将深入探讨MFC皮肤库界面的实现原理、设计方法以及相关的知识点。 首先,MFC皮肤库的核心在于对窗口控件的外观进行封装,以实现不同风格的界面。这通常涉及到以下几个方面: 1. **资源管理**:皮肤...