7.3 通用控件
7.3.1 通用控件簡介
常見的通用控件有狀態(tài)欄、工具欄、列表視圖、樹型視圖、進(jìn)度條、滾動(dòng)條等,它們是增強(qiáng)型子窗口控件。由于數(shù)目較多,全部裝載到內(nèi)存并注冊(cè)是非常浪費(fèi)資源的,所以默認(rèn)情況下應(yīng)用程序并不加載它們。除了Richedit控件外,所有通用控件的可執(zhí)行代碼都在comctl32.dll庫中,應(yīng)用程序要使用通用控件必須首先加載comctl32.dll庫。因?yàn)镽ichedit控件非常復(fù)雜,所以它有自己的DLL——Riched20.dll。
在應(yīng)用程序中加載comctl32.dll庫的函數(shù)是InitCommonControls,它也是comctl32.dll模塊中的函數(shù),聲明在commctrl.h頭文件中。Richedit控件有所不同,要使用它必須顯式地使用LoadLibrary函數(shù)加載Riched20.dll庫。
創(chuàng)建通用控件的方法有兩種,一種是使用資源編譯器把它們放到對(duì)話框中,另一種是自己寫創(chuàng)建代碼。幾乎所有的通用控件都可以通過調(diào)用CreateWindowEx來創(chuàng)建,只要傳遞相應(yīng)的通用控件類名即可。一些通用控件有特殊的創(chuàng)建函數(shù),但是這些函數(shù)僅是對(duì)CreateWindowEx的封裝,以使創(chuàng)建過程更容易。這類函數(shù)有:
CreateToolbarEx 創(chuàng)建工具欄 CreateStatusWindow 創(chuàng)建狀態(tài)欄 CreatePropertySheetPage 創(chuàng)建屬性頁 PropertySheet 創(chuàng)建屬性表格 ImageList_Create 創(chuàng)建圖像列表為了創(chuàng)建通用控件必須要知道它們的類名,表7.2總結(jié)了comctl32.dll庫中的通用控件。
通用控件可以有通用的窗口風(fēng)格,如WS_CHILD等。它們也可以有自己獨(dú)特的風(fēng)格,如樹型視圖控件有TVS_XXXXX風(fēng)格,列表控件有LVS_XXXX風(fēng)格等。
通用控件可以有通用的窗口風(fēng)格,如WS_CHILD等。它們也可以有自己獨(dú)特的風(fēng)格,如樹型視圖控件有TVS_XXXXX風(fēng)格,列表控件有LVS_XXXX風(fēng)格等。
7.3.2 使用通用控件
為了示例通用控件的用法,筆者先編寫了一個(gè)簡單的進(jìn)程管理器程序。它會(huì)在一個(gè)列表視圖控件中列出系統(tǒng)內(nèi)當(dāng)前活動(dòng)的進(jìn)程,當(dāng)用戶雙擊某一項(xiàng)時(shí),會(huì)彈出一個(gè)對(duì)話框,詢問是否要終止這個(gè)進(jìn)程的運(yùn)行。
這個(gè)程序示例了列表視圖和狀態(tài)欄兩個(gè)通用控件的用法,它們的源代碼在配套光盤的07ComctlDemo工程下。下面是ComctlDemo.cpp文件中的內(nèi)容。
// ComctlDemo.cpp文件 #include <windows.h> #include <commctrl.h> #include <tlhelp32.h>#include "resource.h"// 鏈接到comctl32.lib庫 #pragma comment(lib,"comctl32.lib")// 狀態(tài)欄ID號(hào) #define IDC_STATUS 101BOOL __stdcall DlgProc(HWND, UINT, WPARAM, LPARAM); void UpdateProcess(HWND hWndList);int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int) {// 初始化Comctl32.dll庫::InitCommonControls();::DialogBoxParam(hInstance, (LPCTSTR)IDD_MAIN, NULL, DlgProc, NULL); return 0; }BOOL __stdcall DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {switch(message){ case WM_INITDIALOG:{// 初始化列表視圖控件HWND hWndList = ::GetDlgItem(hDlg, IDC_LIST);// 設(shè)置它的擴(kuò)展風(fēng)格::SendMessage(hWndList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);LVCOLUMN column;// 指定LVCOLUMN結(jié)構(gòu)中的pszText、fmt、cx域有效column.mask = LVCF_TEXT|LVCF_FMT|LVCF_WIDTH; // 設(shè)置有效的域的屬性column.fmt = LVCFMT_CENTER; // 指定文本居中顯示column.cx = 100; // 指定此欄的寬度column.pszText = "映象名稱"; // 指定此欄顯示的文本// 添加一個(gè)新的專欄::SendMessage(hWndList, LVM_INSERTCOLUMN, 0, (LPARAM)&column);// 再添加一個(gè)專欄column.pszText = "PID";column.cx = 50;::SendMessage(hWndList, LVM_INSERTCOLUMN, 1, (LPARAM)&column);// 初始化狀態(tài)欄// 創(chuàng)建狀態(tài)欄HWND hWndStatus = ::CreateStatusWindow(WS_CHILD|WS_VISIBLE|SBS_SIZEGRIP, NULL, hDlg, IDC_STATUS);// 設(shè)置背景色::SendMessage(hWndStatus, SB_SETBKCOLOR, 0, RGB(0xa6, 0xca, 0xf0));// 給狀態(tài)欄分欄int pInt[] = { 152, -1 };::SendMessage(hWndStatus, SB_SETPARTS, 2, (long)pInt);// 設(shè)置各欄的文本::SendMessage(hWndStatus, SB_SETTEXT, 0, (long)" 準(zhǔn)備就緒");::SendMessage(hWndStatus, SB_SETTEXT, 1, (long)" Windows程序設(shè)計(jì)進(jìn)階之路!");// 刷新進(jìn)程列表UpdateProcess(hWndList);}break;case WM_COMMAND:switch(LOWORD(wParam)){case IDOK:::EndDialog(hDlg, IDOK);break;case IDCANCEL:::EndDialog(hDlg, IDCANCEL);break;case IDC_UPDATE:UpdateProcess(::GetDlgItem(hDlg, IDC_LIST));break;}break;case WM_NOTIFY:{if(wParam == IDC_LIST){NMHDR* pHeader = (NMHDR*)lParam;HWND hWndList = pHeader->hwndFrom;if(pHeader->code == NM_DBLCLK) // 雙擊事件{NMLISTVIEW* pNMListView = (NMLISTVIEW*)pHeader;// 用戶雙擊的項(xiàng)號(hào)int nIndex = pNMListView->iItem;// 取得進(jìn)程ID號(hào)char szID[56];LVITEM lvi;memset(&lvi, 0, sizeof(LVITEM));lvi.iSubItem = 1; // nIndex項(xiàng)目中的第1個(gè)子項(xiàng)lvi.cchTextMax = 56;lvi.pszText = szID;::SendMessage(hWndList, LVM_GETITEMTEXT, (WPARAM)nIndex, (long)&lvi);// 詢問用戶if(::MessageBox(hDlg, "確實(shí)要終止進(jìn)程嗎?", "07ComctlDemo", MB_OKCANCEL|MB_DEFBUTTON2) == IDCANCEL)return 0;// 試圖打開目標(biāo)進(jìn)程,終止它HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE, FALSE, atoi(szID));if(hProcess != NULL){HWND hWndStatus = ::GetDlgItem(hDlg, IDC_STATUS);if(::TerminateProcess(hProcess, 0)){::SendMessage(hWndStatus, SB_SETTEXT, 0, (long)"終止進(jìn)程成功!");UpdateProcess(hWndList);}else{::SendMessage(hWndStatus, SB_SETTEXT, 0, (long)"終止進(jìn)程失敗!");}}}}}break;}return 0; }void UpdateProcess(HWND hWndList) {// 刪除所有的項(xiàng)::SendMessage(hWndList, LVM_DELETEALLITEMS, 0, 0);int nItem = 0; // 項(xiàng)計(jì)數(shù)PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) }; HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if(hProcessSnap == INVALID_HANDLE_VALUE) return; if(Process32First(hProcessSnap, &pe32)) { do { // 取得進(jìn)程ID號(hào)char szID[56];wsprintf(szID, "%u", pe32.th32ProcessID);// 插入一個(gè)項(xiàng)LVITEM item = { 0 };item.iItem = nItem;item.mask = LVIF_TEXT; // 指定pszText域有效item.pszText = (LPTSTR)pe32.szExeFile; // 設(shè)置文本::SendMessage(hWndList, LVM_INSERTITEM, 0, (long)&item);// 設(shè)置新項(xiàng)的文本LVITEM lvi;lvi.iSubItem = 1; // 指定要設(shè)置第1個(gè)專欄的文本lvi.pszText = (LPTSTR)szID; // 要設(shè)置的文本::SendMessage(hWndList, LVM_SETITEMTEXT, nItem, (LPARAM)&lvi);nItem++;} while(Process32Next(hProcessSnap, &pe32)); }::CloseHandle(hProcessSnap); }
通用控件是從Comctl32.dll模塊導(dǎo)出的,相關(guān)定義文件是commctrl.h,其靜態(tài)鏈接庫為comctl32.lib。VC++在默認(rèn)情況下并未加載此庫,在使用通用控件前必須把comctl32.lib加入到工程中,最簡單的方法就是在文件中使用下面的命令。
也可以通過菜單命令“Project/Settings”打開Project Settings對(duì)話框,切換到Link選項(xiàng)卡,選擇Category組合框中的Input,在Object/library modules窗口下加入comctl32.lib。
在調(diào)用任何通用控件庫中的函數(shù)前,請(qǐng)首先調(diào)用InitCommonControls(或InitCommonControlsEx)函數(shù),它負(fù)責(zé)注冊(cè)和初始化通用控件的窗口類。
7.3.3 使用狀態(tài)欄
1.創(chuàng)建狀態(tài)欄
狀態(tài)欄一般位于主窗口的底部,用于顯示程序運(yùn)行中的狀態(tài)信息。可以使用CreateWindowEx函數(shù)來創(chuàng)建它,也可以用CreateStatusWindow函數(shù)創(chuàng)建。
2.將狀態(tài)欄分欄
狀態(tài)欄在剛創(chuàng)建時(shí)只有一欄,為了顯示不同種類的信息,有時(shí)需要將狀態(tài)欄分為多個(gè)欄目,可以通過向狀態(tài)欄發(fā)送SB_SETPARTS消息將它分欄。
SB_SETPARTS消息的wParam參數(shù)指定要分欄的數(shù)量,lParam參數(shù)指向一個(gè)整型數(shù)組,指定了每個(gè)欄的寬度,-1說明最后一欄占據(jù)了剩下的所有寬度。
3.設(shè)置狀態(tài)欄文本
通過向狀態(tài)欄發(fā)送SB_SETTEXT消息可以將字符串顯示到指定的分欄中。
uType可以指定為以下的值:
SBT_NOBORDERS 顯示的文本不帶邊框 SBT_OWNERDRAW 分欄由用戶自己繪畫 SBT_POPOUT 使分欄以凸的形狀顯示程序也可以通過發(fā)送SB_GETTEXT消息來獲取某個(gè)分欄的文本。
::SendMessage (hWndStatus, SB_GETTEXT, iPart, (long)lpsz);iPart參數(shù)指定要獲取的分欄的編號(hào),lpsz指向一個(gè)緩沖區(qū),用來接收返回的字符串。消息的返回值是設(shè)置文字時(shí)使用的uType。在發(fā)送SB_GETTEXT消息之前,也可以通過發(fā)送 SB_GETTEXTLENGTH消息來獲得分欄中文本字符串的長度。
4.移動(dòng)狀態(tài)欄
為了使?fàn)顟B(tài)欄的大小隨著主窗口大小的改變而改變,需要用下面的代碼處理WM_SIZE消息。
程序并不需要指定有效的位置和尺寸,因?yàn)闋顟B(tài)欄控件有自動(dòng)調(diào)整大小能力。只要在父窗口改變大小時(shí)通知它就可以了。
5.設(shè)置背景色
創(chuàng)建狀態(tài)欄控件后,使用下面的代碼可以設(shè)置其背景色。
7.3.4 使用列表視圖
1.設(shè)置列表視圖的顯示風(fēng)格
列表視圖控件就是控件選擇窗口中的List Control控件。當(dāng)把這個(gè)控件添加到對(duì)話框中以后,為了取得像例子一樣的運(yùn)行效果(報(bào)告方式),還要設(shè)置它的屬性。雙擊這個(gè)控件,彈出的List Control Properties對(duì)話框,切換到Styles選項(xiàng)卡,更改View風(fēng)格為Report(默認(rèn)情況下是Icon)。
2.將列表視圖分欄
列表視圖控件首先將它所占用的空間在水平方向上分成若干個(gè)專欄(column),每個(gè)欄的屬性用LVCOLUMN結(jié)構(gòu)來描述。當(dāng)要添加新專欄的時(shí)候,要先恰當(dāng)?shù)卦O(shè)置此結(jié)構(gòu)中各域的值,然后向列表視圖窗口發(fā)送LVM_INSERTCOLUMN消息,消息的lParam參數(shù)指定了LVCOLUMN結(jié)構(gòu)地址,wParam參數(shù)指定了要添加的專欄號(hào),如下代碼所示。
LVCOLUMN 結(jié)構(gòu)中的mask是一組標(biāo)志位,它指示了該結(jié)構(gòu)體中哪些成員變量是有效的。只有指定了一個(gè)成員有效,Windows在收到LVM_INSERTCOLUMN消息后才會(huì)去查看這個(gè)成員的值。
3.在列表視圖中添加項(xiàng)
有了專欄以后,列表視圖控件又在垂直方向上分了許多的項(xiàng),每一行是一項(xiàng),其屬性用LVITEM結(jié)構(gòu)描述。LVM_INSERTITEM消息用來向列表視圖中插入新項(xiàng),消息的wParam參數(shù)指定了要添加項(xiàng)的序號(hào),lParam參數(shù)指定了LVITEM結(jié)構(gòu)的首地址。下面是程序中添加新項(xiàng)的代碼。
4.設(shè)置項(xiàng)文本
添加新項(xiàng)之后還要設(shè)置該項(xiàng)中各子項(xiàng)的文本,可以使用LVM_SETITEMTEXT消息實(shí)現(xiàn)。
5.響應(yīng)列表視圖消息
當(dāng)有事件發(fā)生時(shí)通用控件向父窗口發(fā)送WM_NOTIFY消息,而不是WM_COMMAND消息。消息的wParam參數(shù)指定了控件的ID號(hào),lParam參數(shù)是一個(gè)指向NMHDR結(jié)構(gòu)的指針。
對(duì)于通知消息來說,這個(gè)參數(shù)指向了一個(gè)更大的結(jié)構(gòu),NMHDR結(jié)構(gòu)是這個(gè)更大結(jié)構(gòu)的第一個(gè)成員。列表視圖控件就是這樣的,lParam參數(shù)指向NMLISTVIEW結(jié)構(gòu)。程序接收到WM_NOTIFY消息之后,查看wParam參數(shù)記錄的控件ID號(hào),如果是列表視圖控件發(fā)來的就通過以下代碼處理它。
NMHDR* pHeader = (NMHDR*)lParam; HWND hWndList = pHeader->hwndFrom; if(pHeader->code == NM_DBLCLK) // 雙擊事件 { NMLISTVIEW* pNMListView = (NMLISTVIEW*)pHeader; // 用戶雙擊的項(xiàng)序號(hào) int nIndex = pNMListView->iItem; // 取得nIndex項(xiàng)中記錄的進(jìn)程ID號(hào) …… }NMHDR結(jié)構(gòu)中code的值是NM_DBLCLK,說明這是一個(gè)雙擊事件,此時(shí)NMLISTVIEW結(jié)構(gòu)中的iItem域記錄了用戶雙擊的項(xiàng)序號(hào),這樣就可以取得進(jìn)程ID號(hào)了。
7.3.5 使用進(jìn)度條
進(jìn)度條控件(Progress Bar)的作用是顯示程序的進(jìn)度,常常用作數(shù)據(jù)讀寫、文件拷貝和磁盤格式化等長時(shí)間操作時(shí)的進(jìn)度提示。下面的小例子說明了進(jìn)度條控件的用法,其源代碼在07ProgressDemo工程下。用戶單擊前進(jìn)按鈕,進(jìn)度條進(jìn)度將會(huì)增加。
// ProgressDemo.cpp文件 #include <windows.h> #include <commctrl.h> #include "resource.h"// 鏈接到comctl32.lib庫 #pragma comment(lib,"comctl32.lib")BOOL __stdcall DlgProc(HWND, UINT, WPARAM, LPARAM);int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int) {::InitCommonControls();::DialogBoxParam(hInstance, // 實(shí)例句柄(LPCTSTR)IDD_MAIN, // 對(duì)話框資源ID號(hào)NULL, // 父窗口句柄DlgProc, // 消息處理函數(shù)NULL); // 對(duì)話框初始化的值,在WM_INITDIALOG消息的lParam參數(shù)中取出return 0; }BOOL __stdcall DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {switch(message){ case WM_INITDIALOG:{// 初始化進(jìn)度條控件HWND hWndProgress = ::GetDlgItem(hDlg, IDC_PROGRESS);// 設(shè)置進(jìn)度條的取值范圍::SendMessage(hWndProgress, PBM_SETRANGE, 0, MAKELPARAM(0, 20));// 設(shè)置步長::SendMessage(hWndProgress, PBM_SETSTEP, 1, 0);// 設(shè)置背景色::SendMessage(hWndProgress, PBM_SETBKCOLOR, 0, RGB(0, 0, 0xff));// 設(shè)置進(jìn)度條的顏色::SendMessage(hWndProgress, PBM_SETBARCOLOR, 0, RGB(0xff, 0, 0));}break;case WM_COMMAND:switch(LOWORD(wParam)){case IDOK:// 增加進(jìn)度條進(jìn)度::SendDlgItemMessage(hDlg, IDC_PROGRESS, PBM_STEPIT, 0, 0);break;case IDCANCEL:::EndDialog (hDlg, IDCANCEL);break;}break;}return 0; }
進(jìn)度條控件的使用方法非常簡單,程序首先向進(jìn)度條發(fā)送PBM_SETRANGE消息設(shè)置它的取值范圍,其中的0和20分別指定了最小值和最大值;然后發(fā)送PBM_SETSTEP消息設(shè)置步長,即進(jìn)度條每一步移動(dòng)的長度;最后是發(fā)送IDC_PROGRESS消息增加進(jìn)度條進(jìn)度。這個(gè)程序?qū)⑦M(jìn)度條的取值范圍設(shè)為了0~20,步長設(shè)為1,這樣只要單擊20次前進(jìn)按鈕,進(jìn)度條就移動(dòng)到頭了。
另外,還可以發(fā)送PBM_SETPOS消息來設(shè)置進(jìn)度條當(dāng)前的位置,消息的wParam參數(shù)包含了要設(shè)置的值。可以通過發(fā)送PBM_GETPOS消息取得進(jìn)度條當(dāng)前的位置。
總結(jié)
- 上一篇: 甲骨文终获Java编程语言版权
- 下一篇: Vulkan Samples 阅读 --