在這一講,我們將學習什麼是視窗子類化和怎樣按你所想要的方式方便地使用它。
理論:
如果你曾經在 Windows 環境下編過程式,有時候就會發現:有一個現成的視窗,幾乎有你所需要的全部功能,但還不完全一樣(否則就沒有必要講這一節了)。你曾遇到過這樣的處境嗎,如果你需要一個具有過濾特殊字元功能的 Edit 控制項。當然最直接的方法就是自己用代碼來實現,但這的確是一個費時又很困難的任務,而視窗子類化就可以用來做這種事情。
視窗子類化允許你接管被子類化的視窗,使你對它有絕對的控制權。舉個例子了來闡明一下:例如你需要一個只接受十六進位數字輸入的文本編輯框,如果使用一個簡單的 Edit控制項,當使用者輸入十六進位以外的字元時,你既不知道也無計可施。也就是說,當使用者進文字框中輸入字串 "zb+q*" 時,如果除了拒絕接受整個字串以外幾乎什麼也不能做,至少這顯得特別不專業。重要的是,你需要具有輸入檢測的能力,即每當使用者輸入一個字元到編輯框中時要能檢測這個字元。
現在來解釋實現細節:當使用者往文字框中輸入字元時,Windows 會給Edit控制項的視窗函數發送 WM_CHAR 訊息。這個視窗函數本身寄生於 Windows 中,因此不能直接修改它。但是我們可以重新導向這個訊息使之發送到我們自己編寫的視窗處理函數。如果自訂視窗要處理這個訊息那就可以處理它,如果不處理就可以把這個訊息轉寄到它原來視窗處理函數。通過這種方式,自訂的視窗處理函數就把它自己插入到 Windows 系統和 Edit 控制項之間。
看下面的流程:
視窗子類化之前
Windows ==>Edit 控制項的視窗處理函數。
子類化之後
Windows ==>自訂的視窗處理函數==> Edit 控制項的視窗處理函數。
注意子類化並不局限於控制項,可以子類化任何視窗,現在我們要把精力集中到怎樣實現子類化一個視窗上。讓我們想想Windows 怎樣知道 Edit 控制項的視窗處理函數放在什麼地方。猜的?…肯定不是。原來 WNDCLASSEX 結構的成員 lpfnWndProc 指出了視窗函數地址。如果能用自己編寫的視窗函數的地址來替換這個成員變數,那 Windows 不就把訊息發到自訂的視窗函數了嗎! 我們通過調用函數SetWindowLong 來實現這個任務,此函數的原型為:
SetWindowLong PROTO hWnd:DWORD, nIndex:DWORD, dwNewLong:DWORD
hWnd = 將要實施子類化的視窗的控制代碼
nIndex = 函數了功能索引
GWL_EXSTYLE 設定視窗的擴充風格.
GWL_STYLE 設定新的視窗風格
GWL_WNDPROC 設定新的視窗處理函數地址
GWL_HINSTANCE 設定新的應用程式控制代碼
GWL_ID 設定新的視窗標識
GWL_USERDATA 設定一個與這個視窗相關的給使用者使用的32位的資料
dwNewLong = 用來更新的資料
我們的工作還是比較簡單的:
寫一個視窗函數用於處理髮給 Edit 控制項的訊息。
用參數GWL_WNDPROC調用SetWindowLong 函數,如果調用成功那麼傳回值就是與調用功能相聯絡的一個32位的整數
在我們的程式中,傳回值就是原先視窗函數的地址。我們要儲存這個值以便以後使用。 記住:有一些我們不處理的訊息,需要把它們派遣給原來的視窗函數來處理,這就用到另外一個函數 CallWindowProc, 函數原型為:
CallWindowProc PROTO lpPrevWndFunc:DWORD, hWnd:DWORD, Msg:DWORD, wParam:DWORD, lParam:DWORD
lpPrevWndFunc = 視窗原來函數的地址. 剩下的四個參數就是發給自訂函數的參數,直接把它們傳給函數 CallWindowProc 就行了。