MFC工具条和状态栏
Windows控制窗口
Windows (Windows95或者以上版本) 提供了系列通用控制窗口,其中包括工具条(ToolBar)、状态栏(StatusBar)、工具条提示窗口(ToolTip)。
Windows在一个DLL加载时注册个控制窗口的“窗口类”。例如,工具条的“窗口类”是“ToolbarWindow32”,状态栏的“窗口类”是“msctls_statusbar32”,工具条提示窗口的“窗口类”是“tooltips_class32”。为了保证该DLL被加载,使用控制“窗口类”前,应该首先调用函数InitCommonControl。MFC在窗口注册函数AfxDeferRegisterClass中实现了这一点。见2.2.1节MFC下窗口的注册。
创建通用控制窗口,可以使用专门的创建函数,如创建工具条的函数::CreateToolBarEx,创建状态栏的函数::CreateStatusBarEx。也可以调用窗口创建函数::CreateWindowEx,但是需要指定预定义的“窗口类”,必要的话还要其他步骤,如使用“ToolbarWindow32”“窗口类”创建工具栏后,还需要在工具栏中添加或者插入按钮。
一般,通用控制可以指定控制窗口风格(Style)。例如,具备风格CCS_TOP,表示该控制窗口放到父窗口客户区的顶部,具备CCS_BOTTOM,表示该控制窗口在客户区的底部。具体的控制窗口类可以有特别的适合于自己的风格,例如,TTS_ALWAYSTIP表示只要光标落在工具栏的按钮上,ToolTip窗口不论激活与否都会显示出来。
每一控制窗口类都有自己的窗口过程来处理自己的窗口消息,实现特定的功能。控制窗口类的窗口过程由Windows提供。
工具条
工具条的窗口过程处理了必要的消息,提供了标准工具条的功能,例如,工具条对客户化特征提供内在的支持,用户可以通过一个客户化对话框来添加、修改、删除或者重新安排工具条按钮。这些特征是否可以被用户所用或者用到什么地步是可以由程序控制的。
工具条的窗口过程将自动设置工具条的尺寸大小和位置,如果指定了控制窗口风格CCS_TOP或者CCS_BOTTOM,则窗口过程把工具条放到父窗口客户区的顶部或者底部。窗口过程任何时候只要收到WM_SIZE或者TB_AUTOSIZE消息就自动地调整工具条的大小和位置。
工具条的按钮被选中后,会产生一个命令消息,它的窗口过程把该消息送给父窗口的窗口过程处理。
工具条中的按钮并不以子窗口的形式出现,而是以字符或者位图按钮的方式显示,每个按钮大小相同,缺省是24*22个像素。每个按钮都有一个索引,索引编号从0开始。每个按钮包括如下属性:
按钮的字符串索引,位图索引,风格,状态,命令ID
按钮可以有两种风格TBSTYLE_BUTTON和TBSTYLE_CHECK,前者像一个标准按钮那样响应用户的按击,后者响应每一次按击,在按下和跳起两种状态之间切换。按钮响应用户的动作,给父窗口发送一个包含了该按钮对应命令ID的命令消息。一般一个按钮的命令ID对应一个菜单项。
工具条维护两个列表,分别用来存放工具条按钮使用的字符串或者位图,列表中的位图或者字符串从0开始编号,编号和按钮的索引相对应。
工具条可以是Dockable(泊位)或者Floatable(漂浮)的。
工具条可以有TBSTYLE_TOOLTIPS风格,如果具有这种风格,则创建和管理一个Tooltip控制,这是一个小的弹出式窗口,用来显示描述按钮的文本,平时该窗口隐藏,当鼠标落到按钮上面并停留约一秒后才弹出,在鼠标附近显示。
由于Tooltip窗口平时是隐藏的,所以不能接收鼠标消息来决定何时显示本窗口。这样,接收鼠标的窗口必须把鼠标消息送给Tooltip窗口,这是通过给Tooptip窗口发送消息TTM_RELAYEVENT来实现的。
状态栏
状态栏类似于工具条,有自己的窗口过程,可以泊位、漂浮。不过,习惯上状态栏都位于屏幕底部。每个状态条分成若干格(Status bar panes),每格从0开始编号,编号作为格的索引。每一个格,如同工具条的按钮一样,并不是一个Windows窗口。
MFC的工具条和状态栏类
MFC使用CToolBarCtrl、CStatusBarCtrl和CToolTipCtrl窗口类分别对工具条、状态栏、Tooltip控制窗口进行了封装。
但是,直接使用这些类还不是很方便。MFC提供了CToolBar、CStatusBar来处理状态栏和工具条,CToolBar、CStatusBar功能更强大,灵活。这两个类都派生于CControlBar。
在MFC下,建议这些控制条子窗口ID介于AFX_IDW_TOOLBARFIRST(0xE800)和AFX_IDW_CONTROLBAR_LAST(0Xe8FF)之间。这256个ID中,前32个又有其特殊性,用于MFC的打印预览中。
CControlBar派生于CWnd类,是控制条窗口类的基类,它派生出CToolBar、CStatusBar、CDockBar、CDialogBar、COleResizeBar类。CControlBar实现了以下功能:
和父窗口(边框窗口)的顶部或者底部或者其他边对齐。
可以包含子条目,这些条目或者是基于HWND的子窗口,或者是基于非HWND的条目。负责分配条目数组。
支持CBRS_TOP(缺省,控制条放在顶部),CBRS_BOTTOM(放在底部),CBRS_NOALIGN(父窗口大小变化时不重新放置控制条)等几种控制风格。
支持派生类的实现。几个派生类有一定的共性,或者其中两个有一定的共性,这样CControlBar实现的函数一部分只适用于某个派生类,一部分适用于两个或者多个派生类,还有一部分适用于所有的派生类。所谓适用,这里指派生类直接继承了CControlBar的实现,或者覆盖了其实现但是建立在扩展其实现的基础上。类似地,CControlBar的成员变量也不是为所有派生类所共同适用的。
CStatusBar和CControlBar一方面建立在CControlBar的基础之上,另一方面以Windows的通用控制状态栏和工具条为基础。它们继承了CControlBar类的特性,但是所封装的窗口句柄是相应的Windows控制窗口的句柄,如同CFormView继承了CSrcollView的视类特性,但是其窗口句柄是无模式对话框窗口句柄一样。
典型地,如果在使用AppWizard生成应用程序时,指定了要求工具条和状态栏的支持,则在主边框窗口的OnCreate函数中包含一段如下的代码,用来创建工具条、状态栏和设置一些特性。
//创建工具栏
if (!m_wndToolBar.Create(this) ||!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
//创建状态栏
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
// TODO: Remove this if you don't want tool tips or a resizeable toolbar
//对工具栏设置Tooltip特征
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
//使得工具栏可以泊位在边框窗口
// TODO: Delete these three lines if you don't want the toolbar to
// be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
工具条除了Tooltip,Resizeable,Dockable特性外,还可以是Floatable。应用程序可以使用CFrameWnd::SaveBarState保存边框窗口的控制条的有关信息到INI文件或者Windows Register库,使用LoadBarSate从INI文件或者Register库中读取有关信息并恢复各个控制条的设置。
下文,将讨论工具条等的创建、销毁,从中分析CControlBar和派生类的关系,讨论CControlBar如何实现共性,如何支持派生类的特定要求,派生类又如何实现自己的特定需求等。
控制窗口的创建
创建工具条、状态条、对话框工具栏的方法是不同的,所以必须给每个派生类CToolBar、CStatusBar、CDialogBar设计和实现自己的窗口创建函数Create。但是,它们是也是有共性的,共性由CControlBar的PreCreateWindow处理。在窗口创建之后,各个派生类都要进行的处理(共性)由CControlBar的OnCreate完成,特别的处理通过派生类的OnNcCreate完成。
PreCreateWindow
首先,讨论CControlBar 类的PreCreateWindow的实现。
BOOL CControlBar::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CWnd::PreCreateWindow(cs))
return FALSE;
//修改窗口风格,强制适用clipsliblings,以防重复绘制
cs.style |= WS_CLIPSIBLINGS;
//default border style translation for Win4
//(you can turn off this translation by setting CBRS_BORDER_3D)
if (afxData.bWin4 && (m_dwStyle & CBRS_BORDER_3D) == 0)
{
DWORD dwNewStyle = 0;
switch (m_dwStyle & (CBRS_BORDER_ANY|CBRS_ALIGN_ANY))
{
case CBRS_LEFT: //控制条在边框窗口的左边显示
dwNewStyle = CBRS_BORDER_TOP|CBRS_BORDER_BOTTOM;
break;
case CBRS_TOP://控制条在边框窗口的顶部显示
dwNewStyle = CBRS_BORDER_TOP;
break;
case CBRS_RIGHT://控制条在边框窗口的右边显示
dwNewStyle = CBRS_BORDER_TOP|CBRS_BORDER_BOTTOM;
break;
case CBRS_BOTTOM://控制条在边框窗口的底部显示
dwNewStyle = CBRS_BORDER_BOTTOM;
break;
}
// set new style if it matched one of the predefined border types
if (dwNewStyle != 0)
{
m_dwStyle &= ~(CBRS_BORDER_ANY);
m_dwStyle |= (dwNewStyle | CBRS_BORDER_3D);
}
}
return TRUE;
}
其中,afxData是一个全局变量,MFC用它来记录系统信息,如版本信息等。这里afxData.bWin4表示Windows版本是否高于4.0。
CToolBar的PreCreateWindow函数修改了窗口风格,也修改状态栏、工具栏等的CBRS_风格。CBRS_风格的改变不会影响窗口风格。因为这些CBRS_风格被保存在成员变量m_dwStyle中。
除了上述在程序中用到的影响工具条、状态栏等显示位置的CBRS_风格外,还有和泊位相关的CBRS_风格,CBRS_ALIGN_LEFT、CBRS_ALIGN_RIGHT、CBRS_ALIGN_BOTTOM、CBRS_ALIGN_TOP、CBRS_ALIGN_ANY,分别表示工具条可以在停泊