今天,咱們還是接著玩“控件斗地主”,這是我原創的超級游戲,有益身心健康,玩一朝,十年少。
哦,對,腦細胞極速運動了一下,想起了一個問題,這個破問題雖然網上有很多種解決方案,但是,并沒有讓所有人都解決問題。
不知道大家有沒有調用過LoadIconMetric函數,這個函數在靜態庫Comctl32.lib中有定義(當然,動態庫也有),不過,創建項目的時候,默認并沒有引用它的,于是,大家知道,解決調用的方法就是在代碼中加上:
~~~
#pragma comment(lib, "Comctl32.lib")
~~~
我一般習慣這種方法,這樣不必去修改項目屬性。但是,很多朋友說過,在Win 7以后的系統,依然沒有成功,我也嘗到了調用失敗的“甜頭”,我一直在想,這是為什么呢?
于是,我又試了另一種方法,就是用LoadLibrary加載Comctl32.dll,然后通過函數指針去調用它:
~~~
typedef LRESULT (WINAPI * pLoadICMT)(.......);
~~~
但結果還是沒成功,GetProcAddress返回的地址為0,又一次嘗到了失敗帶來的“刺激”感。
直到某一天,我在寫某程序時,從上一文中大家都看到,那個按鈕的視覺風格和Win9x/2000差不多,似乎沒有XP那種充滿美學水準的效果。其實,這是因為我們的程序沒有啟用視覺效果,默認情況下,使用版本5中的控件,而要有XP以上的風格,是在版本6的控件內庫中才有。
當然方法可以很多人都知道,就是定義一個用于視覺效果的清單文件,本質是XML格式。不過我用的開發工具是VS 2005之后的版本,就不用弄個XML文件那么麻煩了,直接到MSDN上復制粘貼這段代碼放到你的代碼文件(.cpp)中,就是這個,直接抄過來就行了,適當的時候,要巧用MSDN上的資源。
~~~
// 開啟視覺效果 Copy from MSDN
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
~~~
version是版本號,要有好看的效果,記得要6,不要寫用低版本的。processorArchitecture是處理器平臺,x86或amd64,用*號最好,通殺。
真是巧啊,原來這么一來,噗,LoadIconMetric也能調用了。總的來說,就是在代碼文件加上以下內容:
~~~
#include <CommCtrl.h> //包含頭文件
// 導入靜態庫
#pragma comment(lib, "Comctl32.lib")
// 開啟視覺效果 Copy from MSDN
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
~~~
這問題干掉了,開始今天的吹牛大行動。
單選按鈕,是的,在WinForm里面你肯定知道,RadioButto,復選框就是CheckBox。不過那個時候.NET還沒那么猛,那個時代,就是玩VB6,所以我知道VB里面,單選按鈕叫Option吧。
然后找遍了Win32的控件庫,怎么沒見Radio和CheckBox,于是,我陷入了萬分痛苦之中。不久后閱讀MSDN文檔,我就明白了,其實這兩個玩意兒都是BUTTON類的,只是應用了不同的style罷了。
好的,咱們先來弄一個單選的吧。
~~~
case WM_CREATE:
{
CreateWindow(L"Button",L"這玩意兒好",
BS_RADIOBUTTON | WS_CHILD | WS_VISIBLE,
10,10,150,28,
hwnd, (HMENU)IDC_RADBTN1,
(HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE),
NULL);
}
return 0;
~~~
應用BS_RADIOBUTTON樣式,就可以使按鈕變成單選按鈕,不過,別忘了WS_CHILD | WS_VISIBLE。
運行一下。

不過,當你點擊它的時候,會發現它并不會選中,那是因為BS_RADIOBUTTON樣式不會自動讓它選上。我們換一種可以自動處理的樣式,帶AUTO打頭的。
就是用 BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE,如下圖的預覽結果。

