代碼下載:subclass(請點擊進入論壇回複以後下載)
也許你需要一個特殊的Edit來限制浮點數的輸入,但是現有的Edit卻並不能完成這項工作――因為它只能夠單純的限制大小寫或者純數字。當你在論壇上求救的時候,某個網友告訴你:“用子類化。”你也許會在看到一線曙光的同時多出了一連串的問題:何為子類化?子類化的原理是什嗎?如何?子類化?下面就讓我從一個簡單的C++程式開始,一步步解開你的疑團吧。
首先,我為你列出以下這個C++程式:
01.
#include <iostream>
02.
using
namespace
std;
03.
class
Parent
04.
{
05.
public
:
06.
void
func(
void
) { cout <<
"func of Parent"
<< endl; }
07.
};
08.
class
Child :
public
Parent
09.
{
10.
public
:
11.
void
func(
void
) { cout <<
"func of Child"
<< endl; }
12.
};
13.
void
main()
14.
{
15.
Parent p;
16.
Child c;
17.
p.func();
18.
c.func();
19.
}</iostream>
現在我來解說一下。這段代碼中我定義了兩個C++類:父類和子類,並且子類是繼承自父類的;它們有一個具有相同名稱的成員函數func。在main函數中,我分別構造了父類和子類的對象,並調用了它們各自的成員函數func。結果如下:
1.
func of Parent
2.
func of Child
簡單說來,這段代碼就是子類根據自己的需要改寫了func成員函數。而Win32的子類化的原理也與此類似,只不過子類化實際上並沒有像C++一樣重載哪個函數,而是靠攔截Windows系統中的某些訊息來自己進行處理罷了。舉例來說,請大家看以下這段簡單的視窗回調過程:
01.
LRESULT
CALLBACK ProcMain(
HWND
hDlg,
UINT
Msg,
WPARAM
wParam,
LPARAM
lParam)
02.
{
03.
switch
(Msg)
04.
{
05.
case
WM_CLOSE:
06.
EndDialog(hDlg, 0);
07.
break
;
08.
case
WM_DESTROY:
09.
PostQuitMessage(0);
10.
break
;
11.
}
12.
return
0;
13.
}
在這個回調之中,我手動處理了兩個訊息:在單擊了“關閉”按鈕(WM_CLOSE)的時候,我將對話方塊關閉(EndDialog);在對話方塊銷毀(WM_DESTROY)的時候,我向系統訊息佇列中發送了退出的訊息來完成結束工作(PostQuitMessage)。也就是說,如果把WM_CLOSE的響應代碼改成:
1.
case
WM_CLOSE:
2.
ShowWindow(hDlg, SW_MINIMIZE);
3.
break
;
這樣一來,這個對話方塊就會和MSN一樣,在單擊了“關閉”之後,就會完成最小化的工作了。那麼,對於視窗過程已定義好的系統控制項,將如何手動響應它的訊息呢?
我們可以用函數指標的辦法,將我們感興趣的訊息攔截下來,處理完之後再讓預定義的視窗過程處理。這個過程大致如下:
1.
WNDPROC OldProc;
2.
OldProc = (WNDPROC)SetWindowsLong(hWnd, GWL_WNDPROC, (
LONG
)NewProc);
當然,這裡的新視窗過程NewProc是預先由你實現好的。上述代碼執行以後,系統在處理hWnd的視窗訊息時,就會先進入你實現的NewProc回調過程,然後在處理過你感興趣的訊息之後,通過CallWindowProc函數和你預先儲存的OldProc再次回到原來的回調過程中完成剩餘的工作。
以上就是視窗子類化的原理分析,下面我通過一個執行個體來實際解說如何對視窗進行子類化。
這個例子的介面如下:
介面上方的編輯框是用來限制浮點輸入的,下面則是一個普通的超級連結。
好了,下面我開始按步驟完成對這兩個視窗的子類化:
第一步,在主視窗對話方塊初始化的時候,儲存原有的視窗過程,並設定新的視窗過程。代碼如下:
1.
case
WM_INITDIALOG:
2.
EditProc = (WNDPROC)SetWindowLong(GetDlgItem(hDlg, IDC_EDIT), GWL_WNDPROC, (
LONG
)ProcFloat);
3.
StaticProc = (WNDPROC)SetWindowLong(GetDlgItem(hDlg, IDC_ST_HOMEPAGE), GWL_WNDPROC, (
LONG
)ProcLink);
4.
break
;
第二步,實現浮點編輯框的視窗過程:
01.
LRESULT
CALLBACK ProcFloat(
HWND
hWnd,
UINT
Msg,
WPARAM
wParam,
LPARAM
lParam)
02.
{
03.
if
(Msg == WM_CHAR && wParam !=
''
.
''
&& (wParam <=
''
0
''
|| wParam >=
''
9
''
) && wParam != VK_BACK)
04.
{
05.
MessageBeep(MB_OK);
06.
return
0;
07.
}
08.
else
09.
return
CallWindowProc(EditProc, hWnd, Msg, wParam, lParam);
10.
}
這裡需要解釋的是,由於控制項本身的需求,所以只需要攔截一個訊息,就是接收字元的WM_CHAR。當使用者輸入的字元不是小數點、0~9以及退格鍵(注意不要少了退格鍵,否則你將會發現你的編輯框無法刪除輸入錯誤的數字)的時候,就發出一聲聲音以提示輸入錯誤。至於其它的訊息,則調用原有的回呼函數進行處理。
第三步,實現超級連結的視窗過程:
01.
LRESULT
CALLBACK ProcLink(
HWND
hWnd,
UINT
Msg,
WPARAM
wParam,
LPARAM
lParam)
02.
{
03.
switch
(Msg)
04.
{
05.
case
WM_SETCURSOR:
06.
SetCursor(LoadCursor(NULL, IDC_HAND));
07.
break
;
08.
case
WM_LBUTTONDOWN:
09.
ShellExecute(NULL,
"open"
,
"http://home.ncust.edu.cn/~titilima"
, NULL, NULL, SW_SHOWNORMAL);
10.
break
;
11.
default
:
12.
return
CallWindowProc(StaticProc, hWnd, Msg, wParam, lParam);
13.
}
14.
return
0;
15.
}
這段代碼很容易明白:它完成了兩件事,其一是設定游標指標為手形(注意:對於較早的Windows系統,是沒有預定義的IDC_HAND指標的,這個時候你需要在EXE的資源中自己畫一個手形指標,比如Delphi之中的手形指標就是自己畫的),其二是當單擊了滑鼠左鍵的時候開啟你想開啟的網頁連結。
其實對於超級連結,它更主要的東西是在子類化之外實現的――就是它的字型顏色(注意這段代碼是在主視窗對話方塊的回調過程中實現的):
1.
case
WM_CTLCOLORSTATIC:
2.
if
(GetDlgItem(hDlg, IDC_ST_HOMEPAGE) == (
HWND
)lParam)
3.
{
4.
SetTextColor((
HDC
)wParam, 0xff0000);
5.
SetBkMode((
HDC
)wParam, TRANSPARENT);
6.
return
(
LRESULT
)CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
7.
}
8.
break
;
還有幾點要說明的是:
1、你的這個Static超連結必須擁有一個唯一的資源ID,比如我的這個就是IDC_ST_HOMEPAGE,這樣才能用GetDlgItem獲得它的控制代碼來完成子類化;
2、你必須為它設定SS_NOTIFY樣式,以保證在單擊它的時候它能夠通知父視窗對話方塊;
3、單擊它開啟網頁的處理也可以放在子類化之外,處理主視窗對話方塊的WM_COMMAND訊息也可以實現這一功能。
關於Win32的視窗子類化就介紹到這裡了,你可以 點這裡下載本文的配套原始碼 ,代碼中有詳細的注釋。