工具条研究手记(3)- 工具条源代码剖析

/*****************************************************************/
/*          工具条研究手记(3)- 工具条源代码剖析                */
/*****************************************************************/

下面将通过研究工具条的源代码探讨几个问题。

1、工具条类的数据

CToolBar类有下列数据成员,均为保护成员。并在构造函数中设置它们的缺省值如下:

 CMapStringToPtr* m_pStringMap = NULL;  // used as CMapStringToUInt
 HRSRC m_hRsrcImageWell = NULL;  // 用于加载资源图片
 HINSTANCE m_hInstImageWell= NULL;  // 加载图片的进程句柄
 HBITMAP m_hbmImageWell = NULL;  // 保存已经加载的工具条位图
 BOOL m_bDelayedButtonLayout = TRUE;  // 用于控制工具条按钮布局
//
// 注:
//
// 在工具条析构的时候会自动销毁 m_hbmImageWell和 m_pStringMap两个对象。
//

 CSize m_sizeImage;  //按钮图片的缺省大小
 m_sizeImage.cx = 16;
 m_sizeImage.cy = 15;

 CSize m_sizeButton; //按钮的缺省尺寸
 m_sizeButton.cx = 23;
 m_sizeButton.cy = 22;

//下面的数据定义位于CControlBar类中,是public,可以改变它们的值,获得边界尺寸怪异的工具条
//方法是:从CToolBar 派生一个新的工具条类,然后在构造函数里面改变这几个数据的值。
//
// int m_cxLeftBorder, m_cxRightBorder;
// int m_cyTopBorder, m_cyBottomBorder;
//
//这里仅改变两个值 ,为了更易于加把手
 m_cyTopBorder = 3;
 m_cyBottomBorder = 3;
}

2、 关于Create和CreateEx

工具条的缺省构造函数是CToolBar(),因此声明一个对象以后要生成它。这两种生成方式有什么不同呢?

看它们的定义:

BOOL Create(CWnd* pParentWnd, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_TOP, UINT nID = AFX_IDW_TOOLBAR);
BOOL CreateEx(CWnd* pParentWnd, DWORD dwCtrlStyle = TBSTYLE_FLAT,
 DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP, CRect rcBorders = CRect(0, 0, 0, 0),
 UINT nID = AFX_IDW_TOOLBAR);

实际上 Create函数最终也是调用CreateEx:

BOOL CToolBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID)
{
 return CreateEx(pParentWnd, 0, dwStyle,
  CRect(m_cxLeftBorder, m_cyTopBorder, m_cxRightBorder, m_cyBottomBorder), nID);
}

因此,我们看到如果用Create函数生成工具条,仅仅是风格稍有不同,后者采用了平滑按钮,不画底边。:

#define TBSTYLE_BUTTON          0x0000
#define TBSTYLE_FLAT            0x0800

#define CBRS_TOP            (CBRS_ALIGN_TOP|CBRS_BORDER_BOTTOM)

类似的按钮样式还包括:

#define TBSTYLE_BUTTON          0x0000
#define TBSTYLE_SEP             0x0001
#define TBSTYLE_CHECK           0x0002
#define TBSTYLE_GROUP           0x0004
#define TBSTYLE_CHECKGROUP      (TBSTYLE_GROUP | TBSTYLE_CHECK)
#if (_WIN32_IE >= 0x0300)
#define TBSTYLE_DROPDOWN        0x0008
#endif
#if (_WIN32_IE >= 0x0400)
#define TBSTYLE_AUTOSIZE        0x0010 // automatically calculate the cx of the button
#define TBSTYLE_NOPREFIX        0x0020 // if this button should not have accel prefix
#endif

#define TBSTYLE_TOOLTIPS        0x0100
#define TBSTYLE_WRAPABLE        0x0200
#define TBSTYLE_ALTDRAG         0x0400
#if (_WIN32_IE >= 0x0300)
#define TBSTYLE_FLAT            0x0800
#define TBSTYLE_LIST            0x1000
#define TBSTYLE_CUSTOMERASE     0x2000
#endif
#if (_WIN32_IE >= 0x0400)
#define TBSTYLE_REGISTERDROP    0x4000
#define TBSTYLE_TRANSPARENT     0x8000
#define TBSTYLE_EX_DRAWDDARROWS 0x00000001
#endif

