(一)工具条控制的主要功能 所谓工具条就是具有位图和分隔符组成的一组命令按钮,位图按钮部分可以是下推按钮、检查盒按钮、无线按 钮等。工具条对象类派生于主窗口架框类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()用来设置工具条左边界和状态,以便为画把手和关闭按 钮调整客户区域并提供绘图状态。 此外,还有鼠标移动到把手上光标改变形状和关闭按钮功能,由于篇幅所限这里从略,有兴趣的读者完全可以自己实现 |
CToolBar的使用总结
最新推荐文章于 2017-11-30 17:42:17 发布
今天需要把程序工具栏的颜色背景改变,苦无解决办法,正在解决中,发现这边文章,学习之