windows编程(八)
按鈕類控件
?? ?終于到了令人激動的時刻, 盡管在前一階段的學習中我們已經學習了如何在客戶區中繪制簡單的圖形以及如何使用鍵盤和鼠標, 但是距離 Windows意義上的軟件 似乎還是有點遙遠, 而今天, 我們要做的就是將這個距離再縮短一大步! 這階段要學習的就是 子窗口控件 的使用。
?? ?
?? ?在其他一些 Windows應用軟件上我們經常能夠看到一些大致相同的按鈕、復選框、組合框、列表框等控件, 這些控件很有可能就是使用 標準子窗口控件 來實現的。
?? ?
?? ?
一、子窗口的創建
?? ?在講解 "標準子窗口控件" 的使用之前我們首先應該知道如何去創建一個子窗口, 因為這些 "子窗口控件" 實際上都是通過創建一個子窗口的形式來進行創建的, 因此我們應該把理解的重點放在 "子窗口" 上, 而不是 "控件" 上。
?? ?
?? ?子窗口的創建可以將整個客戶區劃分為多個矩形區域, 并且每個子窗口都可以有自己的句柄、窗口過程和客戶區, 每個子窗口過程只接收與自身窗口有關的鼠標消息、鼠標消息的參數 lParam 中包含的坐標是相對于 子窗口 客戶區的左上角的。簡單的說, 子窗口具有一個普通窗口的一切特性。
?? ?
?? ?子窗口的創建同樣是使用 CreateWindow 函數進行創建的, 下面我們通過一個示例來認識這個過程:
??? 代碼已折疊, 點擊展開:
[ 代碼: Demo_01_CreateChildWindow.c ]
?? ?盡管這段代碼有將近100行, 但是我相信有了前面的基礎, 這段代碼也是能夠輕松理解的。
?? ?這段代碼演示的是在父窗口的(10, 10)位置處創建一個大小為 200x200 的子窗口, 效果如下:
???
?? ?
?? ?首先從整體上把握下這段代碼:
?? ??? ?第一步: 聲明父窗口回調函數和子窗口的回調函數:
?? ??? ?第二步: 定義WinMain函數, 在WinMain函數中建立父窗口的 wndclass 窗口類并注冊:
?
?? ??? ?第三步: 改變父窗口 wndclass 類中的部分屬性, 使其成為子窗口的 wndclass, 并注冊子窗口窗口類:
?
?? ??? ?第四步: 顯示窗口、進入消息循環:
?? ??? ?第五步: 定義父窗口回調函數 WndProc, 在處理 WM_CREATE 消息時創建子窗口:
?? ??? ?第六步: 定義子窗口回調函數 ChildWndProc:
?
?? ?一些細節把握:
?? ??? ?1>. 全局的子窗口類名
?? ??? ??? ?在第四行中的代碼 TCHAR szChildClass[] = TEXT("ChildClass") ; 我們把子窗口類名稱設為全局這是因為在 WinMain 函數中和父窗口的回調函數中都需要用到它的名字。
?? ??? ??? ?
?? ??? ?2>. 在注冊子窗口的窗口類時, 沒有再重新定義一個 WNDCLASS 類型的變量, 而是簡單的復用了一下父窗口中的 wndclass, 其中有3個成員與父窗口不同:
?? ??? ??? ?lpszClassName, 即子窗口類的名稱 ;
?? ??? ??? ?cbWndExtra 被設置成一個 long 型數據所占的存儲單元大小(4字節), 這個成員通知 Windows 在內部結構中給基于這個窗口類的每個窗口預留4個字節的額外存儲空間, 以用來給用戶為每個窗口保存不同的信息 ;
?? ??? ??? ?lPfnWndProc 成員被設置成 ChildWndProc, 表示子窗口類的窗口過程 。
?? ??? ??? ?
?? ??? ?3>. 創建子窗口時的 (HINSTANCE) GetWindowLong(hwnd, RDW_ERASE )
?? ??? ??? ?實際上 CreateWindow 函數在創建子窗口時需要 hInstance 句柄, 而GetWindowLong函數的作用就是獲得有關指定窗口的信息, 函數也獲得在額外窗口內存中指定偏移位地址的32位度整型值, GetWindowLong的原型:
?? ??? ??? ??? ?LONG GetWindowLong( HWND hwnd, int nIndex ) ;
?? ??? ??? ?nIndex 為索引值, 它可以是以下標識符之一:
?
?? ?在子窗口的窗口過程的處理上, 我們僅僅處理了一個 WM_LBUTTONDWON 消息, 當鼠標在子窗口客戶區中按下左鍵時就彈出一個對話框告訴用戶"鼠標左鍵在子窗口客戶區中按下!"。
?? ?
?? ?這些就是創建一個子窗口的演示。使用子窗口的程序設計有助于程序的結構化和模式化, 提高代碼的復用率。
?? ?
?? ?如果你已經將上面示例中的代碼理解的話, 那么恭喜你! 你已經又向成功邁向了一大步, 因為標準子窗口控件的使用比我們自己創建子窗口還要簡單!
?? ?
???
二、使用標準子窗口控件的按鈕類
?? ?按鈕類: 按鈕類不僅僅是指我們所常見的按鈕控件, 他是一類控件的統稱, 包括: 按鈕、復選框、單選按鈕、組合框。以創建一個按鈕作為示例, 創建一個按鈕并不需要我們再去定義一個WNDCLASS類再去注冊它, 也不需要再去為這個控件指定子窗口過程函數, 因為這些已經在一些相關的頭文件中給完成了, 我們只需要做相關的調用即可:
?
已折疊, 點擊展開全部:
View Code - CreateButton 1 #include<windows.h> 2 3 LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ; 4 5 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow ) 6 { 7 TCHAR szAppName[] = TEXT( "CreateChildWindow" ) ; 8 9 HWND hwnd ; 10 MSG msg ; 11 WNDCLASS wndclass ; 12 13 wndclass.lpfnWndProc = WndProc ; 14 wndclass.lpszClassName = szAppName ; 15 wndclass.style = CS_VREDRAW | CS_HREDRAW ; 16 wndclass.hInstance = hInstance ; 17 wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ) ; 18 wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION ) ; 19 wndclass.cbClsExtra = 0 ; 20 wndclass.cbWndExtra = 0 ; 21 wndclass.hbrBackground = (HBRUSH) GetStockObject( WHITE_BRUSH ) ; 22 wndclass.lpszMenuName = NULL ; 23 24 if( !RegisterClass(&wndclass) ) 25 { 26 MessageBox( NULL, TEXT("無法注冊窗口類!"), szAppName, MB_OK | MB_ICONERROR ) ; 27 return 0 ; 28 } 29 30 hwnd = CreateWindow( szAppName, TEXT("創建按鈕 - 演示"), 31 WS_OVERLAPPEDWINDOW, 32 CW_USEDEFAULT, CW_USEDEFAULT, 33 800, 600, 34 NULL, NULL, hInstance, NULL ) ; 35 36 ShowWindow( hwnd, iCmdShow ) ; 37 UpdateWindow( hwnd ) ; 38 39 while( GetMessage(&msg, NULL, 0, 0) ) 40 { 41 TranslateMessage( &msg ) ; 42 DispatchMessage( &msg ) ; 43 } 44 45 return msg.wParam ; 46 } 47 48 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) 49 { 50 static HWND btnHwnd ; //子窗口句柄 51 52 switch(message) 53 { 54 case WM_CREATE: //在接收到 WM_CREATE 消息時創建一個按鈕 55 btnHwnd = CreateWindow( TEXT("button"), TEXT("創建按鈕"), 56 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 57 10, 10, //在父窗口客戶區(10, 10)位置創建一個按鈕 58 100, 30, //按鈕的大小為100x30 59 hwnd, //父窗口句柄 60 (HMENU)1, //按鈕控件ID 61 (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE ), 62 NULL ) ; 63 return 0 ; 64 65 case WM_DESTROY: 66 PostQuitMessage( 0 ) ; 67 return 0 ; 68 } 69 return DefWindowProc( hwnd, message, wParam, lParam ) ; 70 }??? [代碼: Demo_02_CreateButton.c]
?? ?
?? ?這樣就完成了一個按鈕的創建, 效果:
???
?? ?盡管這個按鈕看起來還有一些問題, 比如為什么按鈕的風格那么難看, 以及按鈕上的文字也不怎么美觀, 沒關系, 這些都是可以解決的, 按鈕的風格問題等把標準控件學習完畢后一次性解決, 這里先把目前我們需要解決的事處理掉再關注這些。
?? ?
?? ?當嘗試點擊這個按鈕時只是覺得他向下凹了一下, 然后又恢復了, 好像什么也沒做一樣, 是的, 實際上他做了很多事情, 比如這個凹陷后又恢復的效果就是系統幫我們完成的, 慶幸的是這個效果是windows幫我們完成的, 要不然我們又要花費更多的代碼來完成這個效果。
?? ?
?? ?1>. 子窗口傳遞信息給父窗口
?? ??? ?如果我們要使用這個按鈕, 我們只需要捕獲這個按鈕向父窗口發送來的消息即可, 這個消息就是 WM_COMMAND, 每當用戶在子窗口上進行一些操作時, 系統就會像父窗口發來 WM_COMMAND 這個消息, 通過 wParam 和 lParam 中的值我們還可以進一步的了解用戶進行的是什么操作。
?? ??? ?WM_COMMAND 消息中 wParam 和 lParam 值含義如下:
| LOWORD(wParam) | 子窗口ID |
| HIWORD(wParam) | 通知碼 |
| lParam | 子窗口句柄 |
?? ??? ??? ?
?? ??? ?
?
通知碼的意義就是進一步給出消息的意思, 這些通知碼
#define BN_CLICKED 0 //單擊 #define BN_PAINT 1 //被重繪 #define BN_HILITE 2 //被選擇 #define BN_UNHILITE 3 //高亮被移除 #define BN_DISABLE 4 //被禁用 #define BN_DOUBLECLICKED 5 //被雙擊 #define BN_PUSHED BN_HILITE #define BN_UNPUSHED BN_UNHILITE #define BN_DBLCLK BN_DOUBLECLICKED #define BN_SETFOCUS 6 //被設置焦點 #define BN_KILLFOCUS 7 //失去焦點?
?? ??? ?實際上這些通知碼大部分我們不會用到, 起碼目前是這樣, 通知碼的6(BN_SETFOCUS) 和 7(BN_KILLFOCUS) 只有當按鈕樣式中包含 BS_NOTIFY 時才會被發送。
?? ??? ?如果要處理按鈕的一些消息, 只需像這樣做既可:
?? ???
?? ??? ?一個示例: 處理按鈕的單擊消息:
代碼已折疊, 點擊展開:
View Code 1 #include<windows.h> 2 3 LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ; 4 5 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow ) 6 { 7 TCHAR szAppName[] = TEXT( "CreateChildWindow" ) ; 8 9 HWND hwnd ; 10 MSG msg ; 11 WNDCLASS wndclass ; 12 13 wndclass.lpfnWndProc = WndProc ; 14 wndclass.lpszClassName = szAppName ; 15 wndclass.style = CS_VREDRAW | CS_HREDRAW ; 16 wndclass.hInstance = hInstance ; 17 wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ) ; 18 wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION ) ; 19 wndclass.cbClsExtra = 0 ; 20 wndclass.cbWndExtra = 0 ; 21 wndclass.hbrBackground = (HBRUSH) GetStockObject( WHITE_BRUSH ) ; 22 wndclass.lpszMenuName = NULL ; 23 24 if( !RegisterClass(&wndclass) ) 25 { 26 MessageBox( NULL, TEXT("無法注冊窗口類!"), szAppName, MB_OK | MB_ICONERROR ) ; 27 return 0 ; 28 } 29 30 hwnd = CreateWindow( szAppName, TEXT("創建按鈕 - 演示"), 31 WS_OVERLAPPEDWINDOW, 32 CW_USEDEFAULT, CW_USEDEFAULT, 33 800, 600, 34 NULL, NULL, hInstance, NULL ) ; 35 36 ShowWindow( hwnd, iCmdShow ) ; 37 UpdateWindow( hwnd ) ; 38 39 while( GetMessage(&msg, NULL, 0, 0) ) 40 { 41 TranslateMessage( &msg ) ; 42 DispatchMessage( &msg ) ; 43 } 44 45 return msg.wParam ; 46 } 47 48 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) 49 { 50 static HWND btnHwnd ; //子窗口句柄 51 52 switch(message) 53 { 54 case WM_CREATE: //在接收到 WM_CREATE 消息時創建一個按鈕 55 btnHwnd = CreateWindow( TEXT("button"), TEXT("創建按鈕"), 56 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 57 10, 10, //在父窗口客戶區(10, 10)位置創建一個按鈕 58 100, 30, //按鈕的大小為100x30 59 hwnd, //父窗口句柄 60 (HMENU)1, //按鈕控件ID 61 (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE ), 62 NULL ) ; 63 return 0 ; 64 65 case WM_COMMAND: 66 switch(LOWORD(wParam)) //判斷子窗口ID, 根據子窗口ID做出不同響應 67 { 68 case 1: //處理ID為1的子窗口消息 69 switch(HIWORD(wParam)) //通過HIWORD(wParam)進一步判斷消息類型 70 { 71 case BN_CLICKED: //處理的按下通知碼 72 MessageBox( hwnd, TEXT("按鈕被按下!"), TEXT("按鈕消息"), MB_OK ) ; 73 break ; 74 } 75 break ; 76 } 77 return 0 ; 78 79 case WM_DESTROY: 80 PostQuitMessage( 0 ) ; 81 return 0 ; 82 } 83 return DefWindowProc( hwnd, message, wParam, lParam ) ; 84 }?? ???? [代碼: Demo_03_DealWithButtonMessage.c]
?? ??? ?
?? ?2>. 父窗口傳遞信息給子窗口
?? ??? ?子窗口可以向父窗口發送消息, 父窗口同樣也可以向子窗口發送消息, 在 WINUSER.H 中定義了8個專用于按鈕的消息:
?? ???? 使用 SendMessage函數即可將這些消息發送給子窗口, SendMessage 函數原型:
LRESULT SendMessage(HWND hWnd, //子窗口句柄UINT Msg, //消息WPARAM wParam, //wParam值LPARAM lParam //lParam值);?
三、按鈕類中的更多成員
?? ?要改變按鈕的樣式, 只需在創建子窗口時添加相關的按鈕樣式即可, 除了上面的演示的 BS_PUSHBUTTON 按鈕樣式, 還有更多的樣式如下:
?? ?對這些按鈕的相關常用操作:
?? ??? ?1>. 按鈕(Push Button)
?? ??? ??? ?BS_PUSHBUTTON 和 BS_DEFPUSHBUTTON 樣式的按鈕我們可以強制改變他的顯示狀態(正常或凹陷), 只需要向這個按鈕發送個消息改變他的狀態即可。
?? ??? ??? ?將按鈕凹陷下去:
?
?? ??? ??? ?將按鈕正常顯示:
?
?? ??? ??? ?也可以通過一個 BM_GETSTATE 的消息獲取按鈕的當前狀態, 被按下時返回 TRUE, 沒有被按下時返回 FLASE。
?? ??? ??? ?
?? ??? ?2>. 復選框(Check Box )
?? ??? ??? ?①. 將復選框前打勾
?? ??? ??? ????
?? ??? ??? ?②. 取消打勾
?? ??? ??? ?也可以通過一個 BM_GETCHECK 的消息獲取按鈕的當前狀態, 被打勾時返回 非0或TRUE, 沒有被打勾時返回 0或FLASE。
?? ??? ??? ?
?? ??? ??? ?BS_3STATE 和 BS_3AUTOSTATE 類型的復選框還具有第三種狀態, 打的勾為灰色, 當 wParam 消息被設為2時這個類型的復選框就會被設為這種狀態。
?? ??? ??? ?
?? ??? ?3>. 單選按鈕(Radio Button)
?? ??? ??? ?單選按鈕的窗口樣式為 BS_RADIOBUTTON 和 BS_AUTORADIOBUTTON, 后者只能用于對話框。單選按鈕沒有切換狀態, 當用戶按下一個單選按鈕時即使再次按下, 他的樣式也不會再變化, 使用 SendMessage 向單選按鈕發送消息可改變其中的標記:
?? ??? ??? ?①. 標記
?? ??? ??? ????
?? ??? ??? ?②. 取消標記
?
?? ??? ??? ?
?? ?一些對窗口的通用操作(包括子窗口):
?? ??? ?1>. 改變按鈕文本
?? ??? ??? ?通過調用 SetWindowText 可以改變按鈕中顯示的文本:
?? ??? ??? ??? ?SetWindowText( hwnd, pszString ) ;
?? ??? ??? ??? ?
?? ??? ??? ?對于普通的窗口來說, 該函數能改變窗口標題欄中的文本, 而對于按鈕控件改變的即為顯示的文本。
?? ??? ??? ?
?? ??? ?2>. 獲取窗口的當前文本
?? ??? ??? ?iLength = GetWindowText( hwnd, pszBuffer, iMaxLength ) ;
?? ??? ??? ?iMaxLength用來限制緩沖區 pszBuffer 所能接收的最長文字數量。
?? ??? ??? ?
?? ??? ?3>. 隱藏窗口
?? ??? ??? ?ShowWindow( hwndChild, SW_HIDE ) ;
?? ??? ??? ?
?? ??? ?4>. 顯示窗口
?? ??? ??? ?ShowWindow( hwndChild, SW_SHOWNORMAL ) ;
?? ??? ??? ?
?? ??? ?5>. 判斷窗口是否可見
?? ??? ??? ?IsWindowVisible( hwndChild ) ;
?? ??? ??? ?非0為可見, 0為不可見。
?? ??? ??? ?
?? ??? ?6>. 禁用窗口
?? ??? ??? ?EnableWindow( hwndChild, FALSE ) ;
?? ??? ??? ?禁用窗口后相關的控件的字符串將變成灰色, 表示不可用, 同時窗口也不再響應鼠標或鍵盤的輸入。
?? ??? ??? ?
?? ??? ?7>. 啟用窗口
?? ??? ??? ?EnableWindow( hwndChild, TRUE ) ;
?? ??? ??? ?
?? ??? ?8>. 判斷窗口是否可用
?? ??? ??? ?IsWindowEnabled( hwndChild ) ;
?? ??? ??? ?啟用返回非0, 禁用返回0。
?? ??? ????
?
示例中的代碼下載: http://files.cnblogs.com/mr-wid/Learn_WinC_day_13.rar
窗口類樣式(Window Style):
WS_BORDER //帶有邊框的窗口WS_CAPTION //帶有標題欄的窗口WS_CHILD //子窗口WS_CHILDWINDOW //與WS_CHILD相同WS_CLIPCHILDREN //當在父窗口內繪圖時, 排除子窗口區域WS_CLIPSIBLINGS //使窗口排除子窗口之間的相對區域WS_DISABLED //窗口呈不可用狀態WS_DLGFRAME //帶對話框邊框樣式,不帶標題框WS_GROUP //組樣式WS_HSCROLL //帶有水平滾動條WS_ICONIC //初始化時呈最小化狀態WS_MAXIMIZE //初始時最大化窗口WS_MAXIMIZEBOX //具有最大化按鈕WS_MINIMIZE //初始時最小化窗口WS_MINIMIZEBOX //具有最小化按鈕WS_OVERLAPPED //具有一個標題欄和邊框WS_OVERLAPPEDWINDOW //這些樣式的組合 WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX, WS_MAXIMIZEBOXWS_POPUP //彈出風格, 不能用于子窗口WS_POPUPWINDOW //這些樣式的組合 WS_BORDER, WS_POPUP, WS_SYSMENUWS_SIZEBOX //可調節邊框大小WS_SYSMENU //標題框上帶有窗口菜單(須指定WS_CAPTION樣式)WS_TABSTOP //可接受TAB鍵WS_THICKFRAME //同WS_SIZEBOXWS_TILED //同WS_OVERLAPPEDWS_TILEDWINDOW //同WS_OVERLAPPEDWINDOWWS_VISIBLE //初始時是可見的WS_VSCROLL //帶有垂直滾動條
?? ?
?? ?
編輯框樣式(Edit Style):
?
------------------?
轉載于:https://www.cnblogs.com/liweizhong/p/3383397.html
總結
以上是生活随笔為你收集整理的windows编程(八)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [读书笔记] 两则之一: 100Gbps
- 下一篇: Python学习 Part6:错误和异常