(一)工具条控制的主要功能
所谓工具条就是具有位图和分隔符组成的一组命令按钮,位图按钮部分可以是下推按钮、检查盒按钮、无线按 钮等。工具条对象类派生于主窗口架框类CframeWnd或CMDIFrameWnd,其类控制CToolBar::GetToolBarCtrl是 MFC类库中封装的一个成员函数,允许使用类库中提供的一般控制和附加功能,CtoolBar类控制成员控制提供了Windows一般控制的所有功能,然 而,通过调用 GetToolBarCtrl成员函数取得引用后,可以使工具条具有更强的特性。
工具条的创建具有四个步聚:首先是建立工具条资源;然后建立工具条对象结构;其次通过调用建立函数建立工具条对象并绑定;最后调用LoadToolBar调入工具条资源。
另外,还可以通过直接加载位图的方法来建立,步骤如下:首先建立工具条对象;然后通过调用建立函数建立工具条并绑定对象;其次调入包含按钮的位图;最后利用SetButtons 函数设置按钮的风格并与位图建立联系。
其中,所有按钮位图均存放在一个位图文件中,按钮位图的大小相同,默认为16点宽、15点高,位图必须从左至右存放。设置按钮函数具有指向一组控制标识符ID的指针和索引值,用来确定每个按钮的位置,如果存在分隔符ID_SEPARATOR, 那么该图像就不存在索引值。正常情况下工具条中的按钮都是单排从左至右排列的,可以通过SetButtonInfo函数改变排序规则。 工具条中最终形成的按钮大小相同,均为24 x 22 象素,每个按钮只对象一幅图像。工具条中的按钮默认为下推按钮,通过设置TBBS_CHECKBOX风格可以实现检查盒按钮,通过调用SetRadio成员函数可以实现无线按钮。
(二)工具条控制的对象结构
1、工具条的对象结构
(1)工具条的建立方法
CToolBar &ToolBar 建立工具条对象结构
Create 建立工具条对象并绑定
工具条类CToolBar::Create 的调用格式如下:
BOOL Create( CWnd* pParentWnd, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_TOP,
UINT nID = AFX_IDW_TOOLBAR );
其中参数pParentWnd用来确定指向工具条父窗口的指针;参数dwStyle用来确定工具条的风格,其取值如下;参数nID用来确定工具条子窗口的标识符。
CBRS_TOP 表示工具条在框架窗口的顶部
CBRS_BOTTOM 表示工具条在框架窗口的底部
CBRS_NOALIGN 表示工具条在父窗口改变大小时不响应
CBRS_TOOLTIPS 表示工具条具有动态提示功能
CBRS_SIZE_DYNAMIC 表示工具条是静态的不能改变
CBRS_SIZE_FIXED 表示工具条是动态的可以改变
CBRS_FLOATING 表示工具条是浮动的
CBRS_FLYBY 表示状态条上显示工具条中按钮的信息
CBRS_HIDE_INPLACE 表示工具条隐藏
除以上函数外,还包括设置按钮和位图的大小SetSizes、设置工具条的高度SetHeight、调 入工具条资源LoadToolBar、调入工具条按钮位图LoadBitmap、设置工具条按钮位图SetBitmap、设置工具条中位图按钮的风格和索 引值SetButtons等控制函数。
(2)工具条的类属性
工具条控制类的属性包括取得标识符ID对象按钮索引CommandToIndex、取得索引对应的命令 标识符ID或分隔符GetItemID、取得索引对应的矩形区域GetItemRect、取得按钮风格 GetButtonStyle、设置按钮风格SetButtonStyle、取得按钮的ID标识-风格-图象数GetButtonInfo、设置按钮ID 标识-风格-图象数SetButtonInfo、取得按钮提示文本GetButtonText、设置按钮提示文本SetButtonText和取得工具条 直接存取控制GetToolBarCtrl等。
2、工具条控制的对象结构
(1)工具条控制的建立方法
CToolBarCtrl &ToolBarCtrl 建立工具条控制对象结构
Create 建立工具条控制对象并绑定
工具条控制类CToolBarCtrl::Create的调用格式如下:
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
其中参数dwStyle用来确定工具条控制的风格,必须存在WS_CHILD风格;参数rect用来确定工具条控制的大小和位置;参数pParentWnd用来确定工具条控制的父窗口指针,不能为NULL;参数nID用来确定工具条控制的标识符。
可以利用WS_CHILD、WS_VISIBLE和WS_DISABLED来设置工具条窗口的风格,但必须合理设置如下控制风格:
CCS_ADJUSTABLE 允许用户处理工具条窗口大小,如果存在工具条窗口必须处理相应信
CCS_BOTTOM 使控制处于父窗口客户区域底部并与窗口同样宽
CCS_NODIVIDER 禁止在控制的顶部绘制2个象素的高亮条
CCS_NOHILITE 禁止在控制的顶部绘制1个象素的高亮条
CCS_NOMOVEY 使控制改变大小和移动时自动水平对齐,垂直对齐必须处理WM_SIZE消息
如果CCS_NORESIZE风格有效,则该风格无效
CCS_NOPARENTALIGN禁止控制自动移到父窗口顶部或底部,如果CCS_TOP或 CCS_BOTTOM风格
有效,则高度调整为默认而宽度可以改变
CCS_NORESIZE 禁止设置新的大小或无效值时使用默认宽度和高度值,而使用建立值
CCS_TOP 使控制自动停靠在父窗口客户区域顶部并与父窗口同样宽度
最后,还必须利用下面的风格来控制工具条
TBSTYLE_TOOLTIPS 使工具条建立并管理动态提示控制
TBSTYLE_WRAPABLE 使工具条控制按钮具有多行排列格式
(2)工具条控制中的数据结构
工具条控制中最常用的数据结构为TBBUTTON,其具体结构如下:
typedef struct _TBBUTTON {
int iBitmap; // 基于0的位图索引值
int idCommand; // 按钮按下时发送的命令值
BYTE fsState; // 按钮的状态
BYTE fsStyle; // 按钮的风格
DWORD dwData; // 应用程序定义的数据
int iString; // 基于0的按钮标签字符串索引值
} TBBUTTON;
其中按钮状态fsState的值如下:
TBSTATE_CHECKED 表示按钮具有TBSTYLE_CHECKED风格并且被按下
TBSTATE_ENABLED 表示按钮允许接受输入,否则变灰不接受任何输入
TBSTATE_HIDDEN 表示按钮不可见并且不接受任何输入
TBSTATE_INDETERMINATE 表示按钮是变灰的
TBSTATE_PRESSED 表示按钮正被按下
TBSTATE_WRAP 表示按钮具有换行特性,该按钮必须具有TBSTATE_ENABLED状态
按钮风格style可以是下列值的组合:
TBSTYLE_BUTTON 表示建立标准下推按钮
TBSTYLE_CHECK 表示建立检查状态按钮
TBSTYLE_CHECKGROUP表示建立检查按钮群
TBSTYLE_GROUP 表示建立按下状态按钮群
TBSTYLE_SEP 表示建立按钮分隔符
(3)工具条控制的类属性
工具条控制的类属性必然的联系判断按钮使能状态IsButtonEnabled、判断按钮检查状态 IsButtonChecked、判断按钮按下状态IsButtonPressed、判断按钮是否隐藏IsButtonHidden、判断按钮变灰状态 IsButtonIndeterminate、设置按钮状态SetState、取得按钮状态GetState、取得按钮有关信息GetButton、取得 按钮总数GetButtonCount、取得按钮矩形区域GetItemRect、设置按钮结构大小SetButtonStructSize、设置按钮大 小SetButtonSize、设置按钮位图大小SetBitmapSize、取得按钮提示控制GetToolTips、设置按钮提示控制 SetToolTips等。
(4)工具条控制类的操作方法
工具条控制类的操作方法包括使能按钮EnableButton、检查按钮CheckButton、按下 按钮PressButton、隐藏按钮HideButton、变灰按钮Indeterminate、增加按钮AddButtons、插入按钮 InsertButton、删除按钮DeleteButton、取得控制符ID对应的索引CommandToIndex、恢复工具条状态 RestoreState、保存工具条状态SaveState和重新确定工具条大小AutoSize等。
(三)工具条控制的应用技巧
可以这样说,工具条和上述常用控制是应用程序中不可缺少的功能元素,它的优劣会直接影响程序的基本功能和操作特性。所以这里将对工具条的建立技巧、状态保存与恢复、平面特性、停靠位置、排序方法、消息映射、状态更新、控制使用和属性控制等方面,全面阐述工具条的使用技巧。
1、工具条的建立技巧
(1)普通工具条的建立方法
如果应用程序在建立时就具有工具条,则只需对工具条中的按钮图标进行简单的增加、修改和删除等操作就可满足要求。如果未建立或者想增加其它工具条,则应按步骤追加建立。
首先打开已建立好的基于单文档的框架工程文件CTool并选择"Insert->Resource->ToolBar"选项,插入工具条资源并设置资源标识符;然后编辑工具栏中的按钮图标和相应的按钮标识符,并利用类向导ClassWizard 为按钮消息增加COMMAND和UPDATE_COMMAND_UI两种处理函数;在资源文件中增加和修改工具条图标的动态提示等内容;打开MainFrm.h包含文件在"CToolBar m_wndMainToolBar"后增加"CToolBar m_wndTestToolBar" 等来创建增加的工具条对象;在MainFrm.h 中设置建立函数所需的成员变量,如颜色变量为m_bColor、动态提示功能变量为m_bToolTips 等,注意成员变量名与其获取的参数应完全对应以便使用;最后在MainFrm.cpp中的OnCreate()建立函数中按下述示例规则增加控制代码,其实现具体步骤如下:
①在MainFrm.h中增加工具条对象控制和成员变量
#define TOOLLEFT 18
class CMainFrame:public CFrameWnd
......//其它代码
public:
BOOL m_bToolTips;//工具条提示功能
......//其它代码
protected://工具条控制成员变量
CStatusBar m_wndStatusBar; //框架程序的状态条
CTestToolBar m_wndMainToolBar;//框架程序的工具条
CTestToolBar m_wndTestToolBar;//新增工具条
CTestToolBar m_wndDockToolBar;//浮动工具条
CTestToolBar m_wndDockNextBar;//浮动工具条
......//其它代码
}
框架程序中工具条的控制类正常应为CToolBar,可以是自己设计的派生类CtestToolBar(为笔者扩充平面特性等功能后的新工具条控制类名)等,具体根据实际需要而定。利用CDialogBar类和CStyleBar 类还可以建立扩展类型的工具条,详见后面工具条中控制应用技巧,但在该文件头处必须
包含如下命令:
#ifndef __AFXEXT_H__
#include <afxext.h>//直接存取CToolBar和CStatusBar
#endif
②在MainFrm.cpp中完善窗口建立函数
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{ if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
WINDOWPLACEMENT wp;//保存主窗口及工具栏窗口位置状态
if (ReadWindowPlacement(&wp))//读取位置状态信息
SetWindowPlacement(&wp); //设置位置状态信息
m_bToolTips=(AfxGetApp()->GetProfileInt(//读提示功能
_T("General"),_T("ToolTips"),1)!=0); //默认值为1
m_wndMainToolBar.SetState(TOOLLEFT,TRUE);//设置初始状态
EnableDocking(CBRS_ALIGN_ANY);//停靠位置,必须提前位置
if (!m_wndMainToolBar.Create(this,WS_CHILD|WS_VISIBLE
|CBRS_SIZE_DYNAMIC|CBRS_TOP|((m_bToolTips)?
(CBRS_TOOLTIPS|CBRS_FLYBY):0),IDR_MAINFRAME)||
!m_wndMainToolBar.LoadToolBar(IDR_MAINFRAME))
{ //CBRS_SIZE_DYNAMIC为锁定位置风格
TRACE0("主工具条MAINFRAME建立失败/n");
return -1;} // 建立失败处理
......//建立其它工具条代码,基本相同
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
m_wndMainToolBar.SetWindowText(_T("主工具栏"));//设置标题
m_wndMainToolBar.EnableDocking(CBRS_ALIGN_ANY);//停靠位置
//m_wndMainToolBar.ModifyStyle(0,TBSTYLE_FLAT);//平面特性
......//设置其它工具条位置代码,基本相同
DockControlBar(&m_wndMainToolBar,
AFX_IDW_DOCKBAR_TOP);//锁定位置
DockControlBarLeftOf(&m_wndTestToolBar,
&m_wndMainToolBar);//连接工具条
DockControlBar(&m_wndDockToolBar,AFX_IDW_DOCKBAR_RIGHT);
m_wndDockToolBar.SetColumns(AfxGetApp()->GetProfileInt(
_T("General"),_T("Columns"),3));//恢复列格式,默认为3
DockControlBarLeftOf(&m_wndDockNextBar,&m_wndDockToolBar);
m_wndDockNextBar.SetColumns(AfxGetApp()->GetProfileInt(
_T("General"),_T("Columns"),3));
LoadBarState(_T("General"));//恢复保存的状态和位置
return 0;
}
以上建立过程除工具条建立和资源调用函数外,还涉及到了窗口和工具条的状态保存和恢复函数、注册表参数 读取函数、工具条停靠位置函数、工具条标题修改函数、工具条连接函数、工具条列格式控制函数和工具条风格修改函数,其中工具条建立函数中的风格设置很重 要,如果建立的工具条需要重新设置多行多列的排序功能,除正确设置工具条停靠位置参数外,还必须设置CBRS_SIZE_FIXED 风格,即允许程序改变工具条窗口的尺寸,如果工具条不需要重新排序,则必须设置为CBRS_SIZE_DYNAMIC 风格,否则工具栏不但不能进行重新排序和正确停靠到理想的位置,而且也无法正确保存和恢复工具条的位置和状态,这一点应引起编程者高度重视。其余函数以后 分别介绍。
(2)浮动工具条的建立方法
如果要建立浮动工具条,必须使用如下工具条的控制方法:
Cpoint pt(GetSystemMetrics(SM_CXSCREEN)-100,GetSystemMetrics(SM_CYSCREEN)/3);
FloatControlBar(&m_wndPaletteBar,pt);//浮动工具条
(3)多位图工具条的建立方法
如果工具条存在多幅按钮位图,如单色和彩色等,则必须将工具条按钮存在在位图资源文件中而不是工具条资源中,并如下建立:
if(!m_wndDockToolBar.Create(this,WS_CHILD|WS_VISIBLE|
CBRS_SIZE_FIXED|CBRS_TOP|CBRS_TOOLTIPS,ID_PALETTEBAR)||
!m_wndDockToolBar.LoadBitmap(IDR_DOCKTOOLBAR)||
!m_wndDockToolBar.SetButtons(DockTool,
sizeof(DockTool)/sizeof(UINT)))
其中DockTool为按钮IDs数据结构,其定义方法如下:
static UINT BASED_CODE DockTool[]=
{ ID_SEPARATOR,
ID_STYLE_LEFT,
ID_STYLE_CENTERED,
ID_STYLE_RIGHT,
ID_STYLE_JUSTIFIED,
};
上述建立过程中的EnableDocking 函数必须放在所有工具条建立函数之前,否则可能出现很难发现的错误,如特殊工具条初始位置控制等。工具条的所有特性均在上述建立函数中确定,所以其建立过程是实现理想工具条的关键环节。
2、工具条状态保存和恢复
很多应用程序中都具有保存和恢复应用程序及其工具条等状态的功能,即下次启动应用程序后进入上次的运行状态,这种功能只需进行一次界面布局便可永久保存,极大方便用户。
要正确保存和恢复应用程序界面状态,必须对应用程序窗口和工具条窗口等均进行保存和恢复,这需要完善应用程序的建立和关闭过程。具体步骤如下:
(1)首先利用类向导ClassWizard为应用程序增加窗口关闭WM_CLOSE消息处理功能OnClose();
(2)在MainFrm.cpp中为应用程序状态设置成员变量
static TCHAR BASED_CODE szSection[]=_T("Settings");
static TCHAR BASED_CODE szWindowPos[]=_T("WindowPos");
static TCHAR szFormat[]=_T("%u,%u,%d,%d,%d,%d,%d,%d,%d,%d");
(3)编制窗口位置状态读取和写入函数
static BOOL PASCAL NEAR ReadWindowPlacement(LPWINDOWPLACEMENT pwp)
{ //窗口位置状态读取函数,从INI文件中
CString strBuffer=AfxGetApp()->GetProfileString(szSection,szWindowPos);
if (strBuffer.IsEmpty()) return FALSE;
WINDOWPLACEMENT wp;//窗口位置数据结构
int nRead=_stscanf(strBuffer,szFormat,
&wp.flags,&wp.showCmd,//为数据结构读取数值
&wp.ptMinPosition.x,&wp.ptMinPosition.y,
&wp.ptMaxPosition.x,&wp.ptMaxPosition.y,
&wp.rcNormalPosition.left,&wp.rcNormalPosition.top,
&wp.rcNormalPosition.right,&wp.rcNormalPosition.bottom);
if (nRead!=10) return FALSE;
wp.length=sizeof wp;//结构大小
*pwp=wp; //结构指针
return TRUE;
}
static void PASCAL NEAR WriteWindowPlacement(
LPWINDOWPLACEMENT pwp)
{ //窗口位置状态写入函数,写到INI文件
TCHAR szBuffer[sizeof("-32767")*8+sizeof("65535")*2];
wsprintf(szBuffer,szFormat,//将参数值转换为字符串
pwp->flags,pwp->showCmd,
pwp->ptMinPosition.x,pwp->ptMinPosition.y,
pwp->ptMaxPosition.x,pwp->ptMaxPosition.y,
pwp->rcNormalPosition.left,pwp->rcNormalPosition.top,
pwp->rcNormalPosition.right,pwp->rcNormalPosition.bottom);
AfxGetApp()->WriteProfileString(szSection,szWindowPos,szBuffer);
}
(4)在应用程序建立函数OnCreate()中增加状态读取和设置功能
WINDOWPLACEMENT wp;//保存主窗口及工具条窗口位置状态
if (ReadWindowPlacement(&wp))//读取位置状态信息
SetWindowPlacement(&wp); //设置位置状态信息
(5)在应用程序建立函数OnCreate()中增加工具条状态恢复功能
m_wndDockToolBar.SetColumns(AfxGetApp()->GetProfileInt(
_T("General"),_T("Columns"),3));//恢复列格式,默认为3
m_wndDockNextBar.SetColumns(AfxGetApp()->GetProfileInt(
_T("General"),_T("Columns"),3));
LoadBarState(_T("General"));//恢复保存的状态和位置
(6)在应用程序关闭函数OnClose()中完善状态保存功能
void CMainFrame::OnClose()
{ //保存工具条等的状态
SaveBarState(_T("General"));//保存工具条状态
AfxGetApp()->WriteProfileInt(_T("General"),//写入列数
_T("Columns"),m_wndDockToolBar.GetColumns());
AfxGetApp()->WriteProfileInt(_T("General"),
_T("ToolTips"),(m_bToolTips!=0));//写入提示功能
WINDOWPLACEMENT wp;
wp.length=sizeof wp;
if (GetWindowPlacement(&wp)){
wp.flags=0;
if (IsZoomed()) wp.flags|=WPF_RESTORETOMAXIMIZED;
//如果窗口被放大,则保存为最大化状态
WriteWindowPlacement(&wp);
}
CFrameWnd::OnClose();
}
虽然SaveBarState()和LoadBarState()函数保存和恢复了工具条的所有默认位 置状态,但在实际自己实现的功能参数部分并不能被保存,所以应单独编写这些参数的保存代码,如工具栏的排列格式列参数值、颜色状态标志和是否存在动态提示 功能标志等,在实际编程时一定要注意。
3、工具条的平面特性
工具条的平面特性给人耳目一新之感,很多大型应用程序中的工具条都采用这一特性,并取得了巨大成功。利 用VC++5中的COMCTL32.DLL动态链接库可以实现平面式工具条,其主要解决问题包括:由于MFC使用风格控制位来控制工具条的外观,所以在建 立工具条时不能直接设置这种风格,必须在建立后利用SetFlatLookStyle()函数来修改;工具条控制本身也不在各级按钮之间绘制分隔线,其另 一个任务就是截取WM_PAINT消息,并在相应的位置处增加分隔线;工具条控制也不绘制左边的把手(gripper) ,最后的任务就是调整客户区域并绘制并绘制相应的gripper。
显然,实际工作中需要动态链接库COMCTL32.DLL支持的上述方法很不方便。尽管最简便的方法是 利用VC++ 5中的未公开工具栏风格TBSTYLE_FLAT,可以得到工具条的平面特性,只需在工具条建立后简单地增加一条代码 "m_WndMainToolBar.ModifyStyle(0,TBSTYLE_FLAT)",但笔者经试验发现这种方法存在两个严重错误:其一是所 建立的平面工具条在移动时,不能自动清除移动前的按钮图标,使工具条画面杂乱无章;其二是当建立的平面工具条具有浮动特性时,只要鼠标指针移动到浮动工具 条上,整个应用程序窗口就会自动消失。所以第二种方法根本不可行。实现平面工具条的最好方法是在派生类中自己来完成,虽然这一过程比较复杂普通用户很难做 到,但如果存在一个完美的平面工具条控制类,在自己的应用程序中增加相应控制类就是一件很容易的事了。下面是笔者实现完美平面工具条派生类的步骤:
(1)首先利用类向导ClassWizard为工具条控制类派生一个新类CTESTTOOLBAR ,并设置相应的派生类实现文件名。由于新类的基类无法直接选择CTOOLBAR,所以在选择新类的基类时先选择CTOOLBARCTRL为基类,当派生类 生成后再将实现文件中的所有CTOOLBARCTRL类名修改为CTOOLBAR控制类,并利用ClassWizard 为新类增加消息WM_PAINT、WM_NCPAINT、WM_MOUSEMOVE、WM_LBUTTONDOWN和WM_LBUTTONUP消息处理功 能函数,以便实现新类中平面工具条的各种特性。同时,要在MainFrm.cpp中增加包含文件TestToolBar.h。
(2)完善派生类实现文件TestToolBar.h内容
class CTestToolBar : public CToolBar
{......//其它代码
public:
CTestToolBar(); //新类构造函数
UINT GetColumns() { return m_nColumns;};//取得列数
void SetState(UINT nLeft,BOOL nStated);//设置列数和状态
void OnDrawBorder(int index,CDC &dc,int flag);//画边框
void OnEraseBorder(int index,CDC &dc);//删除边框
void OnDrawBorders();//画平面特性
void OnDrawSep(int index,CDC &dc);//画分隔线
void OnDrawGrapper();//画把手
......//其它代码
#ifdef _DEBUG //增加插入控制
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected: //增加成员变量
UINT m_nColumns; //工具栏按钮列数
UINT m_nFlags; //鼠标按键标志
int m_nIndex; //按下的按钮号
int m_nFlagl; //左键按下标志
UINT m_nStated; //工具栏状态
CRect rt; //关闭按钮矩形区域
......//其它代码
}
(3)完善派生类实现文件TestToolBar.cpp内容
......//其它代码
#define TOOLLEFT 18
#define LBUTTONDOWN 1
#define LBUTTONUP 2
......//其它代码
CTestToolBar::CTestToolBar()
{ //在构造函数中初始化变量
m_nColumns=0; //工具栏按钮列数
m_cxLeftBorder=16; //左边界
m_cxRightBorder=3; //右边界
m_cyTopBorder=3; //顶边界
m_cyBottomBorder=3;//底边界
m_nFlags=0; //按键标志成员变量
m_nIndex=0xffff; //按下的按钮号
m_nFlagl=0; //左键按下标志
m_nStated=TRUE; //工具栏状态
}
......//其它代码
#ifdef _DEBUG//插入代码完善
void CTestToolBar::AssertValid() const
{ CToolBar::AssertValid(); }
void CTestToolBar::Dump(CDumpContext& dc) const
{ CToolBar::Dump(dc); }
#endif //_DEBUG
......//其它代码
虽然需要实现的函数比较多,但总起来说不过是取得客户区域或窗口所有区域的文本设备、建立画笔和绘图函数的集合,所以这里只给出了画按钮凸凹边线的函数,其它函数可仿造实现。
void CTestToolBar::OnDrawBorder(int index,CDC &dc,int flag)
{ //画按钮边线flag=0凸=1凹
CRect rect;
GetItemRect(index,&rect);//取得客户区域
rect.right--;rect.bottom--;
CPen *oldpen;
UINT color1,color2;
if (flag==0){//两种状态的颜色处理
color1=COLOR_BTNHILIGHT;//按钮高度颜色
color2=COLOR_BTNSHADOW; //按钮阴影颜色
} else {
color1=COLOR_BTNSHADOW;
color2=COLOR_BTNHILIGHT;
}
CPen pen1(PS_SOLID,1,::GetSysColor(color1));
CPen pen2(PS_SOLID,1,::GetSysColor(color2));
dc.SelectStockObject(NULL_BRUSH);
oldpen=dc.SelectObject(&pen1);
dc.MoveTo(rect.right,rect.top);//画按钮边亮线
dc.LineTo(rect.left,rect.top);
dc.LineTo(rect.left,rect.bottom);
dc.SelectObject(&pen2); //画按钮边暗线
dc.MoveTo(rect.right,rect.top);
dc.LineTo(rect.right,rect.bottom);
dc.LineTo(rect.left,rect.bottom);
//dc.SelectStockObject(BLACK_PEN);//画按钮边黑线
//dc.MoveTo(rect.right+1,rect.top);
//dc.LineTo(rect.right+1,rect.bottom+1);
//dc.LineTo(rect.left,rect.bottom+1);
dc.SelectObject(oldpen);
DeleteObject(pen1);
DeleteObject(pen2);
}
void CTestToolBar::OnDrawBorders()
{ //实现平面工具条
CRect rect;
CPoint pt;
GetCursorPos(&pt); //取得鼠标指针
ScreenToClient(&pt);//变成窗口坐标
int index;
int count=GetCount();//工具条按钮总数
CClientDC dc(this); //窗口客户区域
TBBUTTON button; //按钮数据结构
CToolBarCtrl &ToolBarCtrl=GetToolBarCtrl();
OnDrawGrapper(); //画把手
for(index=0;index<count;index++){
GetItemRect(index,&rect);//取得按钮矩形区域
rect.left++;rect.top++;
ToolBarCtrl.GetButton(index,&button);//取得按钮信息
if(button.fsState&(TBSTATE_CHECKED|TBSTATE_HIDDEN))
continue;
if(button.fsStyle&TBSTYLE_SEP){//画分隔线
if(m_nNew!=0) OnDrawSep(index,dc);
} else if ((m_nIndex==index)||
button.fsState&TBSTATE_PRESSED){//凹按钮
OnEraseBorder(index,dc);//删除按钮边界
if (rect.PtInRect(pt)) OnDrawBorder(index,dc,1);//绘下凹按钮
else OnDrawBorder(index,dc,0);//绘凸出按钮
} else if (!rect.PtInRect(pt)||m_nFlags==LBUTTONUP||
!(button.fsState&TBSTATE_ENABLED)){
OnEraseBorder(index,dc);//删除按钮边界
} else if (m_nFlags!=LBUTTONDOWN){//凸按钮
OnEraseBorder(index,dc);//删除按钮边界
if(m_nFlagl==0)//鼠标按下防止再次重新出现凸起
OnDrawBorder(index,dc,0);//绘按钮边界
}
m_nFlags=0;//按下后移动后不正常凸起
}
ReleaseDC(&dc);
}
void CTestToolBar::OnPaint()
{ //完善重绘按钮功能
CToolBar::OnPaint();
OnDrawBorders();//处理所有按钮边界
}
void CTestToolBar::OnLButtonDown(UINT nFlags, CPoint point)
{ //完善鼠标左键按下功能
m_nFlags=LBUTTONDOWN;//设置鼠标按键标志
m_nFlagl=1;
CToolBar::OnLButtonDown(nFlags,point);//调原函数
int index;
int count=GetCount();//工具栏按钮总数
TBBUTTON button;
CToolBarCtrl &ToolBarCtrl=GetToolBarCtrl();
for(index=0;index<count;index++){
ToolBarCtrl.GetButton(index,&button);//取得按钮信息
if (button.fsState&TBSTATE_PRESSED){ //记录按下按钮号
m_nIndex=index;
}
}
}
void CTestToolBar::OnLButtonUp(UINT nFlags, CPoint point)
{ //完善鼠标释放功能
m_nFlags=LBUTTONUP;//设置鼠标按键标志
m_nFlagl=0;
CToolBar::OnLButtonUp(nFlags, point);//调原函数
CRect rect;
CPoint pt;
GetCursorPos(&pt);//取得光标位置
ScreenToClient(&pt);//变成窗口坐标
CClientDC dc(this);//窗口客户区域
if (m_nIndex!=0xffff){//判断按下按钮执行功能时仍下凹
GetItemRect(m_nIndex,&rect);//取得矩形区域
rect.left++;rect.top++;
OnEraseBorder(m_nIndex,dc);//删除按钮边界
if (rect.PtInRect(pt)) OnDrawBorder(m_nIndex,dc,1);//绘下凹按钮
}
m_nIndex=0xffff;
}
void CTestToolBar::OnMouseMove(UINT nFlags, CPoint point)
{ //完善鼠标移动功能
CToolBar::OnMouseMove(nFlags, point);
int index;
int count=GetCount();//工具栏按钮总数
CRect rect;
if (nFlags&MK_LBUTTON) m_nFlagl=1;//防止再次重新出现凸起
else m_nFlagl=0;
OnDrawBorders();//绘制所有按钮
for(index=0;index<count;index++){//判断鼠标在哪个按钮上
GetItemRect(index,&rect);
rect.left++;rect.top++;
if (rect.PtInRect(point)&&//取得移动过程中输入焦点
!(GetButtonStyle(index)&TBBS_SEPARATOR)){
SetCapture();//设置鼠标输入焦点
return;
}
}
if (nFlags&MK_LBUTTON){//防止移出而失去输入焦点
SetCapture();//设置鼠标输入焦点
m_nFlagl=1;
return;
} else m_nFlagl=0;
ReleaseCapture();
return;
}
void CTestToolBar::OnNcPaint()
{ //背景重画函数
CToolBar::OnNcPaint();
OnDrawGrapper();
}
void CTestToolBar::SetState(UINT nLeft,BOOL nStated)
{ //状态设置函数
m_cxLeftBorder=nLeft;//左边界
m_nStated=nStated; //工具栏状态
}
(4)有关派生类函数几点说明
①画按钮凹凸边线函数OnDrawBorder()
正常工具条中的按钮具有黑色的边线,使按钮凹凸感更强烈,但在平面工具条中的这种按钮并不美观,所以应 省略黑色边线部分,并且必须使用系统的API函数GetSysColor函数来取得边线颜色,以便系统改变颜色时按钮边线也随之改变,同时由于凹凸按钮边 线画法完全相同,只是颜色相反,所以两者完全可由这个函数来实现;
②画分隔线函数OnDrawSep()
画分隔线时应遍历每个按钮,来取得分隔线的位置,并且利用客户区域文本描述表就可实现,只需画亮暗两条线就可实现;
③画把手函数OnDrawGripper()
画把手时应使用整个窗口的文本描述表,因为客户区域描述表不能在窗口的非客户区域画线,而且还必须判断按钮是否以多行多列方式排列,根据不同的排列方式画水平或垂直把手,同时还要实现画关闭按钮功能,以和VC++5 等界面工具栏功能完全相同,另外还要判断工具栏是否为子窗口状态,以确定是否画把手和关闭按钮;
④删除按钮边线函数OnEraseBorder()
函数用于消除系统所绘按钮凹凸边线,使按钮具有平面效果,也必须利用系统的API函数GetSysColor函数来取得系统颜色,以保证系统改变颜色时能够正常消除按钮边线;
⑤实现平面工具栏所有功能函数OnDrawBorders()
在该函数中应特别注意对按钮分隔符判断、按钮凹凸状态判断、鼠标左键按下后按钮凹凸状态判断、删除系统所画按钮边线判断判断和按下鼠标左键并移动鼠标按钮的凹凸状态判断等,并需要利用工具条控制取得对工具栏的引用;
⑥工具条更新功能函数OnPaint()
在这个函数中应注意对原系统更新功能函数的调用,以实现动态提示和按钮图标的显示等功能;
⑦鼠标左键按下功能函数OnLButtonDown()
该函数中除需要调用原系统鼠标左键按下功能函数,以实现消息的发送等功能外,还需要设置鼠标左键按下标志并记录按下按钮的位置,以便程序正确判断按钮的凹凸状态;
⑧鼠标左键释放功能函数OnLButtonDown()
该函数中除需要调用原系统鼠标左键释放功能函数,以实现按钮执行消息的发送等功能外,还需要设置鼠标左键释放标志,以便程序正确判断按钮的凹凸状态,此外还应重绘按钮凹下状态以使按钮功能执行时按钮应处于凹下状态,来保证工具栏按钮与其它高级应用程序实现的功能完全相同;
⑨鼠标移动功能函数OnMouseMove()
该函数中应记录鼠标左键按下状态标志,并在鼠标移动到按钮上和鼠标左键按下时设置鼠标输入焦点,来保证 平面工具条在鼠标移动过程中的正常凸起状态和鼠标点击按钮后对按钮状态的控制,如利用这一点可实现鼠标点击按钮后按钮下凹,不释放鼠标并移动到任何位置时 按钮凸起,重新移动到按钮上按钮仍下凹,这些全是控制鼠标焦点的功能,并及时释放鼠标输入焦点;
⑩背景重绘等函数OnNcPaint()
背景重绘函数是用来防止一个工具条被切换显示状态后,另一个工具栏中的把手和关闭按钮等消失,这在鼠标 反复双击排序后工具条非客户区域时,另一个排序后工具栏就会出现的现象;另外函数SetState()用来设置工具条左边界和状态,以便为画把手和关闭按 钮调整客户区域并提供绘图状态。
此外,还有鼠标移动到把手上光标改变形状和关闭按钮功能,由于篇幅所限这里从略,有兴趣的读者完全可以自己实现。
4、工具条的停靠位置
(1)标准工具条的停靠位置
工具条类CToolBar是控制条类CControlBar 的派生类,其显示的初始停靠位置是通过调用继承的函数CControlBar::EnableDocking(DWORD dwStyle)来确定的,其参数dwStyle用来指定停靠具体位置,与本文有关的风格如下,其余请参阅VC5的联机帮助:
CBRS_ALIGN_TOP 工具条停靠在客户区域顶部
CBRS_ALIGN_BOTTOM 工具条停靠在客户区域底部
CBRS_ALIGN_LEFT 工具条停靠在客户区域左边
CBRS_ALIGN_RIGHT 工具条停靠在客户区域右边
CBRS_ALIGN_ANY 工具条停靠在客户区域任何位置
利用应用程序向导AppWizard 生成的应用程序,其默认的停靠位置为CBRS_ALIGN_ANY,即允许停靠在客户区域的任何边,正常显示时为靠近客户区域的顶部:EnableDocking(CBRS_ALIGN_ANY) ,详见上述的工具栏建立函数ONCREATE()。
应用程序的单文档和多文档的窗口框架类均为CFrameWnd 的派生类,其指定工具条的停靠位置均是通过调用继承的函数 CFrameWnd::EnableDocking(DWORD dwDockStyle)来实现的,其可选的参数除上述五种之外,还增加了CBRS_FLOAT_MULTI参数,这个参数主要是为设计浮动工具条而增加 的,其用来确定一个框架窗口中允许存在多个浮动工具栏。同样利用应用程序向导AppWizard 生成的应用程序,其默认的停靠位置也是CBRS_ALIGN_ANY,即允许停靠在框架窗口的任何边,正常显示时为靠近框架窗口的顶部,即为 EnableDocking(CBRS_ALIGN_ANY),详见上述的工具条建立函数ONCREATE()。
(2)浮动工具条的停靠位置
当一个框架窗口中存在多个浮动工具条时,需要利用函数void DockControlBar(CControlBar *pBar,UINT nDockBarID=0,LPCRECT lpRect= NULL)来确定要控制停靠位置的工具条,它也是CFrameWnd类的成员函数,其中参数pBar用来指向被控制停靠位置的工具条对象,参数 nDockBarID用来确定工具条停靠在框架窗口的哪条边上,取值为:
AFX_IDW_DOCKBAR_TOP 工具条停靠在框架窗口的顶部
AFX_IDW_DOCKBAR_BOTTOM 工具条停靠在框架窗口的底部
AFX_IDW_DOCKBAR_LEFT 工具条停靠在框架窗口的左边
AFX_IDW_DOCKBAR_RIGHT 工具条停靠在框架窗口的右边
如果参数nDockBarID取值为0,则工具条可以停靠在框架窗口中的任何一个可停靠的边上,其默认位置为顶部。
(3)工具条的连接停靠方法
在很多应用程序中都存在将多个工具条同时停靠在某窗口的某一条边上的同一工具条窗口中的情况,利用上述工具条控制函数DockControlBar的lpRect参数,通过控制工具条的停靠矩形区域来实现这个功能,如笔者实现的函数如下:
①在主程序实现文件MainFrm.h中增加函数定义
public:
void DockControlBarLeftOf(CToolBar* Bar,CToolBar* LeftOf);
②在主程序实现文件MainFrm.cpp中增加如下函数
void CMainFrame::DockControlBarLeftOf(
CToolBar* Bar,CToolBar* LeftOf)
{ //设置工具条停靠在同一边窗口中
CRect rect;
DWORD dw;
UINT n;
RecalcLayout();//重新显示
LeftOf->GetWindowRect(&rect);
rect.OffsetRect(1,0);//设置偏移值以停靠在同一窗口中
dw=LeftOf->GetBarStyle();
n=0;
n=(dw&CBRS_ALIGN_TOP)?AFX_IDW_DOCKBAR_TOP:n;
n=(dw&CBRS_ALIGN_BOTTOM&&n==0)?AFX_IDW_DOCKBAR_BOTTOM:n;
n=(dw&CBRS_ALIGN_LEFT&&n==0)?AFX_IDW_DOCKBAR_LEFT:n;
n=(dw&CBRS_ALIGN_RIGHT&&n==0)?AFX_IDW_DOCKBAR_RIGHT:n;
DockControlBar(Bar,n,&rect);
}
在这个函数中应注意对RecalcLayout()函数和OffsetRect()函数的调用,前一个函数用来重新显示被调整的客户区和工具条,后一个函数用来重新确定矩形区域,这相当于用鼠标将第二个工具条拖动到前一个工具条上。
③修改应用程序建立函数OnCreate()函数中的相应DockControlBar()函数为DoctControlBarOf()函数,并正确设置工具条指针,见工具条的建立技巧中的有关函数。
(4)定制工具条的顶部停靠控制
另一种工具条的停靠位置是定制工具条的停靠位置,如具有通用控制功能工具条的停靠位置,这主要实现左侧定制工具条与顶部工具条之间的位置关系。其实现方法如下:
①打开菜单资源增加顶部位置控制菜单项IDD_DLGBARTOP;
②在实现文件MainFrm.h中增加成员控制变量m_bDialogTop;
BOOL m_bDialogTop;
并在构造函数中为其设置初始值;
③利用类向导函数为菜单项设置响应函数;
④在实现文件MainFrm.cpp中完善消息映射函数。
void CMainFrame::OnButtonDlgbartop()
{ //定制工具条顶部位置控制函数
if (m_bDialogTop) m_wndDlgBar.SetWindowPos(
&m_wndStatusBar,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
//其它工具条停靠在最顶部,该工具条停靠其下
else m_wndDlgBar.SetWindowPos(&wndTop,0,0,0,0,
SWP_NOSIZE|SWP_NOMOVE);//停靠在最顶部
RecalcLayout(); //重新显示窗口
m_bDialogTop=!m_bDialogTop;//改变变量标志
}
void CMainFrame::OnUpdateButtonDlgbartop(CCmdUI* pCmdUI)
{ //设置菜单项检查状态
pCmdUI->SetCheck(m_bDialogTop);
}
5、工具条按钮的排序方法
利用应用程序向导AppWizard 生成的应用程序工具条,其按钮均为单行水平排列的,这在实际程序开发时既不美观又不实用,很多大型应用程序等界面中的工具条都采用多行多列的排序方式,要在自己的应用程序中实现这种排列方式,应按下述方法在派生类中控制:
(1)在TestToolBar.h中增加函数定义和成员变量控制
class CTestToolBar : public CToolBar
{......//其它代码
public:
CTestToolBar(); //在构造函数后增加下一行
void SetColumns(UINT nColumns);//增加列控制
......//其它代码
protected: //增加成员变量
UINT m_nColumns; //工具条列按钮数
......//其它代码
}
(2)在TestToolBar.cpp中增加变量初始化和函数
CTestToolBar::CTestToolBar()
{ //在构造函数中初始化变量
m_nColumns=0; //工具条按钮列数
......//其它代码
}
void CTestToolBar::SetColumns(UINT nColumns)
{ //设置按钮排列格式
m_nColumns=nColumns;//列数
int nCount=GetToolBarCtrl().GetButtonCount();//按钮数
for(int i=0;i<nCount;i++){
UINT nStyle=GetButtonStyle(i);//按钮风格
BOOL bWrap=(((i+1)%nColumns)==0);
if(bWrap) nStyle|=TBBS_WRAPPED;//设置换行
else nStyle&=~TBBS_WRAPPED;//不换行
SetButtonStyle(i,nStyle);//设置风格
}
Invalidate();//窗口更新
GetParentFrame()->RecalcLayout();//工具栏状态更新
}
(3)在应用程序建立函数OnCreate()中为相应的工具条增加列控制功能,并注意对保存和恢复工具条状态函数的列控制参数处理,请参阅工具条建立技巧和状态保存与恢复中的有关函数,重新编译并执行应用程序就可以看到多行多列的工具条。
6、工具条的消息映射技巧
如果工具条上的按钮都存在对应的菜单命令,那么可以直接利用类向导ClassWizard 进行命令消息的映射,否则必须通过手工的方法来进行命令按钮的消息映射。由于同一工具栏的命令按钮都存在相类似的功能,所以只要注意将同一工具条中的命令 按钮ID值设置成连续值,就可以利用范围命令处理机制统一对所有按钮命令进行管理,即所有命令按钮的消息可以在同一命令消息处理函数和更新命令消息处理函 数中进行管理,这样不但结构紧凑而且维护特别方便。鉴于类向导ClassWizard 不支持范围命令处理功能,所以其消息映射机制必须通过手工方式来完成。按钮命令消息既可以在框架窗口中映射,也可以在文档和视图中完成映射,由于文档对象 通常用来管理程序中的数据部分,并且在多文档应用程序(MDI) 中,一个文档可能有多个视图与之关联,因此把工具栏中的命令按钮消息映射在文档中比较合适些。其实现步骤如下:
(1)在文档实现文件CToolDoc.h中增加函数定义和数据成员:
class CCTOOLDoc : public CDocument
{......//其它代码
protected:
UINT iPosition; //命令按钮消息位置
......//其它代码
protected:
//{{AFX_MSG(CCTOOLDoc)
afx_msg void OnTool(UINT nID);//命令消息映射
afx_msg void OnUpdateTool(CCmdUI* pCmdUI);//更新消息映射
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
......//其它代码
}
(2)在文档实现文件CToolDoc.cpp中进行消息映射
BEGIN_MESSAGE_MAP(CCTOOLDoc,CDocument)
//{{AFX_MSG_MAP(CCTOOLDoc)
ON_COMMAND_RANGE(ID_BUTTON_LINE,
ID_BUTTON_SORT,OnTool);
ON_UPDATE_COMMAND_UI_RANGE(ID_BUTTON_LINE,
ID_BUTTON_SORT,OnUpdateTool);
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
函数中的参数ID_BUTTONLINE和ID_BUTTONSORT分别为工具栏中第一个按钮和最后一个按钮的消息,如果要增加或减少了按钮数,则必须使其ID值保持连续,并且需要修改函数中的最后一个消息值。对应函数的代码如下:
void CtoolDoc::OnTool(UINT nID)
{ //按钮消息处理函数
iPosition=nID-ID_BUTTON_LINE;
switch(iPosition){
case 1:
......//调用按钮一的处理功能
case 2:
......//调用按钮二的处理功能
......
case n:
......//调用按钮N的处理功能
break;
}
}
void CToolDoc::OnUpdateTool(CCmdUI* pCmdUI)
{ //更新按钮函数
pCmdUI->SetCheck(iPosition==(pCmdUI->m_nID
-ID_BUTTON_LINE));
}
由于命令按钮比较多,其处理功能代码部分这里省略。
7、驾驭工具条按钮的状态显示更新
(1)互斥按钮的状态更新
很多应用程序中都存在相互排斥的工具条按钮,如开始和停止按钮,初始状态时停止按钮被禁止,当应用程序进行数据通讯、查找和打印等功能时,停止按钮有效而开始按钮禁止,当命令结束或按停止按钮后,开始按钮有效而停止按钮被禁止。这就是工具条中按钮的显示状态更新问题。
与菜单栏一样,工具条的构造、析构过程及其窗口的创建都是由应用程序的主框架窗口来管理的。当用户点击 菜单条的菜单项时,Windows 就会向应用程序发送WM_INITMENUPOPUP消息,以便应用程序在菜单显示之前对菜单项进行添加或修改,当利用MFC 类库编程时只要针对某一具体的菜单项进行消息映射,加入相应的消息处理函数,MFC类库就会发送更新消息UPDATE_COMMAND_UI,使菜单在弹 出时消息处理函数能够对菜单项进行禁止、变灰和其它处理。工具条的消息处理过程与菜单栏完全相同,它也存在工具条显示更新消息 UPDATE_COMMAND_UI ,只不是由于工具条总是处于显示状态,所以对其消息处理函数的调用是在应用程序的空闲状态时进行处理的,这点与菜单栏有所不同,当应用程序命令按钮所要执 行的功能占用大量的处理器资源时,如进行通讯处理或大量数据计算时,应用程序的主框架窗口就无暇进入空闲状态,于是工具条中的命令按钮函数就不能被及时调 用,按钮的显示状态就不能被及时更新,这就为工具条按钮的显示状态及时更新带来一定的困难。
基于文档的应用程序一般情况下都是在文档视中对工具条按钮进行处理,所以要实现按钮显示状态的及时更新,必须在命令按钮响应函数中利用CToolBarCtrl类的成员函数EnableButton()函数对按钮的显示状态进行必要的处理,其步骤如下:
①首先利用类向导ClassWizard 对CCTOOLView类进行开始按钮和停止按钮的COMMAND命令消息映射,增加对应消息的处理函数;
②在实现文件CTOOLView.h中增加成员变量:
public:
BOOL m_bContiune;//开始按钮标志
BOOL m_bSet;//结束按钮标志
③在实现文件CTOOLView.cpp中增加代码完善按钮状态更新:
......//其它代码
void DrawRectText(RECT &rc,CDC &dc);
......//其它代码
void CCTOOLView::OnButtonStart()
{ //完善开始按钮函数功能
CToolBar *pToolBar=(CToolBar *)//取得工具栏对象指针
(GetParentFrame()->GetControlBar(IDR_DOCKTOOLBAR));
//取得工具条控制指针
CToolBarCtrl *pToolBarCtrl=&(pToolBar->GetToolBarCtrl());
//控制工具条按钮状态
pToolBarCtrl->EnableButton(ID_BUTTON_START,FALSE);
pToolBarCtrl->EnableButton(ID_BUTTON_STOP,TRUE);
CClientDC dc(this);//取得设备文本
MSG msg;//定义消息
RECT rc;
GetClientRect(&rc);//以下画网格并显示
m_bContinue=TRUE;
m_bSet=FALSE;
for(int ii=0;ii<2;ii++){
if(m_bContinue==TRUE){
ii=0;//状态改变退出
DrawRectText(rc,dc);
GetMessage(&msg,NULL,0,0);//检查消息
TranslateMessage(&msg);//解释消息
DispatchMessage(&msg);//分配消息
}
}
ReleaseDC(&dc);
m_bSet=TRUE;
}
void DrawRectText(RECT &rc,CDC &dc)
{ //显示文本函数
int i,j,k,nDist=10;
CPen pen,*pOldPen;
pen.CreatePen(PS_SOLID,1,RGB(180,0,0));
pOldPen=dc.SelectObject(&pen);//绘制背景网格
j=rc.right/nDist+1;
k=rc.bottom/nDist+1;
for(i=0;i<j+k;i++){
dc.MoveTo(i*nDist,0);
dc.LineTo(0,i*nDist);
if(i<j){
dc.MoveTo(i*nDist,0);
dc.LineTo(rc.right,(j-i)*nDist);
} else {
dc.MoveTo(0,(i-j)*nDist);
dc.LineTo(rc.right,i*nDist);
}
}
dc.SelectObject(pOldPen);
DeleteObject(&pen);
CFont font,*pOldFont;
font.CreateFont(50,50,0,0,1000,//创建字体
1,1,0,ANSI_CHARSET,
OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,DEFAULT_PITCH,NULL);
pOldFont=dc.SelectObject(&font);
dc.TextOut(10,10,"BUTTON");
dc.SelectObject(pOldFont);
DeleteObject(&font);
}
void CCTOOLView::OnButtonStop()
{ //完善停止按钮函数
m_bContinue=FALSE;
CToolBar *pToolBar=(CToolBar *)//取得工具栏对象指针
(GetParentFrame()->GetControlBar(IDR_DOCKTOOLBAR));
//取得工具条控制指针
CToolBarCtrl *pToolBarCtrl=&(pToolBar->GetToolBarCtrl());
//控制工具条按钮状态
pToolBarCtrl->EnableButton(ID_BUTTON_START,TRUE);
pToolBarCtrl->EnableButton(ID_BUTTON_STOP,FALSE);
for(int jj=0;jj<2;jj++){
if(m_bSet==FALSE){
jj=0;
GetMessage(&msg1,NULL,0,0);//检查消息
TranslateMessage(&msg1);//解释消息
DispatchMessage(&msg1);//分配消息
}
}
}
在开始按钮函数中,当在屏幕上不断显示信息的同时必须利用消息处理函数检查消息队列,并解释和分配相应 的消息,否则其它按钮就无法检查到应用程序的鼠标等消息,无法实现显示状态更新;在更新按钮显示状态前,必须先取得工具栏对象和相应的控制指针,然后才能 实现对按钮状态的控制,并且要实现按钮状态的更新,其设置按钮的更新功能函数必须最后执行结束才能有效,即在停止按钮函数中必须判断标志并检测消息队列。
④在平面工具条派生类中的鼠标按下函数中增加代码,以实现鼠标按下开始和停止按钮后,保证按钮的正常按下和显示状态:
void CTestToolBar::OnLButtonDown(UINT nFlags, CPoint point)
{ //完善鼠标按下函数
......//其它代码
if(m_nIndex==ToolBarCtrl.CommandToIndex(ID_BUTTON_STOP)){
ToolBarCtrl.SetState(ID_BUTTON_STOP,ToolBarCtrl.GetState(
ID_BUTTON_STOP)&(~TBSTATE_ENABLED&~TBSTATE_PRESSED));
ToolBarCtrl.SetState(ID_BUTTON_START,ToolBarCtrl.
GetState(ID_BUTTON_START)|TBSTATE_ENABLED);
ToolBarCtrl.Invalidate();
AfxGetApp()->OnIdle(-1);
m_nIndex=0xffff;
}
if(m_nIndex==ToolBarCtrl.CommandToIndex(ID_BUTTON_START)){
ToolBarCtrl.SetState(ID_BUTTON_START,ToolBarCtrl.GetState(
ID_BUTTON_START)&(~TBSTATE_ENABLED & ~TBSTATE_PRESSED));
ToolBarCtrl.SetState(ID_BUTTON_STOP,ToolBarCtrl.GetState(
ID_BUTTON_STOP)&TBSTATE_ENABLED);
ToolBarCtrl.Invalidate();
AfxGetApp()->OnIdle(-1);
m_nIndex=0xffff;
}
}
(2)按钮按下状态的切换更新
另一种按钮状态的更新情况是按钮按下状态的切换更新,这在绘图工具条和屏幕字体效果工具条中最常见。其实现步骤如下:
①首先在MainFrm.cpp中手工增加消息范围映射函数
ON_COMMAND_RANGE(ID_EDIT_TOGGLE,ID_EDIT_UNINDENT,OnButton)
ON_UPDATE_COMMAND_UI_RANGE(ID_EDIT_TOGGLE,
ID_EDIT_UNINDENT,OnUpdateButton)
②然后在MainFrm.h中消息映射的最后增加成员函数定义和记录按下按钮序号的成员控制变量
int m_bIndex;//按下按钮序号
afx_msg void OnButton(UINT nID);
afx_msg void OnUpdateButton(CCmdUI* pCmdUI);
③最后分别增加实现消息函数的功能代码
void CMainFrame::OnUpdateButton(CCmdUI* pCmdUI)
{ //按钮按下检查状态设置函数
pCmdUI->SetCheck((UINT)(ID_EDIT_TOGGLE+
m_bIndex) == pCmdUI->m_nID);
}
void CMainFrame::OnButton(UINT nID)
{ //记录按下按钮的序号
m_bIndex=(int)(nID-ID_EDIT_TOGGLE);
}
如果涉及到其它控制的变化,可在这两个函数中分别增加。
(3)按钮的部分显示更新
最后一种更新工具条按钮显示状态的情况是部分按钮显示,这需要利用SetButtons()和SetButtonInfo()函数有效处理。步骤如下:
①首先在菜单栏中增加控制菜单项;
②在应用程序实现文件CTool.h中增加如下消息定义和函数定义
......//其它代码
#define WM_IDLEACTIVATE WM_USER+1
......//其它代码
class CCtrlbarsApp : public CWinApp
{
public:
......//其它代码
virtual BOOL OnIdle(LONG lCount);
......//其它代码
};
③在应用程序实现文件CTool. cpp中增加如下代码和副本函数功能,这个函数在工具条按钮的各种显示状态更新中都非常有用,是一个非常重要的程序设计技巧。
在初始化函数CCTOOLApp::InitInstance()最后增加代码:
m_pMainWnd = pMainFrame;
完善相应的功能函数:
BOOL CCTOOLApp::OnIdle(LONG lCount)
{ //保持应用程序一个副本函数
if(lCount == 0){
ASSERT(m_pMainWnd != NULL);
for(HWND hWnd=::GetWindow(m_pMainWnd->m_hWnd,
GW_HWNDFIRST);hWnd!=NULL;hWnd=::GetNextWindow(
hWnd,GW_HWNDNEXT)){//获取主程序子窗口
if(::GetParent(hWnd)==m_pMainWnd->m_hWnd){
if(GetActiveWindow()==hWnd&&(::GetCapture()==NULL))
m_pMainWnd->SetActiveWindow();//保持应用程序活动窗口
SendMessage(hWnd,WM_IDLEUPDATECMDUI,(WPARAM)TRUE,0L);
}//保持窗口的一个有效副本
}
}
return CWinApp::OnIdle(lCount);
}
④最后在菜单项处理函数中进行工具条按钮的重组处理,并在需要处理应用程序窗口副本的任何函数中增加副本处理函数。
void CMainFrame::OnViewShort()
{
m_wndMainToolBar.SetButtons(NULL,7);//共计7个
m_wndMainToolBar.SetButtonInfo(0,ID_BUTTON_LINE,TBBS_BUTTON,1);
m_wndMainToolBar.SetButtonInfo(1,ID_BUTTON_CURSE,TBBS_BUTTON,5);
......
m_wndMainToolBar.SetButtonInfo(6,ID_BUTTON_TEST,TBBS_BUTTON,7);
m_wndToolBar.Invalidate();//更新工具条
AfxGetApp()->OnIdle(-1);//窗口副本处理函数
}
8、工具条中的通用控制使用
在工具条中通常不都是按钮,如存在输入框、下拉组合框和检查盒按钮等。这就需要利用VC中提供的特殊类型的工具条类控制或通过派生新类的方法来实现。
(1)在派生的新类中实现控制
如果利用派生新类的方法,很容易就可以实现文本输入框等通用控制功能。其实现步骤如下:
①利用类向导ClassWizard 建立新类CEDITBAR并生成实现文件;
②利用资源编辑器建立工具条的位图文件;
③在新类实现文件EDITBAR.h中增加如下函数定义和成员变量:
class CEDITBAR : public CToolBar
{
public:
CEDITBAR();
CSearchBox m_SearchBox;//文本查找控制
BOOL Init(CWnd* pParentWnd,BOOL bToolTips);
BOOL SetColor(BOOL bColor);
BOOL SetHorizontal();
BOOL SetVertical();
......//其它代码
BOOL m_bVertical;
//virtual CSize CalcFixedLayout(BOOL bStretch,BOOL bHorz);
virtual CSize CalcDynamicLayout(int nLength,DWORD dwMode);
......//其它代码
}
④在新类实现文件EDITBAR.cpp中增加如下函数:
......//其它代码
#define COMBOBOX_INDEX 5 //定义组合框参数
#define COMBOBOX_WIDTH 150
#define COMBOBOX_HEIGHT 150
#define COMBOBOX_BITMAP 4
static UINT BASED_CODE EditButtons[] =
{ //要求与工具栏位图顺序对应
ID_EDIT_TOGGLE,
ID_EDIT_NEXT,
ID_EDIT_PREV,
ID_EDIT_CLEARALL,
ID_SEPARATOR,
ID_EDIT_FINDEDIT,
ID_SEPARATOR,
ID_EDIT_FINDINFILES,
ID_SEPARATOR,
ID_EDIT_INDENT,
ID_EDIT_UNINDENT,
ID_SEPARATOR,
ID_WINDOW_NEW,
ID_WINDOW_SPLIT,
ID_WINDOW_CASCADE,
ID_WINDOW_HORZ,
ID_WINDOW_VERT,
};
......//其它代码
BOOL CEDITBAR::Init(CWnd* pParentWnd,BOOL bToolTips)
{ //初始化输入框函数
m_bVertical=FALSE;
DWORD dwStyle=WS_CHILD|WS_VISIBLE|CBRS_TOP|CBRS_SIZE_DYNAMIC;
if(bToolTips)dwStyle|=(CBRS_TOOLTIPS|CBRS_FLYBY);
if(!Create(pParentWnd,dwStyle,IDB_CEDIT)) return FALSE;
if(!LoadBitmap(IDB_CEDIT)) return FALSE;
if(!SetButtons(EditButtons,sizeof(EditButtons)
/sizeof(UINT)))return FALSE;
CRect rect(-COMBOBOX_WIDTH,-COMBOBOX_HEIGHT,0,0);
// 非常重要:其一是可以从控制接收信息,如动态提示等
if(!m_SearchBox.Create(WS_CHILD|CBS_DROPDOWN|//建立文本查找
CBS_AUTOHSCROLL|WS_VSCROLL|CBS_HASSTRINGS,rect,this,
IDC_EDITBAR_SEARCHBOX)) return FALSE;
HFONT hFont=(HFONT)GetStockObject(DEFAULT_GUI_FONT);
if(hFont==NULL)hFont=(HFONT)GetStockObject(ANSI_VAR_FONT);
m_SearchBox.SendMessage(WM_SETFONT,(WPARAM)hFont);
if(!SetHorizontal())return FALSE;
return TRUE;
}
BOOL CEDITBAR::SetHorizontal()
{ //设置按钮水平排列函数
m_bVertical=FALSE;
SetBarStyle(GetBarStyle()|CBRS_ALIGN_TOP);
SetButtonInfo(COMBOBOX_INDEX,IDC_EDITBAR_SEARCHBOX,
TBBS_SEPARATOR, COMBOBOX_WIDTH);
if(m_SearchBox.m_hWnd!=NULL){
CRect rect;
GetItemRect(COMBOBOX_INDEX,rect);
m_SearchBox.SetWindowPos(NULL,rect.left,
rect.top,0,0,SWP_NOZORDER|SWP_NOACTIVATE|
SWP_NOSIZE|SWP_NOCOPYBITS);
m_SearchBox.ShowWindow(SW_SHOW);
}
return TRUE;
}
BOOL CEDITBAR::SetVertical()
{ //设置按钮垂直排列函数
m_bVertical=TRUE;
SetButtonInfo(COMBOBOX_INDEX,ID_EDIT_FIND,
TBBS_BUTTON, COMBOBOX_BITMAP);
if(m_SearchBox.m_hWnd != NULL)
m_SearchBox.ShowWindow(SW_HIDE);
return TRUE;
}
CSize CEDITBAR::CalcDynamicLayout(int nLength, DWORD dwMode)
{ //设置固定大小工具条函数
if (dwMode&LM_COMMIT) {
if (dwMode&LM_VERTDOCK)
if(!m_bVertical) SetVertical();
else if(m_bVertical) SetHorizontal();
return CToolBar::CalcDynamicLayout(nLength,dwMode);
} else {
BOOL bOld=m_bVertical;
BOOL bSwitch=(dwMode & LM_HORZ)?bOld:!bOld;
if (bSwitch){
if (bOld) SetHorizontal();
else SetVertical();}
CSize sizeResult=CToolBar::CalcDynamicLayout(nLength,dwMode);
if (bSwitch){
if (bOld) SetHorizontal();
else SetVertical();}
return sizeResult;
}
}
/*
CSize CEDITBAR::CalcFixedLayout(BOOL bStretch,BOOL bHorz)
{ //设置可变大小工具条函数
if(!bHorz&&(m_dwStyle&CBRS_SIZE_DYNAMIC)&&
!(m_dwStyle&CBRS_FLOATING)){
CSize size;
size=CControlBar::CalcFixedLayout(bStretch,bHorz);
CRect rect;
rect.SetRectEmpty();
CalcInsideRect(rect, bHorz);
size.cx=m_sizeVert.cx+(m_rectInsideVert.Width()-rect.Width());
size.cy=m_sizeVert.cy+(m_rectInsideVert.Height()-rect.Height());
return size;
} else {
if(m_bVertical) SetHorizontal();
return CToolBar::CalcFixedLayout(bStretch,bHorz);
}
}
*/
⑤利用类向导ClassWizard建立新类CSearchBox并生成实现文件,同时为新类增加解释消息,即在新类实现文件SearchBox.h中增加如下函数定义:
virtual BOOL PreTranslateMessage(MSG* pMsg);
⑥在其实现文件SearchBox.cpp文件中完善相应函数
BOOL CSearchBox::PreTranslateMessage(MSG* pMsg)
{ //消息解释函数
if((pMsg->message!=WM_KEYDOWN)||(pMsg->wParam!=VK_RETURN))
return CComboBox::PreTranslateMessage(pMsg);
// 在下拉列表中增加最后15次输入的数据
if((pMsg->lParam&0x40000000)==0){//不重复
CString strText;
GetWindowText(strText);
InsertString(0,strText);
SetCurSel(0);
if(GetCount()>15)
DeleteString(GetCount()-1);
}
return TRUE;
}
⑦在主程序实现文件MainFrm.h中增加工具条定义
#include "EDITBAR.H"
CEDITBAR m_wndEditBar;//带输入框工具条
⑧在主程序实现文件MainFrm.cpp中增加代码:
//在建立函数OnCreate中增加
if (!m_wndEditBar.Init(this,m_bToolTips)){
TRACE0("带编输入工具条建立失败/n");
return -1;}
......
DockControlBarLeftOf(&m_wndEditBar,
&m_wndTestToolBar);//连接工具条
此外,还可以为工具条增加按钮检查状态等控制功能,详见工具条按钮的显示状态更新。
(2)利用工具条风格类实现控制
利用工具条风格类同样可以实现下拉列表等控制功能,其具体步骤如下:
①为工具栏建立位图按钮并定义按钮对应的消息,同前;
②在实现文件MainFrm.h中增加工具栏控制和函数定义;
CStyleBar m_wndStyleBar;
BOOL CreateStyleBar()
③在实现文件MainFrm.cpp中增加控制代码
在建立函数OnCreate中增加建立函数
if (!CreateStyleBar()) return -1;
增加建立相应工具条函数的代码
BOOL CMainFrame::CreateStyleBar()
{ //建立具有下拉列表控制的工具条函数
const int nDropHeight = 100;
if(!m_wndStyleBar.Create(this,WS_CHILD|WS_VISIBLE|
CBRS_TOP|CBRS_TOOLTIPS|CBRS_FLYBY,IDW_STYLES)||
!m_wndStyleBar.LoadBitmap(IDB_STYLES)||!m_wndStyleBar.
SetButtons(styles,sizeof(styles)/sizeof(UINT))){
TRACE0("特殊风格工具条建立失败/n");
return FALSE;
}
// 建立组合框
m_wndStyleBar.SetButtonInfo(0,IDW_COMBO,TBBS_SEPARATOR,100);
// 设置组合框与按钮间距离
m_wndStyleBar.SetButtonInfo(1,ID_SEPARATOR,TBBS_SEPARATOR,12);
CRect rect;
m_wndStyleBar.GetItemRect(0,&rect);
rect.top=3;
rect.bottom=rect.top+nDropHeight;
if (!m_wndStyleBar.m_comboBox.Create(//建立组合框
CBS_DROPDOWNLIST|WS_VISIBLE|WS_TABSTOP,
rect, &m_wndStyleBar, IDW_COMBO)){
TRACE0("组合框建立失败/n");
return FALSE;
}
CString szStyle;//填充组合框
if (szStyle.LoadString(IDS_LEFT))
m_wndStyleBar.m_comboBox.AddString((LPCTSTR)szStyle);
......//同类的其它代码
LOGFONT logFont;//为组合框建立字体
memset(&logFont,0,sizeof(logFont));
if(!::GetSystemMetrics(SM_DBCSENABLED)){
logFont.lfHeight=-12;
logFont.lfWeight=FW_BOLD;
logFont.lfPitchAndFamily=VARIABLE_PITCH|FF_SWISS;
CString strDefaultFont;
strDefaultFont.LoadString(IDS_DEFAULT_FONT);
lstrcpy(logFont.lfFaceName, strDefaultFont);
if(!m_wndStyleBar.m_font.CreateFontIndirect(&logFont))
TRACE0("组合框建立字体失败/n");
else m_wndStyleBar.m_comboBox.SetFont(&m_wndStyleBar.m_font);
} else {
m_wndStyleBar.m_font.Attach(::GetStockObject(SYSTEM_FONT));
m_wndStyleBar.m_comboBox.SetFont(&m_wndStyleBar.m_font);
}
return TRUE;
}
同样,也可以为这个特殊风格的工具条增加其它控制功能。
(3)利用对话框类建立多功能工具条
利用对话框工具条类可以建立功能更加强大的工具条,在对话框中实现的功能都可以增加到工具条中去。工具步骤如下:
①利用对话框资源增加一个对话框IDD_DLGTOOLBAR,并设计好对话框中的所有功能及功能对应的ID标识;
②在实现文件MainFrm.h中增加对话框工具条类控制和成员变量
CDialogBar m_wndDlgBar; //对话框工具条控制
BOOL m_bDialogTop;//顶部标志
③在实现文件MainFrm.cpp中增加控制代码
在建立函数OnCreate函数的状态条建立函数后增加:
UINT nID, nStyle;
int cxWidth;
m_wndStatusBar.GetPaneInfo(0,nID,nStyle,cxWidth);
m_wndStatusBar.SetPaneInfo(0,nID,SBPS_STRETCH|SBPS_NORMAL,cxWidth);
if(!m_wndDlgBar.Create(this,IDD_TOOLBARCTRL,
CBRS_LEFT|CBRS_TOOLTIPS|CBRS_FLYBY,IDD_TOOLBARCTRL)){
TRACE0("对话框工具条建立失败/n");
return -1;
}
一定要注意EnableDocking(CBRS_ALIGN_ANY)函数在所有建立函数的前面,并且对话框工具条在状态条建立函数的后面。
④为对话框资源中的所有控制消息手工增加映射,并完善相应的映射函数,可以是工具条的显示与隐藏等功能。示例函数如下:
BOOL CMainFrame::OnDocktool(UINT nID)
{ //控制工具条的显示与隐藏函数
BOOL bVisible = ((m_wndDockToolBar.GetStyle() & WS_VISIBLE) != 0);
ShowControlBar(&m_wndDockToolBar, !bVisible, FALSE);
RecalcLayout();
return TRUE;
}
void CMainFrame::OnUpdateDocktool(CCmdUI* pCmdUI)
{ //工具条检查状态更新
BOOL bVisible = ((m_wndDockToolBar.GetStyle() & WS_VISIBLE) != 0);
pCmdUI->SetCheck(bVisible);
}
⑤在菜单中增加顶部控制菜单项,建立消息函数后按照工具条的停靠位置中的方法为其完善相应的顶部控制功能函数。
9、工具条的显示和隐藏
(1)单一工具条的显示与隐藏
在大型应用程序中可能存在很多工具条,而用户实际使用时可能只需要其中的一部分,这就需要控制工具条的显示隐藏。对于单个工具条,其菜单项的一般控制方法为:
首先打开菜单编辑器,在查看菜单栏中增加工具条对应的子菜单项,如为TestToolBar 工具条增加的子菜单项的“测试”的ID为ID_BOTTON_TEST,其Caption 为“测试工具条”,依次增加其它工具条的控制子菜单项。利用类向导ClassWizard 为主框架窗口类CMainFrame增加各子菜单项的ONCOMMAND和ON_UPDATE_COMMAND_UI 消息映射功能函数,并完善对应的功能函数:
void CMainFrame::OnButtonTest()
{ //菜单命令消息函数
BOOL bVisible=((m_wndTestToolBar.GetStyle()&
WS_VISIBLE)!=0);//取得工具条状态
ShowControlBar(&m_wndTestToolBar,!bVisible,FALSE);
RecalcLayout();//更新工具条
}
void CMainFrame::OnUpdateButtonTest(CCmdUI* pCmdUI)
{ //菜单更新函数
BOOL bVisible=((m_wndTestToolBar.GetStyle()&
WS_VISIBLE)!=0);//取得工具条状态
pCmdUI->SetCheck(bVisible);//设置检查标志
}
(2)特殊工具条的显示与隐藏
当存在利用CStyleBar 类等建立的具有特殊控制功能的工具条时,工具条的显示与消隐必须利用下面的函数:
BOOL CMainFrame::OnEdittool(UINT nID)
{ //具有下拉组合框功能工具条显示与消隐函数
CWnd* pBar;//工具条窗口指针
if ((pBar=GetDlgItem(nID))==NULL)//取得鼠标点击项
return FALSE;//控制工具条窗口风格
pBar->ShowWindow((pBar->GetStyle()&WS_VISIBLE)==0);
RecalcLayout();//重新显示窗口
return TRUE;
}
void CMainFrame::OnUpdateEdittool(CCmdUI* pCmdUI)
{ //具有下拉组合框功能工具条状态更新
CWnd* pBar;//工具条窗口指针
if((pBar=GetDlgItem(pCmdUI->m_nID))==NULL){
pCmdUI->ContinueRouting();//继续判断
return;
} //设置检查状态
pCmdUI->SetCheck((pBar->GetStyle()&WS_VISIBLE)!=0);
}
(3)多工具条的显示与隐藏
如果存在很多工具条,使用菜单控制的方法并不方便,最好采用对话框设置的方法,如笔者在程序中通过对话框来控制工具条的显示与消隐、按钮排列数和动态提示功能的取舍。其步骤如下:
①首先打开资源列表中的对话框资源,利用"Insert->Resource-> Dialog"插入对话框IDD_TOOLBAR,在对话框中建立四个工具条复选框按钮、四个排列数值的单选按钮和两个动态提示功能的单选按钮(请有关图 形),并为各按钮设置ID标识和按钮名称,注意选中每组按钮中首按钮的Group属性;
②通过双击增加的对话框标题栏为对话框建立新类CToolDlg和两个实现文件ToolDlg.h和ToolDlg.cpp;
③打开对话框实现文件ToolDlg.h,增加如下成员控制变量:
class CToolDlg : public CDialog
{ ......//其它代码
//{{AFX_DATA(CToolDlg)
enum { IDD = IDD_TOOLBAR };
BOOL m_bMainTool;//各工具条控制变量
BOOL m_bTestTool;
BOOL m_bDockTool;
BOOL m_bDockNext;
int m_nToolTips;//动态提示控制变量
int m_nColumns; //按钮排列数控制变量
......//其它代码
}
④在实现文件ToolDlg.cpp中增加如下控制代码:
CToolDlg::CToolDlg(CWnd* pParent /*=NULL*/)
: CDialog(CToolDlg::IDD, pParent)
{ //完善成员变量初始化
//{{AFX_DATA_INIT(CToolDlg)
m_bMainTool = FALSE;
m_bTestTool = FALSE;
m_bDockTool = FALSE;
m_bDockNext = FALSE;
m_nToolTips = -1;
m_nColumns = -1;
//}}AFX_DATA_INIT
}
void CToolDlg::DoDataExchange(CDataExchange* pDX)
{ //完善对话框按钮状态初始化
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CToolDlg)
DDX_Check(pDX,IDC_MAINTOOL,m_bMainTool);
DDX_Check(pDX,IDC_TESTTOOL,m_bTestTool);
DDX_Check(pDX,IDC_DOCKTOOL,m_bDockTool);
DDX_Check(pDX,IDC_DOCKNEXT,m_bDockNext);
DDX_Radio(pDX,IDC_SHOW,m_nToolTips);
DDX_Radio(pDX,IDC_TWO,m_nColumns);
//}}AFX_DATA_MAP
}
⑤在主程序实现文件MainFrm.cpp 中增加ToolDlg.h 包含文件,并完善相应菜单选项功能。如笔者程序中为完善“测试”选项的功能,其控制代码如下:
void CMainFrame::OnButtonTest()
{ //完善相应菜单选项
CToolDlg ToolDlg;//调出对话框
UINT nColumns = m_wndDockToolBar.GetColumns();
nColumns -= 2;//取得按钮排列数
//取得工具栏的可视风格
ToolDlg.m_bMainTool=((m_wndMainToolBar.
GetStyle()&WS_VISIBLE)!=0);
ToolDlg.m_bTestTool=((m_wndTestToolBar.
GetStyle()&WS_VISIBLE)!=0);
ToolDlg.m_bDockTool=((m_wndDockToolBar.
GetStyle()&WS_VISIBLE)!=0);
ToolDlg.m_bDockNext=((m_wndDockNextBar.
GetStyle()&WS_VISIBLE)!=0);
ToolDlg.m_nColumns=nColumns;
ToolDlg.m_nToolTips=(m_bToolTips)?0:1;
if (IDOK==ToolDlg.DoModal())
{ //控制工具条的显示与消隐
ShowControlBar(&m_wndMainToolBar,
ToolDlg.m_bMainTool,FALSE);
......//其它工具条控制代码,基本相同
m_bToolTips=(ToolDlg.m_nToolTips==0);
if (m_bToolTips){//控制动态提示功能
m_wndMainToolBar.SetBarStyle(m_wndMainToolBar.
GetBarStyle()|CBRS_TOOLTIPS|CBRS_FLYBY);
......//其它工具条控制代码,基本相同
} else {
m_wndMainToolBar.SetBarStyle(m_wndMainToolBar.
GetBarStyle()&(CBRS_TOOLTIPS|CBRS_FLYBY));
......//其它工具条控制代码,基本相同
}
nColumns=ToolDlg.m_nColumns+2;//控制工具条按钮列数
m_wndDockToolBar.SetColumns(nColumns);
m_wndDockNextBar.SetColumns(nColumns);
m_wndMainToolBar.Invalidate();//控制各工具条显示更新
......//其它工具条控制代码,基本相同
}
}
10、工具条的属性控制
WIN95/98中的鼠标右键弹出属性表功能具有很强的功能特色,很多应用程序中都对各种功能进行了属 性控制设置,给应用程序中的工具条增加属性控制功能更具有现实意义。在工具条的属性表中可以设置工具栏的消隐与显示、工具条按钮的排序和其它各种控制功 能,这要根据应用程序的需要来确定。这里以控制工具条的显示与消隐为例,详细阐述其实现的具体步骤:
(1)首先打开资源列表中的菜单资源,利用"Insert->Resource-> Menu"增加菜单资源IDR_TOOLBAR,在菜单中建立四个工具条菜单选项,并为各菜单选项设置ID标识和菜单项名,注意将各菜单选项标识符设置为 与工具条名称完全相同的控制符,如本例程序中主控工具条ID为IDR_wndMainToolBar,那么控制主控工具条的菜单选项ID标识符也必须是 IDR_wndMainToolBar,这样设置后可以使用MFC 类库中的提供的功能来控制工具条的显示与消隐、菜单项检查状态与非检查状态。
(2)然后利用类向导ClassWizard 为应用程序主框架窗口增加解释消息PreTranslateMessage处理函数;
(3)在应用程序实现文件MainFrm.cpp中完善解释消息函数:
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
{ if (pMsg->message==WM_RBUTTONDOWN){
CWnd* pWnd=CWnd::FromHandlePermanent(pMsg->hwnd);
CControlBar* pBar=DYNAMIC_DOWNCAST(CControlBar,pWnd);//取得控制条句柄
if (pBar!=NULL){
CMenu Menu;//建立弹出式菜单
CPoint pt; //鼠标指针位置
pt.x = LOWORD(pMsg->lParam);
pt.y = HIWORD(pMsg->lParam);
pBar->ClientToScreen(&pt);//转换成屏幕坐标
if (Menu.LoadMenu(IDR_TOOLBAR)){//调入菜单资源
CMenu* pSubMenu = Menu.GetSubMenu(0);//取得选项
if (pSubMenu!=NULL){//跟踪菜单以取得输入
pSubMenu->TrackPopupMenu(TPM_LEFTALIGN|
TPM_RIGHTBUTTON,pt.x,pt.y,this);
}
}
}
}
return CFrameWnd::PreTranslateMessage(pMsg);
}
(4)在消息映射函数后增加
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
......//其它消息映射函数
//}}AFX_MSG_MAP
// 增加扩展命令消息和更新消息映射,利用现成MFC函数更新菜单
ON_UPDATE_COMMAND_UI(IDR_MAINTOOLBAR,OnUpdateControlBarMenu)
ON_COMMAND_EX(IDR_MAINTOOLBAR,OnBarCheck)
......//其它工具条映射函数基本相同,只有标识不同
END_MESSAGE_MAP()
(5)重要的程序设计技巧步骤
上述步骤1和步骤4是实现工具条属性菜单有效控制的最关键步骤,如果不在步骤1中将菜单ID设置成工具 条的ID,或者不在步骤4中设置相应的消息映射,都不可能实现工具条菜单与工具条状态的有效控制。工具条菜单中的其它属性功能可模仿实现。笔者实现的工具 条控制演练示例程序结果如下:
工具条演练示例结果