理解MFC控制条窗口布局原理之一

理解MFC控制条窗口布局原理之一,第1张

理解MFC控制条窗口布局原理之一,第2张

首先,框架窗口

当帧的大小改变时,帧将接收WM_SIZE消息。这个消息的处理函数是CFrameWnd::OnSize。这个函数然后调用RecalcLayout来重新定位子窗口。其主要代码如下:

if(get style()& 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 _诺activate | SWP _诺莫夫| SWP _诺佐德);
}
else
{
reposition bars(0,0xffff,AFX_IDW_PANE_FIRST,reposExtra,& m _ rect border);
}

这里有两点需要注意。第一个是FWS _斯纳巴风格。一般来说,是框架窗口主动改变自己的大小,子窗口不得不修改自己来适应框架窗口的变化。然而,这个FWS_SNAPTOBARS样式是相反的,它使框架窗口改变它的大小以适应它的子窗口。但是我一路下来都没有找到这个样式的框架窗口,都走了else分支(其实这个样式是给CMiniDockFrameWnd的,这个框架窗口的大小是根据它的内部控制条来决定的)。其次,应该注意的是,RecalcLayout是不可重入的。虽然MFC防止重入的方法非常简单有效,但是它的方法能防止多线程重入吗?换句话说,MFC本身不是一个线程安全的库。

好了,现在我们进入了整个重新布局动作的主要功能,RepositionBars。我们来仔细分析一下它做过哪些见不得人的“勾当”(这个功能在MSDN是有文档记录的,所以关于它的几个参数的意义这里就不描述了):

首先,它创建一个AFX_SIZEPARENTPARAMS结构,并填充其成员变量,其中最重要的是两个:bStretch和rect。前者是一个BOOL变量,表示子窗口是否需要拉伸(拉伸到与客户区相同的宽度或高度),后者是当前的客户区大小。

然后根据Z-order,框架窗口依次向其所有子窗口发送WM_SIZEPARENT消息(ID为NID left的子窗口除外),通知其根据新的客户区重新计算大小和位置,并从AFX_SIZEPARENTPARAMS::rect中扣除其矩形,这样计算完所有子窗口后,框架按什么顺序?以MDIDemo为例,这个例子创建了一个CToolBar和一个CThumbnailListCtrlBar。当它们都处于Dock状态时,跟踪记录的已发送窗口的ID是0x e801×0x e81b×0x e81e×0x e81c×0x e81d,看每个子窗口ID的定义就知道顺序是左边Dockbar下面状态栏上面Dockbar,右边Dock Bar。这里需要注意的是,找到的第一个子窗口的ID是0xE900(0xE900定义为AFX _ IDW _窗格_第一个,这个例子中是视图的窗口ID,对应这个ID,还有一个ID叫AFX _ IDW _窗格_最后一个,SDI的视图窗口ID,MDI的MDIClient窗口,divider窗口等。应该在上面两个ID值之间),等于not sent,所以WM_SIZEPARENT消息不发送给它。那么为什么工具栏和缩略图栏也没有得到消息呢?因为Dock bar是它的父窗口,所以消息被发送到Dock Bar,Dock Bar会计算停靠在它里面的所有子窗口的大小,所以框架窗口不需要为此担心。此外,浮动控制条不接受WM_SIZEPARENT消息,原因很简单,浮动控制条不会随着主框架窗口的大小而改变)。