接下來,我們多創建幾個吧。
~~~
/*-----------------------------------------------------------------*/
//控件ID
#define IDC_RADBTN1 50001
#define IDC_RADBTN2 50002
#define IDC_RADBTN3 50003
#define IDC_RADBTNBLUE 51001
#define IDC_RADBTNRED 51002
#define IDC_RADBTNGREEN 51003
/*-----------------------------------------------------------------*/
// 消息處理函數
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
{
// 獲取當前實例句柄
HINSTANCE hthisapp = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
// 縱坐標,控件將以此作為基準,
// 排列時依次增加
int yLoc = 0;
// 用來顯示文本
yLoc += 10;
CreateWindow(L"Static",L"請問你的性別是:",
SS_SIMPLE | WS_CHILD | WS_VISIBLE,
10,yLoc,160,18,
hwnd, NULL,
hthisapp,
NULL);
// 第一組單選按鈕
yLoc += 22;
CreateWindow(L"Button",L"男",
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | WS_GROUP,
12, yLoc, 60, 16,
hwnd,
(HMENU)IDC_RADBTN1,
hthisapp,NULL);
yLoc += 20;
CreateWindow(L"Button",L"女",
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
12,yLoc, 60, 16,
hwnd,(HMENU)IDC_RADBTN2,hthisapp,NULL);
yLoc += 20;
CreateWindow(L"Button",L"人妖",WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
12,yLoc,60,16,hwnd,(HMENU)IDC_RADBTN3,hthisapp,NULL);
// 顯示文本
yLoc += 38;
CreateWindow(L"Static",L"你喜歡哪一種顏色?",
WS_CHILD | WS_VISIBLE | SS_SIMPLE,
10,yLoc,150,18,hwnd,NULL,hthisapp,NULL);
//第二組單選按鈕
yLoc += 22;
CreateWindow(L"Button",L"藍色",WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | WS_GROUP,
12,yLoc,60,16,hwnd,(HMENU)IDC_RADBTNBLUE,hthisapp,NULL);
yLoc += 20;
CreateWindow(L"Button",L"紅色",WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
12,yLoc,60,16,hwnd,(HMENU)IDC_RADBTNRED,hthisapp,NULL);
yLoc += 20;
CreateWindow(L"Button",L"綠色",WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
12,yLoc,60,16,hwnd,(HMENU)IDC_RADBTNGREEN,hthisapp,NULL);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0); //平安退出
return 0;
default:
return DefWindowProc(hwnd,msg,wParam,lParam);
}
return 0;
}
~~~
在創建單選框中,為什么有些加了WS_GROUP樣式,而有些未加呢?
其實是這樣的,既然單選按鈕是單選的,那么,任何一個單選按鈕都與其他的單選按鈕是互斥的關系。所以,在同一個容器(本例是同一個窗口)中就需要把單選按鈕進行分組。
同一組中的單選按鈕相互排斥,他的分組方法:
順序,以Tab鍵順序為參考,這個不用我介紹,你隨便打開一個窗口,然后多按幾下Tab鍵你就懂了,如果不懂,那你真的無可救藥了。
凡是設置了WS_GROUP的單選框做為一組中的首元素,隨后的所有單選按鈕都和它在同一組,直到下一個設置了WS_GROUP樣式的單選按鈕。用上面的例子來說吧。
性別一組中,第一個應用了WS_GROUP的是“男”,隨后的“女”和“人妖”都與“男”在同一組,因為后面一個“藍色”設置了WS_GROUP樣式。所以,
第一組為:男,女,人妖;
第二組為:藍色,紅色,綠色。
顯然,用了BS_AUTORADIOBUTTON后,系統就會自動處理選擇狀態了。

