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