【Windows程式設計】系列第六篇:建立Toolbar與Statusbar
上一篇我們學習瞭解瞭如何使用Windows GDI畫圖,該應用程式都是光光的靜態視窗,我們使用Windows應用程式,但凡稍微複雜一點的程式都會有工具欄和狀態列,工具欄主要用於一些快捷功能按鈕。比如典型的windows應用程式的上面是選單欄,從選單欄我們可以選擇應用程式提供的各種功能,但是有的功能比較常用,且不能放在第一級選單,需要進入二級、三級甚至更多的選單才能選擇。顯然這樣使用起來比較麻煩,於是這時候工具欄的作用就體現出來了,一般工具欄位於選單欄的下面,但是位於客戶視窗的上面。下面就是windows的文字編輯器的工具欄:
Statusbar主要用於顯示應用程式的執行狀態,統計資訊,操作資訊等提示作用,一般是隻讀狀態。典型的狀態列放在視窗的最下面,比如下面就是我正在使用的Word應用程式的狀態列:
我們這次要一起學習的就是使用純Windows API函式建立基本的狀態列和選單欄。如果屬性MFC的朋友知道,如果用MFC來做工具欄和選單欄很簡單。但是使用純API就麻煩一些了,當然帶來的感覺是不一樣的,比如要動態建立,使用MFC的資源編輯器就無能為力,但是對於我們今天要使用的API建立方式來說,就顯示出他的強大了。
首先,前面我們在講解Windows常用控制元件的建立時就知道,所有帶視窗的控制元件建立實際上都是呼叫Windows提供的CreateWindow或者CreateWindowEx(以下以CreateWindowEx為例)這兩個函式,當然今天的選單欄和狀態列一樣需要這兩個函式來建立。由於工具欄和狀態列都是Windows的通用控制元件組中的控制元件,有預設的類名。建立工具欄時,類名為TOOLBARCLASSNAME,建立狀態列時,類名為STATUSCLASSNAME,這兩個巨集定義在commctrl.h檔案中根據是否使用UNICODE編碼分別是“ToolbarWindow32”和“msctls_statusbar32”的寬字元版本和ANSI版本。
- 建立工具欄
除了使用CreateWindowEx建立好工具欄後,作為一個更好看一點,我們還可以給工具欄加上圖示以及功能提示。為了加圖示,最方便的方法是使用一些列影象列表API來載入和管理工具欄的圖片。ImageList_Create可以建立一個圖片列表,它的原型為:
HIMAGELIST ImageList_Create(int cx, int cy, UINT flags, int cInitial, int cGrow);
該函式用法在MSDN上說的比較清楚。這個列表建立後並沒有圖片,只是一個列表管理的容器,還需要載入圖片集。加入圖片所需API如下:
int ImageList_AddMasked(HIMAGELIST himl, HBITMAP hbmImage, COLORREF crMask);
圖片載入後,還可以設定圖片的顯示屬性,包括顯示圖片、文字以及資訊提示功能。工具按鈕的響應是通過向視窗處理程式傳送WM_COMMAND訊息實現的,有使用者處理按鈕事件。工具欄的提示資訊是通過WM_NOTIFY訊息,由使用者設定,工具欄的建立例項請參看後面的應用例項。
- 建立狀態列
裝具狀態列相比工具欄要簡單很多,用CreateWindowEx建立狀態列後,預設情況下,狀態列只有一個顯示面板(panel),要建立多個面板,只要向狀態列把配置好個面板的長度傳送SB_SETPARTS訊息即可,訊息引數分別是面板個數和個面板的終點陣列。比如:
int array[3]={120,120*2,-1};
SendMessage(hWndStatus,SB_SETPARTS,(WPARAM)3,(LPARAM)array);
表示將狀態列面板分為3各部分,第一部分到120畫素為止,第二部分到240畫素為止,剩下的(-1)全部分到第三部分中。
設定面板文字內容,可以給狀態列傳送SB_SETTEXT訊息;要在狀態列面板中增加小圖示,可以給狀態傳送SB_SETICON訊息。
上面的工具欄和狀態列只是介紹了最基本的情況,更多的設定和訊息處理都可以參考MSDN。本系列專注在基本程式設計和使用上,只是介紹常見的用法,不過這些用法也夠初學者程式設計使用。
- 使用例項
下面我們通過一個基本的例項程式說明如何採用純Windows API建立、使用工具欄和狀態列。程式中建立的Toolbar有三個圖示,當程式正常顯示,滑鼠在某個工具欄按鈕上移動,或者處於Disable狀態是,會有不同的圖片顯示,讀者可以自己更換其他自己喜歡的圖示。
#include <windows.h>
#include <commctrl.h>
#include <tchar.h>
#pragma comment(lib, "comctl32.lib")
// Windows XP sytle button
#pragma comment(linker,""/manifestdependency:type='win32' "
"name='Microsoft.Windows.Common-Controls' "
"version='6.0.0.0' processorArchitecture='*' "
"publicKeyToken='6595b64144ccf1df' language='*'"")
#define IDC_TOOLBAR 1001
#define IDC_STATUSBAR 1002
//#define PIC_RESOURCE_USED
#ifdef PIC_RESOURCE_USED
#define IDB_NEW 110
#define IDB_OPEN 111
#define IDB_SAVE 112
#endif
#define ID_FOPEN 1111
#define ID_FCLOSE 1112
#define ID_FSAVE 1113
static TCHAR szAppName[] = TEXT("toolbar");
static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox (NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
//初始化公共空間
INITCOMMONCONTROLSEX icc;
icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
icc.dwICC = ICC_BAR_CLASSES;
InitCommonControlsEx(&icc);
hWnd = CreateWindow(szAppName,szAppName,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,400,300,NULL,NULL,hInstance,NULL);
ShowWindow(hWnd, iCmdShow);
UpdateWindow(hWnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
HWND CreateToolbar(HWND hParentWnd)
{
HWND hWndTB;
TBBUTTON tbb[3];
HIMAGELIST hImageList,hHotImageList,hDisableImageList;
HBITMAP hBitmap;
HINSTANCE hInst = GetModuleHandle(NULL);
//建立Toolbar控制元件
hWndTB = CreateWindowEx(0, TOOLBARCLASSNAME,TEXT(""),WS_CHILD|WS_VISIBLE|WS_BORDER|TBSTYLE_LIST|TBSTYLE_AUTOSIZE|TBSTYLE_TOOLTIPS,0,0,0,0,hParentWnd,(HMENU)IDC_TOOLBAR,hInst,NULL);
if(!hWndTB)
{
return NULL;
}
SendMessage(hWndTB, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
//下面建立三組24x24畫素大小的點陣圖影象列表,用於工具欄圖示
hImageList = ImageList_Create(24,24,ILC_COLOR24|ILC_MASK,3,1);
#ifdef PIC_RESOURCE_USED
hBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_COLOR3));
#else
hBitmap = (HBITMAP)LoadImage(NULL, TEXT("color24x3.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION); //載入一組圖片
#endif
ImageList_AddMasked(hImageList, hBitmap, RGB(255,255,255));
DeleteObject (hBitmap);
SendMessage(hWndTB,TB_SETIMAGELIST,0,(LPARAM)hImageList); //正常顯示時的影象列表
hHotImageList = ImageList_Create(24,24,ILC_COLOR24|ILC_MASK,3,1);
#ifdef PIC_RESOURCE_USED
hBitmap = LoadBitmap(hInst,MAKEINTRESOURCE(IDB_GREEN3));
#else
hBitmap = (HBITMAP)LoadImage(NULL, TEXT("green24x3.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
#endif
ImageList_AddMasked(hHotImageList,hBitmap, RGB(255,255,255));
DeleteObject (hBitmap);
SendMessage(hWndTB,TB_SETHOTIMAGELIST,0,(LPARAM)hHotImageList); //滑鼠懸浮時的影象列表
hDisableImageList = ImageList_Create(24,24,ILC_COLOR24|ILC_MASK,3,1);
#ifdef PIC_RESOURCE_USED
hBitmap = LoadBitmap(hInst,MAKEINTRESOURCE(IDB_GRAY3));
#else
hBitmap = (HBITMAP)LoadImage(NULL, TEXT("gray24x3.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
#endif
ImageList_AddMasked(hDisableImageList,hBitmap, RGB(255,255,255));
DeleteObject (hBitmap);
SendMessage(hWndTB,TB_SETDISABLEDIMAGELIST,0,(LPARAM)hDisableImageList); //當工具欄button失能是的影象列表
ZeroMemory(tbb, sizeof(tbb));
tbb[0].iBitmap =MAKELONG(0,0) ;
tbb[0].fsState = TBSTATE_ENABLED;
tbb[0].fsStyle = TBSTYLE_BUTTON|BTNS_AUTOSIZE;
tbb[0].idCommand = ID_FOPEN;
tbb[0].iString = (INT_PTR)TEXT("開啟");
tbb[1].iBitmap =MAKELONG(1,0);
tbb[1].fsState = TBSTATE_ENABLED;
tbb[1].fsStyle = TBSTYLE_BUTTON|BTNS_AUTOSIZE;
tbb[1].idCommand = ID_FCLOSE;
tbb[1].iString = (INT_PTR)TEXT("關閉");
tbb[2].iBitmap =MAKELONG(2,0);
tbb[2].fsState = TBSTATE_ENABLED;
tbb[2].fsStyle = TBSTYLE_BUTTON|BTNS_AUTOSIZE;
tbb[2].idCommand = ID_FSAVE;
tbb[2].iString = (INT_PTR)TEXT("儲存");
SendMessage(hWndTB, TB_ADDBUTTONS, sizeof(tbb)/sizeof(TBBUTTON), (LPARAM)&tbb); //配置工具欄按鈕資訊
SendMessage(hWndTB,WM_SIZE,0,0);
return hWndTB;
}
HWND CreateStatusBar(HWND hParentWnd)
{
#define PANEL_NUM 3
int array[PANEL_NUM]={120,120*2,-1};
HINSTANCE hInst = GetModuleHandle(NULL);
//建立Statusbar控制元件
HWND hWndStatus = CreateWindowEx(0, STATUSCLASSNAME, TEXT(""), WS_CHILD|WS_BORDER|WS_VISIBLE, 0, 0, 0, 0, hParentWnd, (HMENU)IDC_STATUSBAR, hInst, NULL);
if (hWndStatus)
{
SendMessage(hWndStatus,SB_SETPARTS,(WPARAM)PANEL_NUM,(LPARAM)array); //設定面板個數
SendMessage(hWndStatus,SB_SETTEXT,(LPARAM)1,(WPARAM)TEXT("panel-1")); //設定第二個面板內容
SendMessage(hWndStatus,SB_SETTEXT,(LPARAM)2,(WPARAM)TEXT("panel-2")); //設定第三個面板內容
}
#undef PANEL_NUM
return hWndStatus;
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
static HWND hToolbar;
static HWND hStatusbar;
switch (message)
{
case WM_CREATE:
hToolbar = CreateToolbar(hWnd);
hStatusbar = CreateStatusBar(hWnd);
return 0;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
int wmEvent = HIWORD(wParam);
// 分析選單選擇:
switch (wmId)
{
case ID_FOPEN:
SendMessage(hToolbar, TB_ENABLEBUTTON, (WPARAM)ID_FOPEN, (LPARAM)MAKELONG(FALSE,0));
SendMessage(hToolbar, TB_ENABLEBUTTON, (WPARAM)ID_FSAVE, (LPARAM)MAKELONG(TRUE,0));
break;
case ID_FSAVE:
SendMessage(hToolbar, TB_ENABLEBUTTON, (WPARAM)ID_FSAVE, (LPARAM)MAKELONG(FALSE,0));
SendMessage(hToolbar, TB_ENABLEBUTTON, (WPARAM)ID_FOPEN, (LPARAM)MAKELONG(TRUE,0));
break;
case ID_FCLOSE:
MessageBox(hWnd, TEXT("click!"), TEXT("hint"), MB_OK);
break;
}
}
return 0;
case WM_NOTIFY:
{
LPNMHDR lpnmhdr=(LPNMHDR)lParam;
LPTOOLTIPTEXT lpttext;
if(lpnmhdr->code==TTN_GETDISPINFO)
{
//處理滑鼠在工具欄上懸浮移動時的文字提示
lpttext=(LPTOOLTIPTEXT)lParam;
switch(lpttext->hdr.idFrom)
{
case ID_FOPEN:
lpttext->lpszText=TEXT("開啟檔案");
break;
case ID_FCLOSE:
lpttext->lpszText=TEXT("關閉檔案");
break;
case ID_FSAVE:
lpttext->lpszText=TEXT("儲存為檔案");
break;
}
}
}
return 0;
case WM_SIZE:
{
SendMessage(hToolbar, TB_AUTOSIZE, 0, 0);
SendMessage(hStatusbar, WM_SIZE, 0, 0);
}
return 0;
case WM_MOUSEMOVE:
{
TCHAR szBuf[MAX_PATH];
_stprintf(szBuf,TEXT("Mouse(%d,%d)"),LOWORD(lParam),HIWORD(lParam));
SendMessage(hStatusbar, SB_SETTEXT, 0, (LPARAM)(LPSTR)szBuf);
}
return 0;
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
;
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0 ;
}
return DefWindowProc (hWnd, message, wParam, lParam);
}
該例項程式後,結果顯示如下圖:
該程式的工具欄按下“開啟”按鈕後,自己後變成Disable狀態,點選“儲存”後,“開啟”按鈕會再次啟用。可以看到,在滑鼠移到“儲存”按鈕上時,出現提示“儲存為檔案”的文字提示。
狀態列一共有三個面板,後面兩個在建立時靜態賦字串,第一個則實時捕獲滑鼠在客戶區中的座標位置並顯示出來。
總體來說工具欄和狀態列的基本用法還是比較簡單,只是由於這兩個控制元件屬於微軟的通用控制元件,建立之前需要呼叫InitCommonControlsEx初始化通用控制元件庫並設定需要使用的控制元件。本篇就寫到這裡,感興趣的讀者請繼續關注Windows程式設計基礎系列的後續文章。