之前介紹頂層視窗,這篇開始介紹各種GUI元件的建立方法和事件處理。Gtk把GUI元件稱為widget,但Windows API稱為control(控制項)。
各種GUI系統有很多共通部分,每個GUI系統在製作時都會參考現有的系統,做成大家已經習慣的方式比較容易讓人接受。像是check box是多個是非題,radio button是多選一,在每個系統上都是如此。
這是一些GUI系統的元件一覽,即使不是用這些系統寫程式也可以參考。
Gtk 4 Java Swing HTML <input> tag
按鈕雖然是比較簡單的control,但是本篇要介紹一些寫GUI程式必要的知識,篇幅比較長。
前篇:如何建一個視窗—Windows API篇
按鈕類Gtk篇
Shark的程式教學目錄
buttoncontrol.c
增加一個header:commctrl.h,linker參數增加一個library:comctl32.lib。這兩個檔名是common controls的縮寫。
還有windowsx.h,它裡面定義一些方便的macro。
首先按照「如何建一個視窗—Windows API篇」建好頂層視窗。在建立控制項之前呼叫InitCommonControlsEx()將comctl32.dll初始化。
InitCommonControlsEx()說明 struct INITCOMMONCONTROLSEX說明
參數是宣告一個struct再把它的指標傳入。struct第一個成員是struct的大小,這是Windows API常見的用法,因為新版的struct往往會追加項目,函式裡可以根據struct大小判斷版本做不同處理。第二個成員是要初始化哪些控制項,本篇只用到ICC_STANDARD_CLASSES。
之後的部分暫不解釋,先編譯出來看看,用這個指令
執行的樣子
可以看到三個問題
準備下面兩個純文字檔放在同一資料夾。
manifest.xml
manifest.rc
打這個指令編譯。
這是「Visual C++的命令列工具」提到的resource compiler。resource是Windows exe檔的特有功能,可以把資料嵌入exe檔。
會產生manifest.res,把它放在跟.c同一資料夾。
文件裡有提到其他方法,但我認為做成resource嵌入可執行檔最省事。
問題3要修改四個地方
linker參數增加gdi32.lib和manifest.res
這樣執行後,外觀跟現在的Windows版本一致了。
因為要用命令列輸出一些訊息,程式進入點用「int main()」讓執行時出現命令列視窗。
開始進入正題:按鈕控制項。微軟的官方文件中與本篇有關的:
控制項一覽 按鈕類control的圖和說明
由於有一些程式碼是所有按鈕共通的,寫成一個函式createButton()。
產生按鈕的方法跟產生頂層視窗一樣是CreateWindow(),傳回值也是HWND型態,參數如下:
1:WC_BUTTON macro。查commctrl.h會發現它被代換成一個字串L"Button",直接填L"Button"也可以(大小寫皆可)。
2:顯示在按鈕上的字。
3:window style,一些bit flag。因為這不是頂層視窗,必須有WS_CHILD,其他flag會決定這是哪一種按鈕。因為每個按鈕都需要WS_CHILD|WS_VISIBLE所以寫在createButton()裡面。
4~7:在視窗工作區裡的位置。
8:上層視窗的HWND。
9~11:此處沒用到,填NULL。
先不繼續講程式碼,先看看本篇程式裡的按鈕,把程式build出來操作看看。
CreateWindow()第三參數會決定按鈕種類,以下會說明style怎麼填。
Button Styles
其他button style就請查官方文件,大多是跟其他flag做or運算產生效果。
第二行SetWindowLongPtr()是設定一些整數屬性。GWLP_ID是設定ID,之後處理事件會有用。本篇在按鈕上把ID標示出來以方便辨認。
SetWindowLongPtrW()說明
(說明文件裡的函式名稱是SetWindowLongPtrW。之前說過,只要程式開頭有#define UNICODE,Windows API裡跟字串有關的函式和struct都會用Unicode的版本,反之會用ansi的版本。
LONG_PTR是跟該平臺指標一樣大小的整數,等同intptr_t。)
至於x,y,w,h參數,Windows底層API沒有排版的功能,只能自己指定坐標。本篇用一些常數:BUTTON_W、BUTTON_H、BUTTON_OFSX、BUTTON_OFSY方便計算。
Gtk就有排版功能了,可以根據文字長短、視窗大小自動調整元件的位置大小,以後寫Gtk的教學可能會介紹。
接下來解說Windows處理GUI事件的方式。
GUI是個樹狀結構,不只Windows,其他GUI函式庫也是這樣做。
頂層視窗處理事件的函式:WndProc()是我們自己寫的,按鈕也有各自的WndProc(),是Windows內建。
當滑鼠點按鈕的時候,按鈕本身收到滑鼠的message (就是「讀取鍵盤與滑鼠輸入(Windows)」裡介紹的東西),上層視窗則收到notification(通知)。
上層視窗的WndProc()要怎麼處理通知呢?在官方文件左邊的選單找到「Button Control Notifications」:
點開後按「BN_CLICKED」看說明:BN_CLICKED notification code
有這一段:
The parent window of the button receives this notification code through the WM_COMMAND message.
表示上層收到的是WM_COMMAND訊息。不過很多事件都會送出WM_COMMAND訊息,要根據notification code判斷是哪一種事件。
下面寫wparam的低位2 bytes是控制項ID,第3~4 bytes是notification code,可以用LOWORD()、HIWORD()這兩個macro取出。在WndProc()裡檢查message==WM_COMMAND以及HIWORD(wparam)==BN_CLICKED就可得知按鈕按下。
控制項ID是先前用SetWindowLongPtr()設定的值,可以藉此知道是哪個按鈕被按下。lparam是按鈕的hwnd。
再查看WM_COMMAND訊息的說明,裡面有寫「If an application processes this message, it should return zero.」,所以我們自己寫的WndProc()如果有處理這個事件要傳回0。
check box、radio button、3-state check box還需要知道目前狀態才能處理事件,但狀態沒有從wparam和lparam傳過來,要用Button_GetCheck()取得。它的說明要在左邊選單點開「Button Control Macros」找。
Button_GetCheck macro
裡面寫return None是錯的,實際上會傳回BST_CHECKED、BST_UNCHECKED、BST_INDETERMINATE的其中一個。
按split button右邊的箭頭則會送BCN_DROPDOWN通知,查文件可得知上層視窗收到WM_NOTIFY訊息,這個訊息的額外資訊不一樣,lparam是指向一個NMHDR struct的指標,事件資訊存在裡面。
BCN_DROPDOWN notification code WM_NOTIFY message NMHDR structure
一樣很多事件都會送WM_NOTIFY訊息,要根據nmhdr->code判斷是哪一種事件,nmhdr->idFrom判斷是哪個控制項送出,然後把lparam轉成其他型態取出事件相關資訊(本篇的例子是struct NMBCDROPDOWN)。
「按箭頭之後跳出選單」要寫在這個事件處理裡面。
最後介紹SendMessage()函式。
本篇用到兩個Button_開頭的macro。查Button_GetCheck()的說明,會發現裡面有提到BM_GETCHECK message。
打開windowsx.h找Button_GetCheck()的定義會發現它被代換成SendMessage()。
Button_SetNote()也是被代換成BCM_SETNOTE message。此外設定字型時也是用SendMessage()送出WM_SETFONT message。
Windows API裡取得控制項的資訊或是修改控制項屬性,很多時候是用SendMessage()。第一參數是控制項的hwnd,第二參數是一個整數代表要做哪種操作,第三和四參數是額外資訊,隨操作種類而異。
SendMessage()說明
例如BM_SETCHECK訊息可以改變check box或radio button的狀態,有興趣可以試試。
至於設定字型用的幾個函式,在此不詳細解釋。SystemParametersInfo()可以取得或修改一些系統資訊,CreateFontIndirect()和DeleteObject()跟Windows的一個系統:GDI有關。
SystemParametersInfoW()說明 CreateFontIndirectW()說明 DeleteObject()說明
各種GUI系統有很多共通部分,每個GUI系統在製作時都會參考現有的系統,做成大家已經習慣的方式比較容易讓人接受。像是check box是多個是非題,radio button是多選一,在每個系統上都是如此。
這是一些GUI系統的元件一覽,即使不是用這些系統寫程式也可以參考。
Gtk 4 Java Swing HTML <input> tag
按鈕雖然是比較簡單的control,但是本篇要介紹一些寫GUI程式必要的知識,篇幅比較長。
前篇:如何建一個視窗—Windows API篇
按鈕類Gtk篇
Shark的程式教學目錄
buttoncontrol.c
#define UNICODE #include<windows.h> #include<commctrl.h> #include<windowsx.h> //使用Button_開頭的macro #include<stdio.h> #include<stdint.h> const int BUTTON_W=120; const int BUTTON_H=30; const int BUTTON_OFSX=130; const int BUTTON_OFSY=40; LRESULT CALLBACK wndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam){ switch(message){ case WM_COMMAND:{ uint32_t notifyCode=HIWORD(wparam); if(notifyCode==BN_CLICKED){ uint32_t buttonID=LOWORD(wparam); uint32_t checked=Button_GetCheck((HWND)lparam); printf("button %u clicked. checked %u\n",buttonID, checked); } }return 0; case WM_NOTIFY:{ NMHDR* nmhdr=(NMHDR*)lparam; if(nmhdr->code==BCN_DROPDOWN){ NMBCDROPDOWN* nmhdr2=(NMBCDROPDOWN*)lparam; printf("split button dropdown id=%llu\n", nmhdr2->hdr.idFrom); } }return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd,message,wparam,lparam); } static HWND createButton(int id, WCHAR* text, DWORD style, int x,int y,int w,int h, HWND parent){ HWND hwnd=CreateWindow(WC_BUTTON, text, style|WS_CHILD|WS_VISIBLE,x,y,w,h, parent,NULL,NULL,NULL); SetWindowLongPtr(hwnd, GWLP_ID, id); return hwnd; } int main(){ INITCOMMONCONTROLSEX icc={sizeof(INITCOMMONCONTROLSEX), ICC_STANDARD_CLASSES}; InitCommonControlsEx(&icc); WNDCLASS wndclass; ZeroMemory(&wndclass, sizeof(WNDCLASS)); wndclass.style=CS_HREDRAW|CS_VREDRAW; wndclass.lpfnWndProc=wndProc; wndclass.lpszClassName=L"window"; wndclass.hbrBackground=(HBRUSH)(COLOR_BTNFACE+1); wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); RegisterClass(&wndclass); RECT rect={0,0, BUTTON_W*3+40, 400}; AdjustWindowRect(&rect, WS_CAPTION|WS_SYSMENU|WS_VISIBLE, 0); HWND window=CreateWindow(L"window", L"button control", WS_OVERLAPPED|WS_SYSMENU|WS_VISIBLE, CW_USEDEFAULT,CW_USEDEFAULT, rect.right-rect.left, rect.bottom-rect.top, NULL,NULL,NULL,NULL); //產生按鈕 HWND pushButton1= createButton(1, L"(1) push button", 0, 10,10,BUTTON_W,BUTTON_H, window); HWND checkbox1= createButton(2, L"(2) check box", BS_AUTOCHECKBOX, 10, 10+BUTTON_OFSY,BUTTON_W,BUTTON_H, window); HWND checkbox2= createButton(3, L"(3) check box", BS_AUTOCHECKBOX, 10, 10+BUTTON_OFSY*2,BUTTON_W,BUTTON_H, window); HWND checkbox3= createButton(4, L"(4) check box", BS_AUTOCHECKBOX|BS_PUSHLIKE, 10, 10+BUTTON_OFSY*3,BUTTON_W,BUTTON_H, window); HWND radio1= createButton(5, L"(5) radioA", BS_AUTORADIOBUTTON|WS_GROUP, 10+BUTTON_OFSX, 10+BUTTON_OFSY,BUTTON_W,BUTTON_H, window); HWND radio2= createButton(6, L"(6) radioA", BS_AUTORADIOBUTTON, 10+BUTTON_OFSX, 10+BUTTON_OFSY*2,BUTTON_W,BUTTON_H, window); HWND radio3= createButton(7, L"(7) radioA", BS_AUTORADIOBUTTON|BS_PUSHLIKE, 10+BUTTON_OFSX, 10+BUTTON_OFSY*3,BUTTON_W,BUTTON_H, window); HWND radio4= createButton(8, L"(8) radioB", BS_AUTORADIOBUTTON|WS_GROUP, 10+BUTTON_OFSX*2, 10+BUTTON_OFSY,BUTTON_W,BUTTON_H, window); HWND radio5= createButton(9, L"(9) radioB", BS_AUTORADIOBUTTON, 10+BUTTON_OFSX*2, 10+BUTTON_OFSY*2,BUTTON_W,BUTTON_H, window); HWND radio6= createButton(10, L"(10) radioB", BS_AUTORADIOBUTTON|BS_PUSHLIKE, 10+BUTTON_OFSX*2, 10+BUTTON_OFSY*3,BUTTON_W,BUTTON_H, window); HWND threeStateCheckbox= createButton(11, L"(11) 3-state", BS_AUTO3STATE, 10, 10+BUTTON_OFSY*5,BUTTON_W,BUTTON_H, window); HWND pushButton2= createButton(12, L"(12) defbutton", BS_DEFPUSHBUTTON, 10+BUTTON_OFSX, 10+BUTTON_OFSY*5,BUTTON_W,BUTTON_H, window); HWND pushButton3= createButton(13, L"(13) pushbox", BS_PUSHBOX, 10+BUTTON_OFSX*2, 10+BUTTON_OFSY*5,BUTTON_W,BUTTON_H, window); HWND commandLink= createButton(14, L"(14) command link", BS_COMMANDLINK, 10, 10+BUTTON_OFSY*6,BUTTON_W*2,BUTTON_H*2, window); Button_SetNote(commandLink, L"note"); HWND splitButton= createButton(15, L"(15) split button", BS_SPLITBUTTON, 10, 10+BUTTON_OFSY*8,BUTTON_W*2,BUTTON_H, window); MSG msg; while(GetMessage(&msg,NULL,0,0)>0){ DispatchMessage(&msg); } return 0; } |
還有windowsx.h,它裡面定義一些方便的macro。
首先按照「如何建一個視窗—Windows API篇」建好頂層視窗。在建立控制項之前呼叫InitCommonControlsEx()將comctl32.dll初始化。
InitCommonControlsEx()說明 struct INITCOMMONCONTROLSEX說明
參數是宣告一個struct再把它的指標傳入。struct第一個成員是struct的大小,這是Windows API常見的用法,因為新版的struct往往會追加項目,函式裡可以根據struct大小判斷版本做不同處理。第二個成員是要初始化哪些控制項,本篇只用到ICC_STANDARD_CLASSES。
之後的部分暫不解釋,先編譯出來看看,用這個指令
cl buttoncontrol.c /Febuttoncontrol.exe /O2 /MD /link user32.lib comctl32.lib |
執行的樣子
可以看到三個問題
- 外觀是Windows 95的風格,跟其他程式的外觀不合。
- (14)command link和(15)split button沒出現,因為這兩個是Windows Vista以後新增的。
- 字型也跟其他程式不一樣。
準備下面兩個純文字檔放在同一資料夾。
manifest.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/> </dependentAssembly> </dependency> </assembly> |
#include<winuser.h> CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "manifest.xml" |
打這個指令編譯。
rc manifest.rc |
會產生manifest.res,把它放在跟.c同一資料夾。
文件裡有提到其他方法,但我認為做成resource嵌入可執行檔最省事。
問題3要修改四個地方
//1.增加一個全域變數 HFONT systemFont; //2.createButton()裡增加一行SendMessage()設定字型 static HWND createButton(int id, WCHAR* text, DWORD style, int x,int y,int w,int h, HWND parent){ HWND hwnd=CreateWindow(WC_BUTTON, text, style|WS_CHILD|WS_VISIBLE, x,y,w,h, parent, NULL,NULL,NULL); SetWindowLongPtr(hwnd, GWLP_ID, id); SendMessage(hwnd, WM_SETFONT, (WPARAM)systemFont, FALSE); //增加這行 return hwnd; } int main(){ …… //3.在產生第一個button之前,用這幾行載入系統字型 NONCLIENTMETRICS ncm; ncm.cbSize=sizeof(NONCLIENTMETRICS); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); systemFont=CreateFontIndirect(&ncm.lfMessageFont); …… MSG msg; while(GetMessage(&msg,NULL,0,0)>0){ DispatchMessage(&msg); } //4.程式結束前刪除字型物件 DeleteObject(systemFont); return 0; } |
linker參數增加gdi32.lib和manifest.res
cl buttoncontrol.c /Febuttoncontrol.exe /O2 /MD /link user32.lib comctl32.lib \ gdi32.lib manifest.res |
因為要用命令列輸出一些訊息,程式進入點用「int main()」讓執行時出現命令列視窗。
開始進入正題:按鈕控制項。微軟的官方文件中與本篇有關的:
控制項一覽 按鈕類control的圖和說明
由於有一些程式碼是所有按鈕共通的,寫成一個函式createButton()。
static HWND createButton(int id, WCHAR* text, DWORD style, int x,int y,int w,int h, HWND parent){ HWND hwnd=CreateWindow(WC_BUTTON, text, style|WS_CHILD|WS_VISIBLE, x,y,w,h, parent, NULL,NULL,NULL); SetWindowLongPtr(hwnd, GWLP_ID, id); SendMessage(hwnd, WM_SETFONT, (WPARAM)systemFont, FALSE); return hwnd; } |
1:WC_BUTTON macro。查commctrl.h會發現它被代換成一個字串L"Button",直接填L"Button"也可以(大小寫皆可)。
2:顯示在按鈕上的字。
3:window style,一些bit flag。因為這不是頂層視窗,必須有WS_CHILD,其他flag會決定這是哪一種按鈕。因為每個按鈕都需要WS_CHILD|WS_VISIBLE所以寫在createButton()裡面。
4~7:在視窗工作區裡的位置。
8:上層視窗的HWND。
9~11:此處沒用到,填NULL。
先不繼續講程式碼,先看看本篇程式裡的按鈕,把程式build出來操作看看。
CreateWindow()第三參數會決定按鈕種類,以下會說明style怎麼填。
Button Styles
- push button,圖中的(1)。官方文件裡寫要填BS_PUSHBUTTON,但查winuser.h會發現它的值是0,填0也可以。
最一般的按鈕,按滑鼠按鈕時外觀變成被按下,放開滑鼠按鈕就跳起。 - check box(核取方塊),圖中的(2)~(4)。style=BS_AUTOCHECKBOX。
多個是或否的選擇。每個check box各自獨立。 - radio button(單選按鈕) (5)~(10)。style=BS_AUTORADIOBUTTON。
用在多個選項裡選一個。點選一個之後,群組裡其他radio button自動取消選取。
設定群組的方式是,群組裡第一個radio button有WS_GROUP,之後建立的radio button會視為同一群組,直到下一個WS_GROUP才開始新的群組。
(4)、(7)、(10)帶有BS_PUSHLIKE,這是讓它們的外觀像push button,但行為還是check box和radio button,按了會保持狀態而不是立刻跳起。
查Button Styles的文件會發現也有名稱沒有AUTO的BS_CHECKBOX和BS_RADIOBUTTON,這些是滑鼠點了也不會改變狀態,需要在後述的事件處理寫程式控制。
以上三種是基本,以下是比較不常用或特殊用途的。 - 3-state check box (11)。style=BS_AUTO3STATE。
有三種狀態的check box:空白、打勾、未決定。未決定的外觀有的GUI系統是方塊變灰色,有的是一個正方形填滿方塊。
三種狀態各代表什麼是由作者寫程式決定,但要注意不能讓使用者混淆。
跟一般的check box一樣,可以用BS_PUSHLIKE改變外觀,也有不自動改變狀態的BS_3STATE。 - default push button (12),style=BS_DEFPUSHBUTTON。
邊框比較粗,用在一種特殊種類的視窗:對話框(dialog box),按鍵盤的Enter就等同按這個鈕,但本程式不是對話框所以看不出效果。 - push box (13),style=BS_PUSHBOX。
平常外觀看不出是按鈕,但滑鼠點會有反應。 - command link (14),style=BS_COMMANDLINK。
一個箭頭加兩行字如圖,行為類似push button。除了用CreateWindow()第二參數設定主要顯示的字以外,可以用Button_SetNote()加一行小字。 - split button (15),style=BS_SPLITBUTTON。
按鈕右邊有個小箭頭,通常按了箭頭就會跳出一個選單。不過「跳出選單」的動作不是建立按鈕就自動有,要寫程式控制。
其他button style就請查官方文件,大多是跟其他flag做or運算產生效果。
第二行SetWindowLongPtr()是設定一些整數屬性。GWLP_ID是設定ID,之後處理事件會有用。本篇在按鈕上把ID標示出來以方便辨認。
SetWindowLongPtrW()說明
(說明文件裡的函式名稱是SetWindowLongPtrW。之前說過,只要程式開頭有#define UNICODE,Windows API裡跟字串有關的函式和struct都會用Unicode的版本,反之會用ansi的版本。
LONG_PTR是跟該平臺指標一樣大小的整數,等同intptr_t。)
至於x,y,w,h參數,Windows底層API沒有排版的功能,只能自己指定坐標。本篇用一些常數:BUTTON_W、BUTTON_H、BUTTON_OFSX、BUTTON_OFSY方便計算。
Gtk就有排版功能了,可以根據文字長短、視窗大小自動調整元件的位置大小,以後寫Gtk的教學可能會介紹。
接下來解說Windows處理GUI事件的方式。
GUI是個樹狀結構,不只Windows,其他GUI函式庫也是這樣做。
頂層視窗處理事件的函式:WndProc()是我們自己寫的,按鈕也有各自的WndProc(),是Windows內建。
當滑鼠點按鈕的時候,按鈕本身收到滑鼠的message (就是「讀取鍵盤與滑鼠輸入(Windows)」裡介紹的東西),上層視窗則收到notification(通知)。
上層視窗的WndProc()要怎麼處理通知呢?在官方文件左邊的選單找到「Button Control Notifications」:
點開後按「BN_CLICKED」看說明:BN_CLICKED notification code
有這一段:
The parent window of the button receives this notification code through the WM_COMMAND message.
表示上層收到的是WM_COMMAND訊息。不過很多事件都會送出WM_COMMAND訊息,要根據notification code判斷是哪一種事件。
下面寫wparam的低位2 bytes是控制項ID,第3~4 bytes是notification code,可以用LOWORD()、HIWORD()這兩個macro取出。在WndProc()裡檢查message==WM_COMMAND以及HIWORD(wparam)==BN_CLICKED就可得知按鈕按下。
控制項ID是先前用SetWindowLongPtr()設定的值,可以藉此知道是哪個按鈕被按下。lparam是按鈕的hwnd。
再查看WM_COMMAND訊息的說明,裡面有寫「If an application processes this message, it should return zero.」,所以我們自己寫的WndProc()如果有處理這個事件要傳回0。
check box、radio button、3-state check box還需要知道目前狀態才能處理事件,但狀態沒有從wparam和lparam傳過來,要用Button_GetCheck()取得。它的說明要在左邊選單點開「Button Control Macros」找。
Button_GetCheck macro
裡面寫return None是錯的,實際上會傳回BST_CHECKED、BST_UNCHECKED、BST_INDETERMINATE的其中一個。
按split button右邊的箭頭則會送BCN_DROPDOWN通知,查文件可得知上層視窗收到WM_NOTIFY訊息,這個訊息的額外資訊不一樣,lparam是指向一個NMHDR struct的指標,事件資訊存在裡面。
BCN_DROPDOWN notification code WM_NOTIFY message NMHDR structure
一樣很多事件都會送WM_NOTIFY訊息,要根據nmhdr->code判斷是哪一種事件,nmhdr->idFrom判斷是哪個控制項送出,然後把lparam轉成其他型態取出事件相關資訊(本篇的例子是struct NMBCDROPDOWN)。
「按箭頭之後跳出選單」要寫在這個事件處理裡面。
最後介紹SendMessage()函式。
本篇用到兩個Button_開頭的macro。查Button_GetCheck()的說明,會發現裡面有提到BM_GETCHECK message。
打開windowsx.h找Button_GetCheck()的定義會發現它被代換成SendMessage()。
Button_GetCheck((HWND)lparam); //代換成這樣 SendMessage((HWND)lparam, BM_GETCHECK, 0,0); |
Windows API裡取得控制項的資訊或是修改控制項屬性,很多時候是用SendMessage()。第一參數是控制項的hwnd,第二參數是一個整數代表要做哪種操作,第三和四參數是額外資訊,隨操作種類而異。
SendMessage()說明
例如BM_SETCHECK訊息可以改變check box或radio button的狀態,有興趣可以試試。
至於設定字型用的幾個函式,在此不詳細解釋。SystemParametersInfo()可以取得或修改一些系統資訊,CreateFontIndirect()和DeleteObject()跟Windows的一個系統:GDI有關。
SystemParametersInfoW()說明 CreateFontIndirectW()說明 DeleteObject()說明