程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 解說Win32的窗口子類化

解說Win32的窗口子類化

編輯:關於VC++

也許你需要一個特殊的Edit來限制浮點數的輸入,但是現有的Edit卻並不能完成這項工作——因為它只能夠單純的限制大小寫或者純數字。當你在論壇上求救的時候,某個網友告訴你:“用子類化。”你也許會在看到一線曙光的同時多出了一連串的問題:何為子類化?子類化的原理是什麼?如何實現子類化?下面就讓我從一個簡單的C++程序開始,一步步解開你的疑團吧。

首先,我為你列出以下這個C++程序:

#include <iostream>
using namespace std;
class Parent
{
public:
 void func(void) { cout << "func of Parent" << endl; }
};
class Child : public Parent
{
public:
 void func(void) { cout << "func of Child" << endl; }
};
void main()
{
 Parent p;
 Child c;
 p.func();
 c.func();
}

現在我來解說一下。這段代碼中我定義了兩個C++類:父類和子類,並且子類是繼承自父類的;它們有一個具有相同名稱的成員函數func。在main函數中,我分別構造了父類和子類的對象,並調用了它們各自的成員函數func。結果如下:

func of Parent
func of Child

簡單說來,這段代碼就是子類根據自己的需要改寫了func成員函數。而Win32的子類化的原理也與此類似,只不過子類化實際上並沒有像C++一樣重載哪個函數,而是靠攔截Windows系統中的某些消息來自己進行處理罷了。舉例來說,請大家看以下這段簡單的窗口回調過程:

LRESULT CALLBACK ProcMain(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
{
 switch (Msg)
 {
 case WM_CLOSE:
  EndDialog(hDlg, 0);
  break;
 case WM_DESTROY:
  PostQuitMessage(0);
  break;
 }
 return 0;
}

在這個回調之中,我手動處理了兩個消息:在單擊了“關閉”按鈕(WM_CLOSE)的時候,我將對話框關閉(EndDialog);在對話框銷毀(WM_DESTROY)的時候,我向系統消息隊列中發送了退出的消息來完成結束工作(PostQuitMessage)。也就是說,如果把WM_CLOSE的響應代碼改成:

case WM_CLOSE:
  ShowWindow(hDlg, SW_MINIMIZE);
  break;

這樣一來,這個對話框就會和MSN一樣,在單擊了“關閉”之後,就會完成最小化的工作了。那麼,對於窗口過程已定義好的系統控件,將如何手動響應它的消息呢?

我們可以用函數指針的辦法,將我們感興趣的消息攔截下來,處理完之後再讓預定義的窗口過程處理。這個過程大致如下:

WNDPROC OldProc;
  OldProc = (WNDPROC)SetWindowsLong(hWnd, GWL_WNDPROC, (LONG)NewProc);

當然,這裡的新窗口過程NewProc是預先由你實現好的。上述代碼執行以後,系統在處理hWnd的窗口消息時,就會先進入你實現的NewProc回調過程,然後在處理過你感興趣的消息之後,通過CallWindowProc函數和你預先保存的OldProc再次回到原來的回調過程中完成剩余的工作。

以上就是窗口子類化的原理分析,下面我通過一個實例來實際解說如何對窗口進行子類化。這個例子的界面如下:

界面上方的編輯框是用來限制浮點輸入的,下面則是一個普通的超級鏈接。

好了,下面我開始按步驟完成對這兩個窗口的子類化:

第一步,在主窗口對話框初始化的時候,保存原有的窗口過程,並設置新的窗口過程。代碼如下:

case WM_INITDIALOG:
  EditProc = (WNDPROC)SetWindowLong(GetDlgItem(hDlg, IDC_EDIT), GWL_WNDPROC, (LONG)ProcFloat);
  StaticProc = (WNDPROC)SetWindowLong(GetDlgItem(hDlg, IDC_ST_HOMEPAGE), GWL_WNDPROC, (LONG)ProcLink);
  break;

第二步,實現浮點編輯框的窗口過程:

LRESULT CALLBACK ProcFloat(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
 if (Msg == WM_CHAR && wParam != ''.'' && (wParam <= ''0'' || wParam >= ''9'') && wParam != VK_BACK)
 {
  MessageBeep(MB_OK);
  return 0;
 }
 else
  return CallWindowProc(EditProc, hWnd, Msg, wParam, lParam);
}

這裡需要解釋的是,由於控件本身的需求,所以只需要攔截一個消息,就是接收字符的WM_CHAR。當用戶輸入的字符不是小數點、0~9以及退格鍵(注意不要少了退格鍵,否則你將會發現你的編輯框無法刪除輸入錯誤的數字)的時候,就發出一聲聲音以提示輸入錯誤。至於其它的消息,則調用原有的回調函數進行處理。

第三步,實現超級鏈接的窗口過程:

LRESULT CALLBACK ProcLink(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
 switch (Msg)
 {
 case WM_SETCURSOR:
  SetCursor(LoadCursor(NULL, IDC_HAND));
  break;
 case WM_LBUTTONDOWN:
  ShellExecute(NULL, "open", "http://home.ncust.edu.cn/~titilima", NULL, NULL, SW_SHOWNORMAL);
  break;
 default:
  return CallWindowProc(StaticProc, hWnd, Msg, wParam, lParam);
 }
 return 0;
}

這段代碼很容易明白:它完成了兩件事,其一是設置光標指針為手形(注意:對於較早的Windows系統,是沒有預定義的IDC_HAND指針的,這個時候你需要在EXE的資源中自己畫一個手形指針,比如Delphi之中的手形指針就是自己畫的),其二是當單擊了鼠標左鍵的時候打開你想打開的網頁鏈接。

其實對於超級鏈接,它更主要的東西是在子類化之外實現的——就是它的字體顏色(注意這段代碼是在主窗口對話框的回調過程中實現的):

case WM_CTLCOLORSTATIC:
  if (GetDlgItem(hDlg, IDC_ST_HOMEPAGE) == (HWND)lParam)
  {
   SetTextColor((HDC)wParam, 0xff0000);
   SetBkMode((HDC)wParam, TRANSPARENT);
   return (LRESULT)CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
  }
  break;

還有幾點要說明的是:

1、你的這個Static超鏈接必須擁有一個唯一的資源ID,比如我的這個就是IDC_ST_HOMEPAGE,這樣才能用GetDlgItem獲得它的句柄來完成子類化;

2、你必須為它設置SS_NOTIFY樣式,以保證在單擊它的時候它能夠通知父窗口對話框;

3、單擊它打開網頁的處理也可以放在子類化之外,處理主窗口對話框的WM_COMMAND消息也可以實現這一功能。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
欄目導航
Copyright © 程式師世界 All Rights Reserved