MFC技术系列(四)--Frame窗口之Control Bar(1)

接上篇,  MFC技术系列(四)--Frame窗口(2)

MFC中主要涉及到下面的文件和类:

barcore.cpp CControlBar

dockcont.cpp CDockContext

bardock.cpp CDockBar

winfrm2.cpp CFrameWnd

bartool.cpp CToolBar

 

MFCFrame窗口除了支持Doc-View框架外,还提供了一个重要特性,就是Dock,即:我们可以从CControlBar派生各种Bar,通过CFrameWnd所提供的Dock方法,可以将该Bar Dock到四个方向,或者让其Floating(通过另一个特殊的Frame窗口来支持,后面将会解释)。在MFC提供的默认SDI或者MDI框架代码中,我们能看到类似如下的代码:

    m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);

    EnableDocking(CBRS_ALIGN_ANY);

DockControlBar(&m_wndToolBar);

其中,分别表示起如下作用:

1.     让该tool bar可以dock到任何边

2.       初始化Frame窗口的四个dock bar

3.       tool bardock bar关联起来(做为dock bar的子窗口)

实际上,CFrameWnd提供的Dock功能是由其内部的四个Dock bar所提供的(分别对应四个边)。

1.1           CControlBar

先说说CControlBar类,它的定义位于afxext.h中,实现位于barcore.cpp中。在MFC中说到Bar,一般都是指继承该类而来。既然CControlBar是依赖于CFrameWnd,它的m_pDockSite方法即用来保存其所在的Frame窗口对象的指针,这种关联在CControlBarOnCreate方法中完成,而且Frame窗口也借由它的AddControlBar方法完成对ControlBar的关联(可见这里是双向关联)。当然,在CContolBar销毁(OnDestroy)时,将调用Frame窗口的RemoveControlBar,解除关联。

它的主要功能反映在下面的方法中:

EnableDocking:该方法并不在barcore.cpp中,而是在bardock.cpp中。

下面是两个针对Control Bar尺寸风格进行具体计算的方法,虚方法。这两个方法和对应的风格主要是在CToolBar中应用得较多。该方法的调用入口请参见下面的CFrameWnd的描述:

CalcFixedLayout:对应CBRS_SIZE_FIXED。该风格表现为:无论是停靠,还是浮动状态,其形状都不可改变。

CalcDynamicLayout:对应CBRS_SIZE_DYNAMIC。该风格表现为:如果是浮动状态,那么当用户拖动工具栏的边缘时,工具栏在需要的地方换行以改变形状。如果是停靠状态,则不可以改变形状。

消息响应方法:

1.OnPaint

此时,主要由DoPaint虚方法完成绘制。默认包含两个步骤,即:绘制Bar的边缘DrawBorders和绘制Bar的手柄DrawGripper。值得注意的是,CToolBar具有自己的OnPaint方法,它只是处理了延迟layout的情形,然后调用缺省的消息处理(DefWindowProc)。我们知道系统在处理WM_PAINT消息时,将会发送WM_NCPAINTWM_ERASEBKGND两个消息(在当前场景下,background erase消息是在EraseNonClient方法中发出的)。因此,实际上,CToolBar是在OnNcPaint方法中完成绘制,不过它并没有自己的实现,该方法中仅仅是调用CControlBarEraseNonClient方法。在EraseNonClient方法中,由于是要处理非客户区,所以使用了CWindowDC,而且,剪切掉了客户区域,只留下非客户区域供绘制(使用CDCExcludeClipRect方法,具体请参见后续文章《DC和绘图》),具体绘制的内容仍然是BorderGripper(也是调用上述两个方法)。

2. OnWindowPosChanging

对消息WM_WINDOWPOSCHANGING的响应,此时,根据CBRS_BORDER系列风格,将right,或bottom边缘处理为无效区域。

该消息的产生应当区别停靠和浮动状态:

a. 停靠状态:此时,CControlBar为子窗口,当移动Bar时,其position发生了变化,故会产生该消息;

b. 浮动状态:此时,CControlBar同样为子窗口。但你所移动的为其外面的Frame窗口,所以如果仅仅是移动,将不会产生该消息。只有改边Frame窗口尺寸时,Bar尺寸和position也被更改,才会产生该消息。