另外一点要注意的是,从CreateEx函数中可以得知,工具条窗口的窗口类名字是:

"ToolbarWindow32"

3、关于改变按钮的尺寸,改变后所有按钮还是同一个尺寸。

工具条类的SetSizes函数可以用来改变按钮的尺寸,但是要注意:

 // button must be big enough to hold image
 //   + 7 pixels on x
 //   + 6 pixels on y
 ASSERT(sizeButton.cx >= sizeImage.cx + 7);
 ASSERT(sizeButton.cy >= sizeImage.cy + 6);

4、关于改变工具条的高度

工具条类的SetHeight函数可以用来改变整个工具条的高度,但是要注意的是,调用这个函数以后,工具条按钮自动垂直居中,

如果前面设置了m_cyTopBorder 和 m_cyBottomBorder,它们的值将会改变。

5、关于LoadToolBar

通常,工具条对象Create完成以后,就会调用LoadToolBar函数加载工具条,例如:

 if (!m_wndToolBar.Create(this) ||
  !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
 {
  TRACE0("Failed to create toolbar/n");
  return -1; 
 }
这个加载过程有些细节很有意思,值得看看:

首先是一个结构,这个结构位于编译后的工具条资源的开头

struct CToolBarData
{
 WORD wVersion; //版本号 ==1
 WORD wWidth;
 WORD wHeight;
 WORD wItemCount; //给出后面有几项
 //WORD aItems[wItemCount] 

 WORD* items()
  { return (WORD*)(this+1); }
};
注意,该结构没有使用可变长度数组,而是添加了一个函数,直接返回指向结构数据后面一个字节的指针,这种方法值得借鉴。

BOOL CToolBar::LoadToolBar(LPCTSTR lpszResourceName)
{
 ASSERT_VALID(this);
 ASSERT(lpszResourceName != NULL);

 // 首先定位位图资源
 HINSTANCE hInst = AfxFindResourceHandle(lpszResourceName, RT_TOOLBAR);
 HRSRC hRsrc = ::FindResource(hInst, lpszResourceName, RT_TOOLBAR);
 if (hRsrc == NULL) return FALSE;
 //加载资源数据
 HGLOBAL hGlobal = LoadResource(hInst, hRsrc);
 if (hGlobal == NULL) return FALSE;
 //锁定资源,并强制转化成结构指针
 CToolBarData* pData = (CToolBarData*)LockResource(hGlobal);
 if (pData == NULL) return FALSE;
 ASSERT(pData->wVersion == 1);

 //这里,它不直接使用pData->items(),而是new了一块内存,为什么?
 //另外,通过函数SetButtons的代码,我们可以知道这一串WORD数据
 //给出的是每个按钮的ID
 UINT* pItems = new UINT[pData->wItemCount];
 for (int i = 0; i < pData->wItemCount; i++)
  pItems[i] = pData->items()[i];
 BOOL bResult = SetButtons(pItems, pData->wItemCount);
 delete[] pItems;

 //设置按钮的尺寸
 if (bResult)
 {
  // set new sizes of the buttons
  CSize sizeImage(pData->wWidth, pData->wHeight);
  //注意这里,它都增加了7个象素
  CSize sizeButton(pData->wWidth + 7, pData->wHeight + 7);
  SetSizes(sizeButton, sizeImage);

  //这个时候才加载位图资源,可见位图资源和RT_TOOLBAR资源没有在一起
  bResult = LoadBitmap(lpszResourceName);
 }

 UnlockResource(hGlobal);
 FreeResource(hGlobal);

 return bResult;
}

6、关于函数AfxLoadSysColorBitmap

这个函数是工具条类使用的,是一个未公开的API函数,定义如下:
HBITMAP AFXAPI AfxLoadSysColorBitmap(HINSTANCE hInst, HRSRC hRsrc, BOOL bMono = FALSE);

LoadBitmap函数里面使用这个函数加载工具条按钮的位图资源,然后调用AddReplaceBitmap设置资源句柄。在这个函数中
使用了位图资源前面的16个系统颜色的调色板对位图资源中的颜色做了一下处理,因此位图资源不能超过256色,必须有调色板。
如果你已经有了自己的真彩色HBITMAP资源,直接可以通过函数SetBitmap设置也可以。但是要注意,你必须保证程序
运行期间,你的HBITMAP资源一直有效。因为在函数SetBitmap中,做了如下的设置:

 m_hInstImageWell = NULL;
 m_hRsrcImageWell = NULL;
AddReplaceBitmap函数在添加新的位图资源的同时,调用函数AfxDeleteObject删除了原来的位图资源。

7、关于SetButtonStyle

工具条按钮定义了若干类型,分别是:
TBBS_BUTTON   标准下压按钮(缺省情况)
TBBS_SEPARATOR   分隔条
TBBS_CHECKBOX 自动复选框按钮
TBBS_GROUP    开始一组按钮
TBBS_CHECKGROUP   表示一组自动复选框按钮的首个按钮

8、细谈SetButtonInfo函数及其用途。

SetButtonInfo用于设置某个按钮,它的接口定义如下:
下面是它的几个接口函数说明:

void CToolBar::SetButtonInfo(int nIndex, UINT nID, UINT nStyle, int iImage)

注意CToolBarCtrl类也有这个函数,但是它们的定义是完全不同的,实际上,CToolBar::SetButtonInfo的代码如下:

void CToolBar::SetButtonInfo(int nIndex, UINT nID, UINT nStyle, int iImage)
{
 ASSERT_VALID(this);

 TBBUTTON button;
 _GetButton(nIndex, &button);
 TBBUTTON save;
 memcpy(&save, &button, sizeof(save));
 button.idCommand = nID;
 button.iBitmap = iImage;
 button.fsStyle = (BYTE)LOWORD(nStyle);
 button.fsState = (BYTE)HIWORD(nStyle);
 if (memcmp(&save, &button, sizeof(save)) != 0)
 {
  _SetButton(nIndex, &button);
  m_bDelayedButtonLayout = TRUE;
 }
}

msdn介绍说这个函数的功能是设置工具条按钮的 ID, style, 以及图片的编号。因此,通常这个函数有三个方面的用途:

(1)用于在工具条中添加其他控件

需要注意的是,当给出的nStyle是TBBS_SEPARATOR的时候,iImage是指它的宽度,而不是图片的编号。这个功能在向工具条中添加其它控件(比如编辑控件、组合框控件)的时候非常有用,它可以改变按钮的位置,为添加的控件留出空间。这个在后面将谈到。

(2)用于改变工具条中按钮的样式

例如:
    m_wndToolBar.SetButtonInfo(i, ID_FILE_SAVE, TBBS_CHECKBOX, 2);
该代码将工具条按钮ID_FILE_SAVE设置成CHECKBOX按钮,即单击该按钮显示按下,再次单击显示抬起。

例如:
    int i = m_wndToolBar.CommandToIndex( ID_BUTTON1);
    m_wndToolBar.SetButtonInfo(i, ID_BUTTON1, TBBS_GROUP|TBBS_CHECKBOX      , 9);
    m_wndToolBar.SetButtonInfo(i+1, ID_BUTTON2, TBBS_GROUP |TBBS_CHECKBOX  , 10);
    m_wndToolBar.SetButtonInfo(i+2, ID_BUTTON3, TBBS_GROUP |TBBS_CHECKBOX  , 11);
该代码设置三个相邻的按钮为互斥按钮,即同一时刻只能有一个按钮按下。

TBBS_GROUP |TBBS_CHECKBOX  等价于  TBBS_CHECKGROUP

例如:
    int i = m_wndToolBar.CommandToIndex( ID_BUTTON1);
    m_wndToolBar.SetButtonInfo(i, ID_BUTTON1, TBBS_SEPARATOR  , 0);
上述代码将一个按钮设置成分隔条,并设置它的宽度为0,这可以用于动态隐藏工具条按钮
需要的时候,还可以把它重新显示回来:
    int i = m_wndToolBar.CommandToIndex( ID_BUTTON1);
    m_wndToolBar.SetButtonInfo(i, ID_BUTTON1, TBBS_BUTTON  , 9);
当然,nStyle参数不仅仅可以用msdn上面提到的几种,比如CToolBarCtrl::SetButtonInfo就有更为强大的功能,这个以后会谈到。

(3)改变工具条按钮的图片。有的时候想在程序运行过程中,根据某些条件动态改变按钮的图片,
就可以用这个函数。方法如下:

(1) 在资源里面添加一个位图文件IDB_BITMAP1,注意它的高度和工具条位图的高度保持一致,然后里面可以有几个按钮图片。
(2) 工具条生成以后,把这些图片添加到工具条的图片列表:

 CToolBarCtrl &toolctrl = m_wndToolBar.GetToolBarCtrl();
 int TotalImage =toolctrl.AddBitmap(N, IDB_BITMAP1);  //N给出位图中图片的个数
    这个时候TotalImage里面就保存了工具条里面已经保存的按钮图片的个数,它比实际按钮个数要多,你可以
把这个值保存在CMainFrame类里面,供以后使用
(3) 在需要更换工具条按钮图片的时候,这样写:
 int index = m_wndToolBar.CommandToIndex(ID_FILE_SAVE);
 m_wndToolBar.SetButtonInfo(index, ID_FILE_SAVE, TBBS_BUTTON, TotalImage-1);
则将ID_FILE_SAVE按钮的图片设置成图片列表中的最后一个。


9、CToolBar 类的public函数说明

 void SetSizes(SIZE sizeButton, SIZE sizeImage); //设置按钮尺寸,图片尺寸
 void SetHeight(int cyHeight);   //设置工具条的高度

 BOOL LoadToolBar(LPCTSTR lpszResourceName); //加载工具条
 BOOL LoadToolBar(UINT nIDResource);

 BOOL LoadBitmap(LPCTSTR lpszResourceName); //加载位图,这两个函数基本不用了
 BOOL LoadBitmap(UINT nIDResource);

 //设置工具条位图,可以用来改变工具条按钮的位图
 BOOL SetBitmap(HBITMAP hbmImageWell);  

 //如果没有使用LoadToolBar,而使用LoadBitmap,则需要用这个函数设置按钮
 BOOL SetButtons(const UINT* lpIDArray, int nIDCount); 

 //根据ID得到按钮的位置编号
 int CommandToIndex(UINT nIDFind) const; 
 //根据按钮的位置得到它的ID
 UINT GetItemID(int nIndex) const;

 //返回指定按钮的矩形区域坐标,单位是象素,相对于工具条左上角
 virtual void GetItemRect(int nIndex, LPRECT lpRect) const;
 //获取指定按钮的属性
 UINT GetButtonStyle(int nIndex) const;
 //设置指定按钮的属性
 void SetButtonStyle(int nIndex, UINT nStyle);
 //获取指定按钮的信息,包括ID,类型,相关联的图片编号
 void GetButtonInfo(int nIndex, UINT& nID, UINT& nStyle, int& iImage) const;
 //设置指定按钮的信息
 void SetButtonInfo(int nIndex, UINT nID, UINT nStyle, int iImage);
 //设置指定按钮的文字
 BOOL SetButtonText(int nIndex, LPCTSTR lpszText);
 //获取指定按钮的文字
 CString GetButtonText(int nIndex) const;
 void GetButtonText(int nIndex, CString& rString) const;

 //获取工具条内的CToolBarCtrl
 CToolBarCtrl& GetToolBarCtrl() const;
 //设置父窗口。由于通常在生成工具条的同时指定了父窗口,因此这个函数很少用到。
 void SetOwner(CWnd* pOwnerWnd);
 //用指定位图替换工具条已有位图,如果原来工具条没有设置位图,则添加这个位图。
 BOOL AddReplaceBitmap(HBITMAP hbmImageWell);

//-------------------------------------------------
// End

转自:http://blog.vckbase.com/iwaswzq/articles/1771.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值