发送后(这里有一个细节,框架窗口用SendMessage发送WM_SIZEPARENT消息,也就是说在子窗口处理完消息后SendMessage才会返回,框架窗口再将消息发送到下一个子窗口。全部发送意味着所有子窗口都从AFX _ sizepartparams:: rect)中移除了想要的矩形,剩下的就是最后可用的客户区。根据nFlags的值,执行不同的返回动作。其中,reposQuery表示只进行查询,没有实际的重新布局动作。将最后剩余的客户区域复制到lpRectParam,它将被返回。如果不重新查询,它将被重新放置。对于reposDefault,我们需要将ID为NID severn的子窗口的大小和位置调整到被其他子窗口剪切后剩余的可用客户区域,使这个子窗口刚好完全覆盖最后一个可用区域(也就是说所有子窗口都充满了客户区域)。当nFlag等于reposExtra时,在调整NID leung子窗口的大小和位置之前,使用lpRectParam来校正最后一个可用区域。具体来说,AFX_SIZEPARENTPARAMS::rect向内收缩,收缩的距离由lpRectParam指定,这样最后剩下的客户区就不会被NID severn子窗口占用,而是空会留下一些地方。更正后,一次性重新布局所有子窗口。

至此,框架窗口的所有动作都完成了。

第二,控制条子窗口

在分析了框架窗口之后,再分析要在控制栏这一侧完成的响应动作。根据之前的跟踪,我们知道除了CStatusBar和CDockBar之外,继承自CControlBar的控件栏如CToolBar和CDialogBar都不能接收WM_SIZEPARENT消息,它们的父窗口CDockBar代替它们接收这个消息。所以整个重新布局过程的起点是CDockBar对WM _ sizepart消息的处理函数CDockBar::OnSizeParent(对于CStatusBar,其起点是CControlBar::OnSizeParent,这里不打算进一步分析,有兴趣可以自己做)。步骤1让我们分析这个函数完成的动作。这个功能不长。列出完整的代码:

LRESULT CDockBar::onsize parent(WPARAM WPARAM,LPARAM LPARAM)
{
AFX _ size parent params * lpLayout =(AFX _ size parent params *)LPARAM;

//如果lpLayout-> hDWP = = NULL
BOOL bLayoutQuery = m_bLayoutQuery,则将m _ bLayoutQuery设置为TRUE
CRect rect layout = m _ rect layout;

m _ bLayoutQuery =(lpLayout-> hDWP = = NULL);
m _ rect layout = LP layout-> rect;
LRESULT LRESULT = CControlBar::onsize parent(wParam,lParam);

//restore m _ bLayoutQuery
m _ bLayoutQuery = bLayoutQuery;
m _ rect layout = rect layout;

返回lResult
}

如前所述,WM _ SIZEParentParams消息将指向AFX_SIZEPARENTPARAMS结构的指针作为参数传递。这里我们先把这个结构拿出来,然后判断AFX_SIZEPARENTPARAMS::hDWP是否空。如果是,说明父窗口只是想查询,并没有真正做重新布局的动作(回到RepositionBars,当nFlags为reposQuery时,BeginDeferWindowPos没有被调用,所以AFX_SIZEPARENTPARAMS::hDWP必须为NULL)。完成必要的变量保护后,输入父类CControlBar的OnSizeParent。这里根据控制条窗口的样式,决定如何计算控制条的大小。具体来说;

DWORD dwMode = lpLayout->bStretch?LM _ STRETCH:0;//拉伸?
if((m _ dw style & CBRS _ size _ dynamic)& & m _ dw style & CBRS _ floating)//浮动,形状可变
{
dw mode | = lm _ horz | lm _ mruwidth;//计算水平状态的常用大小
}
else if(dw style & CBRS _ orient _ horz)//水平停靠
{
dw mode | = lm _ horz | lm _ horz dock;//计算水平停靠状态大小
}
else
{
dw mode | = lm _ vert dock;//计算垂直停靠状态大小
}
csize size = calcdynamicllylayout(-1,dw mode);

注意,最后一行调用CalcDynamicLayout,这是一个虚函数。第一个调用的是CControlBar:: CalcDynamicLayout,它调用CalcFixedLayout(也是一个虚函数)。我们注意到CDockBar重载了这个函数,所以转了一圈又回到了CDockBar。

位律师回复
DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » 理解MFC控制条窗口布局原理之一

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情