control bar 的用法 转
来源:互联网 发布:菏泽矩阵直销软件 编辑:程序博客网 时间:2024/06/07 01:34
MFC技术系列(四)--Frame窗口之Control Bar(1)
Visual C++ 2007-12-25 23:36:11 阅读92 评论0 字号:大中小 订阅
接上篇, MFC技术系列(四)--Frame窗口(2)
MFC中主要涉及到下面的文件和类:
barcore.cpp (CControlBar)
dockcont.cpp (CDockContext)
bardock.cpp (CDockBar)
winfrm2.cpp (CFrameWnd)
bartool.cpp (CToolBar)
MFC的Frame窗口除了支持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 bar和dock bar关联起来(做为dock bar的子窗口)
实际上,CFrameWnd提供的Dock功能是由其内部的四个Dock bar所提供的(分别对应四个边)。
1.1 CControlBar类
先说说CControlBar类,它的定义位于afxext.h中,实现位于barcore.cpp中。在MFC中说到Bar,一般都是指继承该类而来。既然CControlBar是依赖于CFrameWnd,它的m_pDockSite方法即用来保存其所在的Frame窗口对象的指针,这种关联在CControlBar的OnCreate方法中完成,而且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_NCPAINT和WM_ERASEBKGND两个消息(在当前场景下,background erase消息是在EraseNonClient方法中发出的)。因此,实际上,CToolBar是在OnNcPaint方法中完成绘制,不过它并没有自己的实现,该方法中仅仅是调用CControlBar的EraseNonClient方法。在EraseNonClient方法中,由于是要处理非客户区,所以使用了CWindowDC,而且,剪切掉了客户区域,只留下非客户区域供绘制(使用CDC的ExcludeClipRect方法,具体请参见后续文章《DC和绘图》),具体绘制的内容仍然是Border和Gripper(也是调用上述两个方法)。
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,它使用系统缺省的消息处理函数,处理所有未处理的消息。
说到该消息,不能不提到下面三个方法:
BeginDeferWindowPos、DeferWindowPos、EndDeferWindowPos。它们常用来处理多个窗口协同的尺寸变化。它实际上是同时记录了多个窗口的位置信息(使用HDWP 结构),在最后一起来改变它们。
3. OnSizeParent
响应父窗口尺寸变化。该消息为MFC私有消息,由父窗口发送到子窗口。在Dock体系中,父窗口就是该Control Bar所在的Frame窗口。
4. OnLButtonDown
鼠标按下,调用CWnd的OnToolHitTest方法判断是否点击为空白区域(没有点击到任何子窗口)时,准备拖动Bar。此时,将委托给其内部的CDockContext实例的StartDrag方法。那么什么时候结束拖动呢? CDockContext帮我们处理了拖动到结束的所有绘画细节。详细内容请看“CDockContext类”一节。
5. OnLButtonDblClk
鼠标双击,同样也是处于空白区域时,在浮动和停靠状态间执行切换。此时,调用CDockContext的ToggleDocking方法。
6. OnMouseActivate
如果处于浮动状态,那么将激活顶层top-level窗口,通常就是应用的主窗口。这里使用了CWnd提供的ActivateTopParent方法。
1.2 CFrameWnd类
该类提供了如下方法用来封装ControlBar的Dock功能:DockControlBar和FloatControlBar。其中,
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. nIDFirst和nIDLast指明了子窗口的ID范围。在RecalcLayout中,使用了0 ~ 0xffff。nIDLeftOver则表明该子窗口将充满除了control bars外剩余的客户区域,在RecalcLayout中,使用了AFX_IDW_PANE_FIRST,这也是Frame窗口中的View的ID。
2. 该方法中,将遍历所有子窗口,如果在first ~ last范围中时,将发送WM_SIZEPARENT消息到每个子窗口。此时,就是Frame窗口的四个DockBar。在它们响应方法OnSizeParent中,将会根据设置的CBRS风格,调用前面提到的CControlBar的CalcDynamicLayout动态的layout方法,该方法的默认实现将会调用CalcFixedLayout,而CDockBar重载了该方法,其实现逻辑比较复杂(bardock.cpp中)
3. 然后,该方法将会调用View的CalcWindowRect方法,以便能充满剩余客户区域。该方法为CWnd提供,CView重载了它。
4. 当ControlBar处于浮动状态时,CFrameWnd的FWS_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。这些类的详细情况,将在后续的章节中描述。
MFC技术系列(四)--Frame窗口之Control Bar(2)
Visual C++ 2007-12-25 23:26:05 阅读35 评论0 字号:大中小 订阅
1.3 CDockContext类
在前面的CControlBar类中,提到了当拖动荡Bar时,将借助CDockContext类的方法来完成。CDockContext类主要就是用来完成对Bar的行为,包括:拖动,在浮动和停靠间Toggle、改变尺寸。下面分别做一个说明:
1. Drag(拖动)
标准的行为,即从鼠标左键按下Bar的空白区域(非子窗口区域),然后拖动,绘制焦点矩型,直到鼠标左键抬起,拖动结束。涉及的方法包括:
StartDrag:分三种情况分别计算拖动矩形的初始值(由于有水平和垂直两个方向,所以矩形都是成对的):CBRS_SIZE_DYNAMIC风格、CBRS_SIZE_FIXED风格和其它风格。对于第二种固定尺寸风格来说,只需要一对矩形;而对第一种风格,因为浮动状态还可以改变尺寸,所以还需要计算一个浮动矩形,用m_rectFrameDragHorz和m_rectFrameDragVert两个成员保存。该矩形的计算经过三个步骤:第一,先调用Bar的CalcDynamicLayout计算Bar的Rect(相当于Frame的客户区);第二,计算Frame的Rect,此时,调用了CMiniFrameWnd的CalcBorders方法,它调用AdjustWindowRectEx API,该方法用来依据客户区域计算整个窗口Rect;第三,减去窗口边缘的尺寸
Move:响应鼠标移动消息,移动上述矩形。另外,还判断是否能够Dock,依据的信息包括:Ctrl键按下(m_bForceFrame)、Shift键按下(m_bFlip)、Dock位置风格(CBRS_ORIENT_VERT和CBRS_ORIENT_HORZ),主要逻辑封装在其CanDock方法中,该方法最终的判断会交给Frame窗口(由m_pDockSite记录)的CanDock方法。
Track:Capture鼠标,从消息队列获取消息,实际处理的消息包括:
a. WM_LBUTTONUP:如果是Drag状态,那么调用EndDrag,结束拖动;否则为Resize状态,那么调用EndResize结束该行为。
b. WM_MOUSEMOVE:Drag状态,调用Move移动焦点矩形;否则,调用Stretch方法,改变焦点矩形的尺寸,然后绘制它
c. WM_KEYUP:调用其OnKey方法,主要处理Ctrl和Shift键
d. WM_KEYDOWN:调用其OnKey方法,主要处理Ctrl和Shift键。此时,如果Esc键被按下,那么CancelLoop将被调用,从而结束上述行为(Drag,或Resize)
e. WM_RBUTTONDOWN:调用CancelLoop
可见,在Drag过程中,可以使用Esc,或者鼠标右键结束Drag。(后面的Resize也同样适用)
EndDrag:下面两个步骤
a. 调用CancelLoop
b. 根据Dock的状态,调用Frame窗口的DockControlBar,或者FloatControlBar完成相应的Dock
另外两个方法:
InitLoop:下面三个步骤:
a. 处理消息队列中的WM_PAINT消息
b. 初始化一些状态和中间尺寸
c. 获得桌面窗口(CWnd的GetDesktopWindow)
d. 调用桌面窗口的LockWindowUpdate,任何绘画都不会生效,当然窗口也不可能被移动,直到调用UnlockWindowUpdate
e. 获取桌面窗口的DC(通过CWnd的GetDCEx)
CancelLoop:下面四个步骤:
a. 调用DrawFocusRect,消除Drag矩形
b. 然后释放鼠标的Capture
c. 调用UnlockWindowUpdate,让绘画生效
d. 释放DC
2. Stretch(Resize)
这个过程很类似上面的Drag。基本可以对应起来,它们共用Track方法,从开始到结束,基本是StartResize, InitLoop, Track (Stretch), EndLoop, EndResize。
3. Toggle Docking
在浮动和停靠两种状态间切换,由该类的ToggleDocking方法完成。该方法比较简单。
4. 绘制焦点矩形
无论是Drag,还是Resize,都有这个步骤。它由该类的DrawFocusRect方法完成,绘制部分调用了CDC的DrawDragRect方法。
MFC技术系列(四)--Frame窗口之Control Bar(3)
Visual C++ 2007-12-25 23:35:05 阅读25 评论0 字号:大中小 订阅
1.4 CDockBar类
该类所代表的子窗口是CControlBar的父窗口,而Frame窗口则是该类所在的子窗口的父窗口。从表面上看,CControlBar Dock在了Frame窗口中,但实际上,中间还有一层窗口,这就是CDockBar。前面已经提到,CFrameWnd提供了四个CDockBar实例,分别对应四个方向。
该类的定义位于afxpriv.h中(同CDockContext一样),实现位于bardock.cpp中。下面是该类的定义:
class CDockBar : public CControlBar
{
DECLARE_DYNAMIC(CDockBar)
// Construction
public:
/* explicit */ CDockBar(BOOL bFloating = FALSE); // TRUE if attached to CMiniDockFrameWnd
virtual BOOL Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID);
// Attributes
BOOL m_bFloating;
virtual BOOL IsDockBar() const;
int GetDockedCount() const;
virtual int GetDockedVisibleCount() const;
// Operations
void DockControlBar(CControlBar* pBar, LPCRECT lpRect = NULL);
void ReDockControlBar(CControlBar* pBar, LPCRECT lpRect = NULL);
BOOL RemoveControlBar(CControlBar*, int nPosExclude = -1, int nAddPlaceHolder = 0);
void RemovePlaceHolder(CControlBar* pBar);
// Implementation
public:
virtual ~CDockBar();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
virtual CSize CalcFixedLayout(BOOL bStretch, BOOL bHorz);
virtual void DoPaint(CDC* pDC);
// public implementation helpers
void GetBarInfo(CControlBarInfo* pInfo);
void SetBarInfo(CControlBarInfo* pInfo, CFrameWnd* pFrameWnd);
int FindBar(CControlBar* pBar, int nPosExclude = -1);
void ShowAll(BOOL bShow);
CPtrArray m_arrBars; // each element is a CControlBar
protected:
BOOL m_bLayoutQuery;
CRect m_rectLayout;
CControlBar* GetDockedControlBar(int nPos) const;
// implementation helpers
int Insert(CControlBar* pBar, CRect rect, CPoint ptMid);
virtual void OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler);
//{{AFX_MSG(CDockBar)
afx_msg void OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp);
afx_msg void OnNcPaint();
afx_msg void OnWindowPosChanging(LPWINDOWPOS lpWndPos);
afx_msg void OnPaint();
afx_msg LRESULT OnSizeParent(WPARAM, LPARAM);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
friend class CMiniDockFrameWnd;
};
1. 首先,我们知道CDockBar也是一种CControlBar。CDockBar的通过其Create方法创建。既然在Control Bar dock时,需要重新计算位置和尺寸,因此,创建时不需要位置信息。调用CWnd的Create方法前,需要先注册class,即:
调用AfxDeferRegisterClass(AFX_WNDCONTROLBAR_REG)。其中,AfxDeferRegisterClass在这里实际是指AfxEndDeferRegisterClass方法,该方法的实定义位于afximpl.h中,实现位于wincore.cpp中。参数AFX_WNDCONTROLBAR_REG为MFC所定义的几个窗口类型的一种。下面是其它几类的声明:
#define AFX_WND_REG 0x00001
#define AFX_WNDCONTROLBAR_REG 0x00002
#define AFX_WNDMDIFRAME_REG 0x00004
#define AFX_WNDFRAMEORVIEW_REG 0x00008
#define AFX_WNDCOMMCTLS_REG 0x00010 // means all original Win95
这些宏对应的类名称的定义也位于afximpl.h中。下面是MFC采用的命名方式:
// special AFX window class name mangling
#ifndef _UNICODE
#define _UNICODE_SUFFIX
#else
#define _UNICODE_SUFFIX _T("u")
#endif
#ifndef _DEBUG
#define _DEBUG_SUFFIX
#else
#define _DEBUG_SUFFIX _T("d")
#endif
#ifdef _AFXDLL
#define _STATIC_SUFFIX
#else
#define _STATIC_SUFFIX _T("s")
#endif
#define AFX_WNDCLASS(s) /
_T("Afx") _T(s) _T("70") _STATIC_SUFFIX _UNICODE_SUFFIX _DEBUG_SUFFIX
#define AFX_WND AFX_WNDCLASS("Wnd")
#define AFX_WNDCONTROLBAR AFX_WNDCLASS("ControlBar")
#define AFX_WNDMDIFRAME AFX_WNDCLASS("MDIFrame")
#define AFX_WNDFRAMEORVIEW AFX_WNDCLASS("FrameOrView")
#define AFX_WNDOLECONTROL AFX_WNDCLASS("OleControl")
同时,提供了几个全局变量来保存这些类名称。对于当前类型,类名变量为_afxWndControlBar。它使用COLOR_BTNFACE作为背景色。
2. GetDockedCount方法说明一个CDockBar实例能够dock多个control bar(保存在其m_arrBars中)。另外,还提供了GetDockedVisibleCount获取可见的control bar的数目。既然能够dock多个control bar,那么就存在对control bar的管理,包括下面的方法:
DockControlBar, RemoveControlBar, ReDockControlBar, RemovePlaceHolder, FindBar, GetDockControlBar。这里重点解读一下DockControlBar,其基本步骤如下:
a. 判断是否已经dock,否则继续
b. 根据CControlBar实例的风格,设置Dock bar的风格
c. 根据Dock bar风格和停靠状态,设置正确的Control Bar的停靠风格,并打开所有border (CBRS_BORDER_ANY)
d. 如果是更换dock bar,那么先将control bar隐藏
e. 如果control bar已经具有一个位置矩形,比如:拖动control bar停靠时,那么调用dock bar的Insert方法,获得合适的rect,然后再移动control bar的位置;否则,直接停靠到最边缘(这通常发生在control bar刚刚创建后)
f. 重新设置control bar的父窗口,然后将control bar从原来的dock bar,或者当前dock bar的原来位置移去
g. 显示control bar
h. 设置让Frame窗口重新lay out其子窗口的标记。注意,此时,layout并未真正发生!那么什么时候真正layout呢,当然是在Frame窗口尺寸改变时。请参看1.2节。
3.
如果需要保存Dock状态和位置,CControlBarInfo类提供了这一功能。通过CDockBar的GetBarInfo方法可以得到CControlBarInfo对象。
(未完,待续...)
- control bar 的用法 转
- matlab bar的用法简介
- List Control的用法:
- list control的用法
- list control的用法
- list control的用法
- picture control的用法
- Spin Control 和 Slider Control 的用法
- Android ToolBar+Translucent System Bar的用法
- onsen ui button-bar的用法尝试
- Android ToolBar+Translucent System Bar的用法
- CSizingControlBar - a resizable control bar
- 请教Control.Tag的用法
- MFC Tab Control 的用法
- MFC Edit control的用法
- Reference和Control的用法
- IPHONE应用开发 - Tab Bar Control(标签栏)使用的简单介绍
- MFC-Scroll Bar用法
- EMF学习笔记6——模型验证
- 自定义标签库中的tld文件
- Oracle 表连接方式(内连接/外连接/自连接) 详解
- Java容器类List、ArrayList、Vector及map、HashTable、HashMap的区别与用法
- 百度笔试题7.3
- control bar 的用法 转
- 哈哈,同志们好
- 百度笔试题7.4
- 百度笔试题7.5
- RAID 磁盘阵列
- 操作系统学习笔记
- 皮影
- Policies/Binary Compatibility Issues With C++
- 百度笔试题8