/*****************************************************************//* 工具条研究手记(6)- 在工具条上面创建控件并响应 *//*****************************************************************/
一、工具条上面创建组合框,并响应组合框的选择消息。
通常由两种方法在工具条上面添加组合框对象,一种方法是从CToolBar派生一个工具条类,然后嵌入一个组合框,生成工具条以后,创建这个组合框,替换工具条上面的一个分割栏。另一种方法是在工具条的父窗口中(通常是框架窗口)嵌入一个组合框,创建的方法相同。下面分别举例:
1、派生工具条类
第一步:用ClassWizard从CToolBar派生一个CMyToolBar工具条类,然后添加一个public 组合框对象:
class CMyToolBar : public CToolBar{public: CMyToolBar(); virtual ~CMyToolBar();public: CComboBox m_Combo;
DECLARE_MESSAGE_MAP()};不用添加任何函数。
第二步:把框架窗口类中的工具条类,改成派生类对象:
#include "mytoolbar.h"
class CMainFrame : public CFrameWnd{......protected: // control bar embedded members CStatusBar m_wndStatusBar; CMyToolBar m_wndToolBar;......};
第三步:在工具条上面添加一个按钮,设置一个ID,比如 ID_MYCOMBOBOX,准备在这个按钮的位置创建组合框,而且组合框对象的ID就用这个ID。
第四步:正常创建工具条以后,创建该内嵌组合框:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct){ if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;
//创建这个工具条 if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) { TRACE0("Failed to create toolbar/n"); return -1; // fail to create }
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar);
//下面的代码开始生成工具条按钮上面的组合框。注意该组合框要代替的按钮的id是ID_MYCOMBOBOX//------------------------------------------------------------------------- int index = 0; RECT rect;
//找到指定的工具项 while(m_wndToolBar.GetItemID(index)!=ID_MYCOMBOBOX) index++; //或者使用 ?int index = m_wndToolBar.CommandToIndex(ID_MYCOMBOBOX);
//设置指定工具项的宽度并获取新的区域? 80是宽度 m_wndToolBar.SetButtonInfo(index, ID_MYCOMBOBOX, TBBS_SEPARATOR, 80); //取得该工具条项的矩形区域 m_wndToolBar.GetItemRect(index, &rect);
//注意适当调整位置,更美观 rect.top+=2; rect.bottom += 200;
// 创建组合框并显示 if (!m_wndToolBar.m_Combo.Create(WS_CHILD|WS_VISIBLE | CBS_AUTOHSCROLL | CBS_DROPDOWNLIST | CBS_HASSTRINGS , rect, &m_wndToolBar, ID_MYCOMBOBOX)) //注意父窗口使用该工具条,ID使用 ~~~~~~~~~~~~ { TRACE0("Failed to create combo-box/n"); return FALSE; } m_wndToolBar.m_Combo.ShowWindow(SW_SHOW); //添加组合框的内容 m_wndToolBar.m_Combo.AddString("25%"); m_wndToolBar.m_Combo.AddString("50%"); m_wndToolBar.m_Combo.AddString("75%"); m_wndToolBar.m_Combo.AddString("100%"); m_wndToolBar.m_Combo.SetCurSel(3);//------------------------------------------------------------------------- ......//其它代码
return 0;}
第五步:添加组合框响应的代码:
响应组合框和对话框中响应一个组合框相同,可以参考对话框中的代码,如下:
(1)mainfrm.h头文件中添加一个响应函数,响应组合框的选择变化消息
void OnSelChangeMyCombo();
(2)mainfrm.cpp文件消息映射表中添加
ON_CBN_SELCHANGE(ID_MYCOMBOBOX,OnSelChangeMyCombo)或者 ON_CONTROL(CBN_SELCHANGE,ID_MYCOMBOBOX,OnSelChangeMyCombo)或者 ON_CBN_SELENDOK(ID_MYCOMBOBOX,OnSelChangeMyCombo)
(3)添加响应函数:
void CMainFrame::OnSelchangeCombo1() { CString str; m_wndToolBar.m_Combo.GetLBText(m_Combo.GetCurSel(),str); MessageBox(str); }
其它组合框消息也可以响应,这些消息的映射代码是:// Combo Box Notification Codes#define ON_CBN_ERRSPACE(id, memberFxn) / ON_CONTROL(CBN_ERRSPACE, id, memberFxn)#define ON_CBN_SELCHANGE(id, memberFxn) / ON_CONTROL(CBN_SELCHANGE, id, memberFxn)#define ON_CBN_DBLCLK(id, memberFxn) / ON_CONTROL(CBN_DBLCLK, id, memberFxn)#define ON_CBN_SETFOCUS(id, memberFxn) / ON_CONTROL(CBN_SETFOCUS, id, memberFxn)#define ON_CBN_KILLFOCUS(id, memberFxn) / ON_CONTROL(CBN_KILLFOCUS, id, memberFxn)#define ON_CBN_EDITCHANGE(id, memberFxn) / ON_CONTROL(CBN_EDITCHANGE, id, memberFxn)#define ON_CBN_EDITUPDATE(id, memberFxn) / ON_CONTROL(CBN_EDITUPDATE, id, memberFxn)#define ON_CBN_DROPDOWN(id, memberFxn) / ON_CONTROL(CBN_DROPDOWN, id, memberFxn)#define ON_CBN_CLOSEUP(id, memberFxn) / ON_CONTROL(CBN_CLOSEUP, id, memberFxn)#define ON_CBN_SELENDOK(id, memberFxn) / ON_CONTROL(CBN_SELENDOK, id, memberFxn)#define ON_CBN_SELENDCANCEL(id, memberFxn) / ON_CONTROL(CBN_SELENDCANCEL, id, memberFxn)
2、不派生工具条类,直接把组合框放在框架窗口类中:
省略第一步。第二步:在框件窗口类中添加组合框对象:class CMainFrame : public CFrameWnd{......protected: // control bar embedded members CStatusBar m_wndStatusBar; CToolBar m_wndToolBar; ?//正常的工具条对象 CComboBox m_Combo;??//添加组合框对象......};第三、四、五步和上面相同,注意不用m_wndToolBar.m_Combo,直接使用m_Combo。
3、使用组合框时,应注意的几个问题:
(1)如果工具条没有TBSTYLE_FLAT属性,则不显示分割条,组合框可以正常显示,如果工具条用CreateEx创建,或者给出TBSTYLE_FLAT,则分割条会显示出来,缺省的工具条高度和组合框的高度差不多,正常情况下看不到组合框下面的分割条。但是如果给工具条按钮设置了文字,则工具条的高度加大,就看到组合框的中间(因为分割条是画在它的rect的中央)上下都出现部分分割条,非常不好看。目前我没有找到解决办法。(2)如果工具条是浮动的,也就是说加入了如下的代码: m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar);并且添加了文字: // 添加按钮文字 for(int i=0;i 〈m_wndToolBar.GetToolBarCtrl().GetButtonCount();i++){ UINT uButtonID = m_wndToolBar.GetItemID(i); CString str; if(str.LoadString(uButtonID)) { int p = str.Find("/n"); if(p) { CString strText = str.Right(str.GetLength() - p - 1); m_wndToolBar.SetButtonText( i, strText); } }}
则工具条的宽度是按照按钮的尺寸和数量计算的,由于组合框占用了比较宽的位置,会造成整个工具条宽度不够,导致后面的按钮被裁剪掉而看不见。解决的方法是加大按钮的尺寸: m_wndToolBar.SetSizes(CSize(60,40),CSize(16,16)); ~~~~ 主要是这个按钮的宽度尺寸
二、工具条上面创建一个滑动条(CSliderCtrl),并响应它。
在这个例子中,我们在工具条上面创建一个滑动条,然后根据它的滑动位置,调整一个图像在视图中的显示比例。
第一步:用ClassWizard创建一个名称为tbSlider的单文档工程,全部使用缺省设置。
第二步:打开资源编辑器,找到工具条,然后添加一个按钮, ID是 IDC_SLIDER。
第三步:打开MainFrm.h,添加一个滑动条对象:
class CMainFrame : public CFrameWnd{protected: // create from serialization onlyCMainFrame();DECLARE_DYNCREATE(CMainFrame)
// Attributespublic:CSliderCtrl m_Slider;.....
第四步:打开MainFrm.cpp 添加创建代码:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct){......// TODO: Delete these three lines if you don't want the toolbar to// be dockablem_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);EnableDocking(CBRS_ALIGN_ANY);DockControlBar(&m_wndToolBar);//-------------------------------------------------------------------// 下面是添加的代码
int index = m_wndToolBar.CommandToIndex(IDC_SLIDER);
m_wndToolBar.SetButtonInfo(index, IDC_SLIDER, TBBS_SEPARATOR, 80);
CRect rect; m_wndToolBar.GetItemRect(index, &rect);
// 创建滑动条并显示 if (!m_Slider.Create(WS_CHILD|WS_VISIBLE | TBS_HORZ | TBS_AUTOTICKS |TBS_BOTTOM , rect, &m_wndToolBar, IDC_SLIDER)) { TRACE0("Failed to create slider ctrl/n"); return FALSE; } //设置滑动的范围 m_Slider.SetRange(0,100); m_Slider.SetPos(20); m_Slider.ShowWindow(SW_SHOW);//------------------------------------------------------------------- return 0;}
第五步:我们这次在视类响应这个滑动条的消息,打开视类的头文件添加响应函数和绘图参数:
class CTbSliderView : public CView{......// Operationspublic: CBitmap m_bmp; //要绘制的位图 double r; //绘制的比例 void OnReleasedcaptureSlider(); //响应函数......
第六步:给视类重载OnInitialUpdate函数。然后打开视类CPP文件,添加如下的代码:
......#include "mainfrm.h" //包含框架类的头文件......BEGIN_MESSAGE_MAP(CTbSliderView, CView)...... ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER, OnReleasedcaptureSlider)END_MESSAGE_MAP()
//当用鼠标调整滑动条的位置,释放鼠标以后,滑动条会发送这个NM_RELEASEDCAPTURE消息
/// 响应函数void CTbSliderView::OnReleasedcaptureSlider(){ CMainFrame * pMain = (CMainFrame*)AfxGetMainWnd(); int pos = pMain->m_Slider.GetPos(); int min,max; pMain->m_Slider.GetRange(min,max); //计算缩放比例,最小缩放比例25%,最大比例4 r = (double)((pos - min)*3.75/(max-min) + 0.25); //刷新窗口,重新绘制图像 Invalidate();}
void CTbSliderView::OnInitialUpdate() { CView::OnInitialUpdate(); // 加载工程目录中的一个位图,别忘了找一个大小合适的位图放在工程的目录里面 HBITMAP hbmp = 0; hbmp = (HBITMAP)LoadImage(NULL,"02.bmp",IMAGE_BITMAP,0,0, LR_DEFAULTSIZE | LR_LOADFROMFILE | LR_CREATEDIBSECTION);
if(hbmp) this->m_bmp.Attach(hbmp); }
CTbSliderView::CTbSliderView(){ // 构造函数里面把比例系数缺省设置成1 r = 0.25;}
CTbSliderView::~CTbSliderView(){}
BOOL CTbSliderView::PreCreateWindow(CREATESTRUCT& cs){ // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs
return CView::PreCreateWindow(cs);}
/// CTbSliderView drawing
void CTbSliderView::OnDraw(CDC* pDC){ CTbSliderDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here if(m_bmp.m_hObject!=NULL) { //取得客户区尺寸 CRect clientRect; GetClientRect(&clientRect); //取得位图的尺寸,按照比例缩放 BITMAP bm; m_bmp.GetBitmap(&bm); int w = bm.bmWidth; int h = bm.bmHeight;
//计算缩放以后图像的大小 int rw = (int)((double)w * r); int rh = (int)((double)h * r);
//计算绘制图像的起始位置,图像居中显示 int sx = (clientRect.Width() - rw)/2; int sy = (clientRect.Height() - rh)/2; //调整,看是否位于客户区内 if(sx < 0) sx = 0; if(sy < 0) sy = 0;
//绘制图像 CDC memDC; memDC.CreateCompatibleDC(pDC); CBitmap *pOldBitmap = memDC.SelectObject(&m_bmp); pDC->StretchBlt(sx,sy,rw,rh,&memDC,0,0,w,h,SRCCOPY); memDC.SelectObject(pOldBitmap); }}
一切ok了,编译运行一下看看效果把,如果那里没有讲到,请多指正。
三、工具条上面创建一个编辑框和一个Spin控件,并且可以用Spin调整编辑框中的数字。
第一步:用ClassWizard创建一个名称为TbEditSpin的单文档工程,全部使用缺省设置。
第二步:打开资源编辑器,找到工具条,然后添加两个相邻的按钮, ID分别是 IDC_TOOLBAREDIT和 IDC_TOOLBARSPIN 。
第三步:打开mainfrm.h,添加控件public: CEdit m_ToolBarEdit; CSpinButtonCtrl m_ToolBarSpin;第四步:打开mainfrm.cpp 创建控件
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct){...... m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar);//----------------------------------------------------------// 下面是添加的代码
CRect rect;
int index = m_wndToolBar.CommandToIndex(IDC_TOOLBAREDIT);
//设置指定编辑框的宽度 80 m_wndToolBar.SetButtonInfo(index, IDC_TOOLBAREDIT, TBBS_SEPARATOR, 80); m_wndToolBar.GetItemRect(index, &rect);
// 创建编辑框并显示 if (!m_ToolBarEdit.Create(WS_CHILD|WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL , rect, &m_wndToolBar, IDC_TOOLBAREDIT)) { TRACE0("Failed to create Edit-box/n"); return FALSE; } m_ToolBarEdit.ShowWindow(SW_SHOW);
//-------------------- //创建SPIN控件 index = m_wndToolBar.CommandToIndex(IDC_TOOLBARSPIN);
//设置宽度 10 m_wndToolBar.SetButtonInfo(index, IDC_TOOLBARSPIN, TBBS_SEPARATOR, 10); m_wndToolBar.GetItemRect(index, &rect);
if (!m_ToolBarSpin.Create(WS_CHILD|WS_VISIBLE |UDS_ARROWKEYS | UDS_SETBUDDYINT,// | UDS_ALIGNRIGHT, rect, &m_wndToolBar, IDC_TOOLBARSPIN)) { TRACE0("Failed to create Spin/n"); return FALSE; } m_ToolBarSpin.ShowWindow(SW_SHOW);
m_ToolBarSpin.SetRange(1,100); m_ToolBarSpin.SetPos(50); m_ToolBarSpin.SetBuddy(&m_ToolBarEdit); m_ToolBarEdit.SetWindowText("50");//------------------------------------------------------------------------- return 0;}
第五步:在视类里面响应控件,可以响应编辑框文字的变化,也可以响应Spin控件,这里给出后者:
//头文件class CTbEditSpinView : public CView{......// Operationspublic: CString strSpin; //显示信息的字符串 //响应函数,因为是通过ON_NOTIFY响应,所以带有两个参数 void OnChangeToolbarSpin(NMHDR* pNMHDR, LRESULT* pResult);
//CPP文件#include "stdafx.h"#include "tbEditSpin.h"#include "tbEditSpinDoc.h"#include "tbEditSpinView.h"#include "mainfrm.h" //加上这个......
BEGIN_MESSAGE_MAP(CTbEditSpinView, CView)...... ON_NOTIFY(UDN_DELTAPOS, IDC_TOOLBARSPIN, OnChangeToolbarSpin) // ON_EN_CHANGE(IDC_TOOLBAREDIT, OnChangeToolbarEdit) //响应编辑框
END_MESSAGE_MAP()
void CTbEditSpinView::OnChangeToolbarSpin(NMHDR* pNMHDR, LRESULT* pResult) { NMUPDOWN* pNMUpDown = (NMUPDOWN*)pNMHDR;
int curPos = pNMUpDown->iPos; int direct = pNMUpDown->iDelta; CString strPos = (direct>0)?("你按了上箭头"):("你按了下箭头"); strSpin.Format("%s, 当前位置是:%d",strPos,curPos); Invalidate();
*pResult = 0;}
// 显示信息void CTbEditSpinView::OnDraw(CDC* pDC){ CRect rect; GetClientRect(&rect); pDC->DrawText(strSpin,&rect,DT_CENTER | DT_VCENTER |DT_SINGLELINE);}
CTbEditSpinView::CTbEditSpinView(){ strSpin = _T("");}
//-----------------------------------------------------------------------
4、总结。
上面一共给出三个例子,大同小异,基本上给出了工具条上面添加控件并且响应控件的做法。如果以后发现更好的例子和这个做法不同的,我会继续贴出。
工具条上面使用控件还可以通过CDialogBar来做,这个话题将放在另外一篇文章里面谈。
//--------------------------------------// End// iwaswzq 2004/11/25
转自: http://blog.vckbase.com/iwaswzq/archive/2007/08/01/1766.html