在VC++下實(shí)現(xiàn)高彩色工具條
引言
一些Windows系統(tǒng)自帶程序如資源管理器、Internet Explorer等程序的工具條看上去和其他一些程序的工具條不太一樣,在顏色上要漂亮許多。其實(shí)這些程序的工具條上的圖標(biāo)均為256色,而普通應(yīng)用程序 在工具欄上所顯示圖標(biāo)的顏色通常只有16色,這就決定了后者在視覺上遠(yuǎn)沒有前者美觀。由于Windows隨系統(tǒng)而帶的程序也是由開發(fā)人員編寫的應(yīng)用程序, 這就說明通過程序編碼可以實(shí)現(xiàn)256色甚至更多色彩的圖標(biāo)在工具欄上的顯示。為此筆者經(jīng)過摸索,通過MFC編程在應(yīng)用程序中實(shí)現(xiàn)了高彩色工具條。現(xiàn)將實(shí)現(xiàn) 的主要方法介紹如下,以饗廣大讀者。
基本設(shè)計(jì)思路
在實(shí)現(xiàn)高彩色工具條之前,先研究一下普通16色的工具條的實(shí)現(xiàn)過程,并從中總結(jié)出改進(jìn)方法。在VC的資源視圖中工具條是一個(gè)資源名為 IDR_MAINFRAME的Toolbar型資源,并可通過在編輯按鈕上的圖標(biāo)來完成工具條上圖標(biāo)的繪制。雖然在資源視圖中工具條上各按鈕的圖標(biāo)是相互 獨(dú)立的,但在存儲(chǔ)時(shí)并非像圖標(biāo)一樣保存為ico格式文件而是以bmp位圖格式保存在磁盤上的。該位圖是一個(gè)由工具條上的按鈕圖標(biāo)組成的長(zhǎng)條型位圖圖像,中 間沒有任何縫隙,在程序運(yùn)行和在資源視圖對(duì)工具條進(jìn)行編輯時(shí)該圖像首先裝載到一個(gè)圖像列表中,然后工具欄根據(jù)索引依次從圖像列表中將圖像顯示到工具條的各 個(gè)按鈕上。由于VC限制工具欄上的圖標(biāo)不能超出16色,因此不論是在資源視圖直接編輯位圖還是用復(fù)制粘貼等手段均無法獲取超出256色的工具條(注:用復(fù) 制粘貼的方法雖然在編輯視圖中可以暫時(shí)顯示出256色的圖標(biāo),但在程序運(yùn)行時(shí)仍會(huì)退化成16色)。
由于不能在資源視圖中通過編輯Toolbar資源實(shí)現(xiàn)16色以上的圖標(biāo),加之工具條在顯示時(shí)有并不直接從Toolbar獲取圖標(biāo)而是從圖像列表中讀 取,因此可以通過其他一些圖像處理軟件做好類似于工具條的bmp圖像(僅顏色比普通工具條bmp圖像豐富,其余完全一樣),并以位圖的形式加入到程序資 源。在使用時(shí),先將其讀取到圖像列表,這樣圖像列表中用于顯示到工具條上的圖標(biāo)的顏色就可以是256、24位、甚至32位色的了。由于工具條缺省時(shí)將直接 加載資源名為IDR_MAINFRAME的Toolbar型資源作為圖標(biāo)的來源,因此還必須通過SetImageList()函數(shù)將含有高彩色工具條位圖 的圖像列表指定為工具條的圖標(biāo)來源。
真彩工具條的實(shí)現(xiàn)
由于工具條的創(chuàng)建是在主框架類的OnCreate()函數(shù)中完成的,因此高彩色圖像的裝載和圖像列表的替換工作必須也在此進(jìn)行。在進(jìn)行程序設(shè)計(jì)之前, 需要做好各種準(zhǔn)備工作,比如高彩色工具條位圖的繪制、高彩色位圖加入到資源等。繪制工具條位圖時(shí),必須控制好圖像的尺寸,如需要有N個(gè)邊長(zhǎng)為 M的圖標(biāo),那么需要繪制的位圖尺寸為長(zhǎng)=N*M;寬=M。真彩位圖在加入到工程之后就不能再在VC的資源視圖中進(jìn)行編輯了。由于這個(gè)彩色位圖僅起到美化界 面的作用,因此具體對(duì)的事件響應(yīng)等工作還要通過設(shè)置原有的Toolbar資源來完成。
準(zhǔn)備工作就緒后,先要把工具條位圖裝載到圖像列表,這樣才能被工具條做獲取。在作這一步時(shí),必須用::LoadImage()函數(shù)去加載工具條位圖,并通過宏MAKEINTRESOURCE()來指定具體要加載哪一個(gè)資源位圖:
HBITMAP hbm = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),
MAKEINTRESOURCE(IDB_TOOLBAR), //加載IDB_TOOLBAR
IMAGE_BITMAP, //按位圖格式
0,0, // cx,cy
LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS );
LoadImage返回一個(gè)位圖句柄HBITMAP,但在MFC下使用CBitmaps會(huì)更加方便,可創(chuàng)建一個(gè)
CBitmap對(duì)象并用Attach()成員函數(shù)將它與位圖句柄聯(lián)系起來:
CBitmap bm;
bm.Attach(hbm);
MFC加載工具欄位圖時(shí)使用了一個(gè)內(nèi)部函數(shù)AfxLoadSysColorBitmap()將缺省顏色設(shè)定為16色,因此為了顯示16色以上的圖像,必須在調(diào)用圖像列表類CImageList的Create()函數(shù)創(chuàng)建圖像列表時(shí)對(duì)圖像清單做進(jìn)一步的處理:
m_ilToolBar.Create(32,32,ILC_COLOR8, 4, 4);
m_ilToolBar.Add(&bm,(CBitmap*)NULL);
這里用ILC_COLOR8標(biāo)明了創(chuàng)建的圖像列表是256色的,在VC的commctrl.h中對(duì)其有定義,并且還提供有其他幾種顏色位深度的預(yù)定義:
#define ILC_COLOR4 0x0004 //16色
#define ILC_COLOR8 0x0008 //256色
#define ILC_COLOR16 0x0010 //16位色
#define ILC_COLOR24 0x0018 //24位色
#define ILC_COLOR32 0x0020 //32位色
如果使用的工具條位圖只有256色(對(duì)于多數(shù)程序這樣已經(jīng)足夠),則顯然沒有必要再使用更高級(jí)別的位深度定義。最后一步,也是最關(guān)鍵的一步,必須通過 SetImageList()函數(shù)指定工具條m_wndToolBar的圖標(biāo)來源不再是原來缺省的圖像列表而是含有高彩色位圖的圖像列表 m_ilToolBar:
m_wndToolBar.GetToolBarCtrl().SetImageList(&m_ilToolBar);
到此為止就可以通過MFC在自己編寫的程序中實(shí)現(xiàn)類似于IE等軟件的漂亮的工具條了。下圖就是筆者用上述方法得到的程序界面:

小結(jié)
本文通過對(duì)作為工具條圖標(biāo)來源的圖像列表的替換,實(shí)現(xiàn)了在普通MFC應(yīng)用程序中具備了以往只有Windows系統(tǒng)自帶程序才具備的高彩色工具條。較好 地美化了程序的界面。本文程序在Windows 98下,由Microsoft Visual C++ 6.0編譯通過。
用VC制作非常酷的工具條
自微軟推出Windows 95后,一大批全新的控件使我們的應(yīng)用程序更加美觀,使用也更加方便。其中一個(gè)顯著的變化就是工具條不再是一個(gè)突出的3D小方框,而是變成了平面的狀態(tài),但 是只要把鼠標(biāo)移動(dòng)到上面,它就會(huì)自動(dòng)地浮出來,大大方便了用戶。
筆者經(jīng)過一段時(shí)間摸索,終于找到了制作這種工具條的方法。原來Windows 95封裝了許多常用的控件,大都被放在Comctrl32.dll中,其中Toolbar控件是用于制作工具條的。下面 簡(jiǎn)要介紹一下如何在VC5.0中添加一個(gè)Toolbar。
眾所周知,所有的控件都是某一類型的窗口,所以制作Toolbar也要從制作窗口開始。由于MFC的Toolbar類并不支持新的功能,所以我們只好用SDK方法,通過API調(diào)用來完成整個(gè)過程 ,該過程與制作一個(gè)傳統(tǒng)的工具條類似。
Toolbar是屬于comctrl32.dll的擴(kuò)展功能,所以要先調(diào)用InitCommonControlsEx()的函 數(shù)。該函數(shù)有一個(gè)重要的參數(shù)決定了對(duì)Toolbar的支持,它的主要作用是注冊(cè)Toolbar窗口,以 便在以后的程序中制作這種窗口,而普通的工具條則要調(diào)用InitCommandControls()。需要注意的是這兩個(gè)函數(shù)的寫法。
INITCOMMONCONTROLSEX icex;
DWORD dwStyle;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
//注意下面這兩個(gè)參數(shù)決定了是否注冊(cè)Toolbar
icex.dwICC=ICC_COOL_CLASSES|ICC_BAR_ CLASSES;
InitCommonControlsEx(&icex);
然后就可以調(diào)用CreateWindowEx這個(gè)函數(shù)來建立Toolbar窗口:
HWND hwndTB = CreateWindowEx(
WS_EX_TOOLWINDOW, //擴(kuò)展工具條風(fēng)格
TOOLBARCLASSNAME, //Toolbar類名
NULL,
WS_CHILD|WS_VISIBLE|TBSTYLE_FLAT, //窗口風(fēng)格
0,0,0,0, //大小
AfxGetApp()->GetMainWnd(), //父窗口名
NULL,
AfxGetInstanceHandle(), //實(shí)例
NULL);
判斷一下窗口句柄,如果不為空,就表示窗口建立成功。此時(shí)的Toolbar不過是一個(gè)空空的窗口,我們可以根據(jù)需要向里面添加按鈕。向Toolbar 中添加按鈕是通過向它發(fā)送消息來 實(shí)現(xiàn)的,以下過程與制作傳統(tǒng)的工具條基本一致。首先,建立一個(gè)ImageList控件,然后通過定義按鈕的數(shù)據(jù)結(jié)構(gòu)來確定每個(gè)按鈕的類型。
// 建立一個(gè)Imagelist 控件,
HWND himl;
//MYICON_CX,MYICON_CY是每個(gè)按鈕的大小
himl= ImageList_Create(MYICON_CX,MYICON_CY,ILC_COLOR4,0,4);
//加入事先作好的工具條位圖IDB_BITMAP2
ImageList_Add( himl,
LoadBitmap(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDB_BITMAP2)),NULL);
//通過消息把位圖加入到Toolbar中
SendMessage(hwndTB, TB_SETIMAGELIST, 0, (LPARAM)himl);
下面加入5個(gè)普通的按鈕:
TBBUTTON tbArray[5]; //按鈕的數(shù)據(jù)結(jié)構(gòu)
for(i=0;i<5;i++)
{
tbArray[i].iBitmap = i; //第i個(gè)位圖
tbArray[i].idCommand = IDM_BUTTONSTART+i;
//命令I(lǐng)D
tbArray[i].fsState = TBSTATE_ENABLED;
tbArray[i].fsStyle = TBSTYLE_BUTTON;
//按鈕風(fēng)格
tbArray[i].dwData = 0;
tbArray[i].iString = i; //顯示的字符串
}
//設(shè)置按鈕結(jié)構(gòu)的大小
::SendMessage(hwndTB,TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
//添加按鈕到Toolbar中
::SendMessage(hwndTB,TB_ADDBUTTONS,(UINT)5, (LPARAM)tbArray);
至此,一個(gè)很酷的工具條基本上制作完成,最后再調(diào)用函數(shù)ShowWindow()即可: ShowWindow(hwndTB, SW_SHOWMAXIMIZED);
當(dāng)點(diǎn)擊按鈕時(shí),Toolbar就把消息傳送到父窗口中,由父窗口響應(yīng)消息。Toolbar中按鈕的ID包含在消息函數(shù)的wParam參數(shù)中,可以設(shè)置 它來調(diào)度不同的模塊。這時(shí)可以重載父窗口的O nCommand()函數(shù),根據(jù)wParam參數(shù)判斷點(diǎn)擊了哪個(gè)按鈕。假定父窗口是主窗口框架,代碼如下:
BOOL CMainFrame::OnCommand(WPARAM wParam,LPARAM lParam)
{
switch(wParam)
{
case IDM_BUTTONSTART+0:
AfxMessageBox(“你點(diǎn)中了第一個(gè)按鈕!!", MB_ICONINFORMATION);
break;
case IDM_BUTTONSTART+1:
AfxMessageBox(“你點(diǎn)中了第二個(gè)按鈕!!",MB_ICONINFORMATION);
break;
case IDM_BUTTONSTART+2:
AfxMessageBox(“你點(diǎn)中了第三個(gè)按鈕!!", MB_ICONINFORMATION);
break;
}
return CMainFrame::OnCommand(wParam,lParam);
}
Visual C++ 版本6中工具條的新特色
Dave Schmi
微軟在www.microsoft.com/visualc已經(jīng)推出Visual C++6.0預(yù)覽版幾個(gè)月了。正式版預(yù)計(jì)到今年年底發(fā)布。同時(shí),預(yù)覽版顯示出版本6將包含大量的改進(jìn)和提高,包括支持Internet控件,例如扁平工具 條等。雖然改進(jìn)的控件包與Internet無關(guān),但它首先出現(xiàn)在Internet Explorer中,因此它就被取做這個(gè)名字了。事實(shí)上,官方發(fā)布的預(yù)覽版的標(biāo)題是“針對(duì)Internet Explorer 4.0的Visual C++ 5.0技術(shù)預(yù)覽”。
在以前關(guān)于MFC工具條類的討論專題中,我曾答應(yīng)提供一個(gè)在版本6中工具條的外觀演示。有一個(gè)很好的消息,那就是你現(xiàn)在用CToolBar所作的所有 工作在新的版本中都是有效的,包括那些在以前的欄目中所描述的一些擴(kuò)展功能。因此,你將很容易修改現(xiàn)存的程序以獲得象Internet Explorer和Visual Studio中那樣“酷”的界面。此外,并沒有什么壞消息。
工具條的新特色
早在版本4中,CToolBar就已被MFC庫完全實(shí)現(xiàn)了。一旦公用控件動(dòng)態(tài)鏈接庫(命名為comctl32.dll)變得無所不在了, CToolBar就成了如今已包含在操作系統(tǒng)中的工具條控件的代名詞了。然而,CToolBar并沒有揭示公用工具條控件的所有能力。如今,通過 CreateEx()函數(shù),它成功了。
公用控件動(dòng)態(tài)鏈接庫現(xiàn)在包含了至少三類風(fēng)格:最初的、在Internet Explorer3.0中加入的以及在Internet Explorer 4.0中加入的。雖然這些版本理論上是向下兼容的,但某些專業(yè)人員曾寫出一些不能在后來版本中正常運(yùn)行的應(yīng)用程序,這可能是這些程序采用了一些沒有公開的 功能,而這些功能并沒有被包含在所有的版本中。
Visual C++程序員沒有這樣的經(jīng)歷,因?yàn)樵赩isual C++4.0或5.0中comctl32.dll并不是一個(gè)可以再分發(fā)的組件,它在安裝Internet Explorer時(shí)被更新,因此MFC程序員無法依靠最新版本的某些功能來用于他們的程序。這就是CToolBar僅僅具有最初的DLL的有限功能的原 因。CToolBar能夠?qū)崿F(xiàn)最新的特色意味著微軟將在Visual C++6.0中包含最新的DLL并將其作為一個(gè)可以再分發(fā)的組件。
絕大多數(shù)新特色將由在調(diào)用CreateEx()和其它CToolBar成員函數(shù)時(shí)指定的新的風(fēng)格標(biāo)志來確定。下面是commctrl.h的一部分,它定義了TBSTYLE類標(biāo)識(shí)符:
#define TBSTYLE_BUTTON 0x0000
#define TBSTYLE_SEP 0x0001
#define TBSTYLE_CHECK 0x0002
#define TBSTYLE_GROUP 0x0004
#define TBSTYLE_CHECKGROU TBSTYLE_GROUP | TBSTYLE_CHECK)
#if (_WIN32_IE $#@62;= 0x0300)
#define TBSTYLE_DROPDOWN 0x0008
#endif
#if (_WIN32_IE $#@62;= 0x0400)
#define TBSTYLE_AUTOSIZE 0x0010
#define TBSTYLE_NOPREFIX 0x0020
#endif
#define TBSTYLE_TOOLTIPS 0x0100
#define TBSTYLE_WRAPABLE 0x0200
#define TBSTYLE_ALTDRAG 0x0400
#if (_WIN32_IE $#@62;= 0x0300)
#define TBSTYLE_FLAT 0x0800
#define TBSTYLE_LIST 0x1000
#define TBSTYLE_CUSTOMERASE 0x2000
#endif
#if (_WIN32_IE $#@62;= 0x0400)
#define TBSTYLE_REGISTERDROP 0x4000
#define TBSTYLE_TRANSPARENT 0x8000
#define TBSTYLE_EX_DRAWDDARROWS 0x00000001
#endif
你會(huì)注意到其中的一些采用了條件編譯,依賴于_WIN32_IE的值,它缺省指的是Internet Explorer 4.0(即取值為0x0400)。對(duì)于Internet Explorer 3.0(即取值為0x0300)以前的版本,大多數(shù)的TBSTYLE標(biāo)識(shí)符指的是按鈕或是一組按鈕。Internet Explorer3.0引入了扁平鈕、文本標(biāo)簽、下拉列表和自定義繪制。Internet Explorer 4.0增強(qiáng)了下拉列表和自定義繪制功能,并且增加了支持OLE拖動(dòng)目標(biāo)到一個(gè)工具條。
扁平鈕和把手
在過去的18個(gè)月中我常常被問及該如何獲得象Internet Explorer和Visual Studio中的工具條一樣不使用浮雕按鈕而是用扁平鈕并且?guī)в斜阌谝苿?dòng)和定位的把手那樣酷的界面。這些特色并不被MFC所支持,因此最簡(jiǎn)單獲取的方法就 是購(gòu)買一個(gè)擴(kuò)展庫。而對(duì)于Visual C++ 6.0來說卻無須多此一舉,因?yàn)樗沟肅ToolBar類實(shí)現(xiàn)了對(duì)扁平鈕、把手和其它新的視覺效果的支持。
在預(yù)覽版中,AppWizard并不會(huì)自動(dòng)包括這些新特色,但它們卻很容易被加入。表1顯示了AppWizard創(chuàng)建的主框架窗口的OnCreate ()函數(shù),表2顯示了需要做哪些修改以獲得具有扁平鈕和把手的工具條。圖1顯示了表1創(chuàng)建出的工具條,而圖2顯示出了表2實(shí)現(xiàn)的工具條。
表 1: CMainFrame::OnCreate as generated by AppWizard
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if(CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
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)))
圖1
表2: Adding flat buttons and the gripper
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if(CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if(!m_wndToolBar.CreateEx(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 dont want tool tips or a resizeable toolbar
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_GRIPPER | CBRS_BORDER_3D | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
// TODO: Delete these three lines if you dont want the toolbar to
// be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
return 0;
}
圖2
為了作出扁平按鈕我必須使用CreateEx()來代替Create()。這個(gè)新的函數(shù)在afxext.h中聲明:
BOOL CreateEx
(
CWnd* pParentWnd, // parent window
DWORD dwCtrlStyle = TBSTYLE_FLAT, // extended style
DWORD dwStyle = // style
WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP,
CRect rcBorders = CRect(0,0,0,0), // border rectangle
UINT nID = AFX_IDW_TOOLBAR // identifier
);
因?yàn)閿U(kuò)展風(fēng)格缺省指的就是TBSTYLE_FLAT,因此我要得到扁平按鈕就只需要簡(jiǎn)單地將AppWizard形成的代碼中的Create()改為 CreateEx()即可。我將在后面實(shí)現(xiàn)其它的擴(kuò)展風(fēng)格。為了獲得把手,我必須在調(diào)用SetBarStyle()函數(shù)時(shí)包含CBRS_GRIPPER標(biāo) 志,參看表2。這是CControlBar類的一個(gè)新風(fēng)格,而CToolBar類是從它繼承而來的。請(qǐng)注意到我也加入了CBRS_BORDER_3D標(biāo) 志,這是為了修正一個(gè)未知的繪制問題,該問題將會(huì)在工具條的邊緣繪制一些多余的點(diǎn)。這也許意味著預(yù)覽版確實(shí)有這個(gè)問題,因?yàn)橐坏┪覍?D標(biāo)志加入就立即解 決了并且也似乎沒有影響到別的什么。
上面所作的兩個(gè)簡(jiǎn)單的改變是使得一個(gè)已存程序獲得酷界面的最省力的方法。在一個(gè)程序具有了扁平鈕和把手的同時(shí),它也不會(huì)發(fā)生不應(yīng)有的其它改變。