这里默认并没有调用基类的方法,而是直接调用了DefWindowProc API。原因是:基类将会调用具体窗口的消息处理函数,即:CWnd中的m_pfnSuper成员,但是某些控件并不能正确处理WM_WINDOWPOSCHANGING消息,比如:带有TBSTYLE_FLAT 风格的CToolBar。所以,直接采用了上述API,它使用系统缺省的消息处理函数,处理所有未处理的消息。

说到该消息,不能不提到下面三个方法:

BeginDeferWindowPosDeferWindowPosEndDeferWindowPos。它们常用来处理多个窗口协同的尺寸变化。它实际上是同时记录了多个窗口的位置信息(使用HDWP 结构),在最后一起来改变它们。

3. OnSizeParent

响应父窗口尺寸变化。该消息为MFC私有消息,由父窗口发送到子窗口。在Dock体系中,父窗口就是该Control Bar所在的Frame窗口。

4. OnLButtonDown

鼠标按下,调用CWndOnToolHitTest方法判断是否点击为空白区域(没有点击到任何子窗口)时,准备拖动Bar。此时,将委托给其内部的CDockContext实例的StartDrag方法。那么什么时候结束拖动呢? CDockContext帮我们处理了拖动到结束的所有绘画细节。详细内容请看“CDockContext类”一节。

5. OnLButtonDblClk

鼠标双击,同样也是处于空白区域时,在浮动和停靠状态间执行切换。此时,调用CDockContextToggleDocking方法。

6. OnMouseActivate

如果处于浮动状态,那么将激活顶层top-level窗口,通常就是应用的主窗口。这里使用了CWnd提供的ActivateTopParent方法

 

1.2           CFrameWnd

该类提供了如下方法用来封装ControlBarDock功能:DockControlBarFloatControlBar。其中,

1. DockControlBar初始化Frame窗口的四个dock bar

2. FloatControlBar要实现浮动,那么包围在ControlBar之外的一定是一个不同于当前Frame窗口的另一个窗口。MFC提供了CMiniDockFrameWnd类服务于此。因此,在该方法内部,实际上创建一个该类的对象,作为该Bar新的Frame。(前面提到CControlBar中使用m_pDockSite存放Frame窗口对象)

 

Frame窗口在哪里做Layout

Frame窗口尺寸改变时,其RecalcLayout将会被调用。在该方法中,FrameWnd的所有子窗口都将被layout,主要工作的方法就是RepositionBars,其声明如下:

void RepositionBars(

   UINT nIDFirst,

   UINT nIDLast,

   UINT nIDLeftOver,

   UINT nFlag = reposDefault,

   LPRECT lpRectParam = NULL,

   LPCRECT lpRectClient = NULL,

   BOOL bStretch = TRUE

);

其中,

1.       nIDFirstnIDLast指明了子窗口的ID范围。在RecalcLayout中,使用了0 ~ 0xffffnIDLeftOver则表明该子窗口将充满除了control bars外剩余的客户区域,在RecalcLayout中,使用了AFX_IDW_PANE_FIRST,这也是Frame窗口中的ViewID

2.       该方法中,将遍历所有子窗口,如果在first ~ last范围中时,将发送WM_SIZEPARENT消息到每个子窗口。此时,就是Frame窗口的四个DockBar。在它们响应方法OnSizeParent中,将会根据设置的CBRS风格,调用前面提到的CControlBarCalcDynamicLayout动态的layout方法,该方法的默认实现将会调用CalcFixedLayout,而CDockBar重载了该方法,其实现逻辑比较复杂(bardock.cpp)

3.       然后,该方法将会调用ViewCalcWindowRect方法,以便能充满剩余客户区域。该方法为CWnd提供,CView重载了它。

4.       ControlBar处于浮动状态时,CFrameWndFWS_SNAPTOBARS风格表明该Frame窗口将调整为刚好包围ControlBar。此时,调用稍微复杂些。

具体代码片段如下:

    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);

 

 

 

 

MFC 提供了如下 CControlBar 的派生类: CDockBar, CToolBar, CStatusBar, CDialogBar, CReBar 。这些类的详细情况,将在后续的章节中描述。

你可能感兴趣的:(框架,api,layout,null,mfc,border)