完整的代碼清單如下:
~~~
#include <Windows.h>
#include <WindowsX.h>
#include <CommCtrl.h> //包含頭文件
// 導入靜態庫
#pragma comment(lib, "Comctl32.lib")
// 開啟視覺效果 Copy from MSDN
#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// 先聲明一個WindowProc回調
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
// 入口點
int WINAPI wWinMain(HINSTANCE hTheApp, HINSTANCE hPrevApp, LPWSTR lpCmd, int nShow)
{
PCWSTR cn = L"My"; // 窗口名
PCWSTR tt = L"應用程序"; // 窗口標題
// 設計窗口類
WNDCLASS wc = { sizeof(WNDCLASS) };
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpfnWndProc = WindowProc;
wc.style = CS_HREDRAW | CS_VREDRAW;
LoadIconMetric(hTheApp, IDI_APPLICATION, LIM_SMALL, &wc.hIcon);
wc.lpszClassName = cn;
wc.hInstance = hTheApp;
RegisterClass(&wc); // 注冊窗口類
// 創建窗口
HWND hwnd = CreateWindow(cn, tt,WS_OVERLAPPEDWINDOW,
28,34,400,330,NULL,NULL,hTheApp,NULL);
if( !hwnd)
{ /* 如果窗口創建失敗,
那繼續執行也沒有意義
長痛不如短痛,結束吧。
*/
return 0;
}
ShowWindow(hwnd,nShow); //顯示窗口
UpdateWindow(hwnd); //更新窗口
// 消息循環
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg); //調度消息到WindowProc回調
}
return 0;
}
/*-----------------------------------------------------------------*/
//控件ID
#define IDC_RADBTN1 50001
#define IDC_RADBTN2 50002
#define IDC_RADBTN3 50003
#define IDC_RADBTNBLUE 51001
#define IDC_RADBTNRED 51002
#define IDC_RADBTNGREEN 51003
#define IDC_BTN_OK 1107 //確定按鈕ID
/*-----------------------------------------------------------------*/
// 消息處理函數
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
{
// 獲取當前實例句柄
HINSTANCE hthisapp = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
// 縱坐標,控件將以此作為基準,
// 排列時依次增加
int yLoc = 0;
// 用來顯示文本
yLoc += 10;
CreateWindow(L"Static",L"請問你的性別是:",
SS_SIMPLE | WS_CHILD | WS_VISIBLE,
10,yLoc,160,18,
hwnd, NULL,
hthisapp,
NULL);
// 第一組單選按鈕
yLoc += 22;
CreateWindow(L"Button",L"男",
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | WS_GROUP,
12, yLoc, 60, 16,
hwnd,
(HMENU)IDC_RADBTN1,
hthisapp,NULL);
yLoc += 20;
CreateWindow(L"Button",L"女",
WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
12,yLoc, 60, 16,
hwnd,(HMENU)IDC_RADBTN2,hthisapp,NULL);
yLoc += 20;
CreateWindow(L"Button",L"人妖",WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
12,yLoc,60,16,hwnd,(HMENU)IDC_RADBTN3,hthisapp,NULL);
// 顯示文本
yLoc += 38;
CreateWindow(L"Static",L"你喜歡哪一種顏色?",
WS_CHILD | WS_VISIBLE | SS_SIMPLE,
10,yLoc,150,18,hwnd,NULL,hthisapp,NULL);
//第二組單選按鈕
yLoc += 22;
CreateWindow(L"Button",L"藍色",WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | WS_GROUP,
12,yLoc,60,16,hwnd,(HMENU)IDC_RADBTNBLUE,hthisapp,NULL);
yLoc += 20;
CreateWindow(L"Button",L"紅色",WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
12,yLoc,60,16,hwnd,(HMENU)IDC_RADBTNRED,hthisapp,NULL);
yLoc += 20;
CreateWindow(L"Button",L"綠色",WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
12,yLoc,60,16,hwnd,(HMENU)IDC_RADBTNGREEN,hthisapp,NULL);
// 創建一個確定按鈕
CreateWindow(L"Button",L"確定",WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
230,180,80,27,hwnd,(HMENU)IDC_BTN_OK,hthisapp,NULL);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0); //平安退出
return 0;
default:
return DefWindowProc(hwnd,msg,wParam,lParam);
}
return 0;
}
~~~
- 前言
- (1):關于C++的幾個要點
- (2):完整的開發流程
- (3):窗口的重繪
- (4):創建菜單
- (5):具有單選標記的菜單
- (6):創建右鍵菜單
- (7):多邊形窗口
- (8):繪圖(A)
- (9):繪圖(B)
- (10):繪圖(C)
- (11):使用控件——先來耍一下按鈕
- (12):使用控件——單選按鈕
- (13):握手對話框
- (14):用對話框作為主窗口
- (15):ListView控件
- (16):ListView的多個視圖
- (17):啟動和結束進程
- (18):使用對話框的兩個技巧
- (19):瀏覽和打開文件
- (20):瀏覽文件夾
- (21):復制&amp;粘貼&amp;剪貼板操作
- (22):抓取屏幕
- (23):漸變顏色填充
- (24):計時器
- (25):監視剪貼板