摘要:本文主要介紹了如何通過窗口子類化技術(shù)來實(shí)現(xiàn)對編輯控件的限制輸入。
關(guān)鍵詞:窗口,子類化,句柄, 消息。
1.問題的提出
盡管Windows系統(tǒng)提供了許多通用的控件如:Edit、ComboBox 、ListBox……等,有些情況下這些標(biāo)準(zhǔn)控件也無能為力。比如:在對學(xué)生成績考評過程中需要一個(gè)供教
師輸入評測等級的編輯控件,要求在編輯框內(nèi)只能輸入A、B、C、D四個(gè)等級,這樣在編輯框內(nèi)就禁止對其他字母或數(shù)字的操作。簡單地使用Windows的編輯框控件是不能對輸入字符進(jìn)行有效過濾的,對于這類問題的解決,我們可以采用子類化的方法來實(shí)現(xiàn)編輯控件的限制輸入。
2.實(shí)現(xiàn)方法
每個(gè)應(yīng)用程序?yàn)榱说怯浺粋€(gè)窗口類,首先要填寫好一個(gè)WNDCLASS,其中的結(jié)構(gòu)參數(shù)lpfnWndProc就是該類窗口函數(shù)的地址,接著調(diào)RegisterClass()函數(shù)向Windows系統(tǒng)申請登記這個(gè)窗口類。這時(shí)Windows會為其分配一塊內(nèi)存來存放該類的全部信息,這個(gè)內(nèi)存塊稱為窗口類內(nèi)存塊。當(dāng)應(yīng)用程序要創(chuàng)建一個(gè)屬于某一已登記窗口類的窗口時(shí),Windows便為這個(gè)窗口分配一塊內(nèi)存,即窗口內(nèi)存塊,用來存放與該窗口有關(guān)的專用信息。這些信息一部分來自傳遞給窗口創(chuàng)建函數(shù)CreateWindow() 或CreateWindowEx()的參數(shù)信息,另一部分則來自所屬窗口類的窗口類內(nèi)存塊,其中參數(shù)lpfnWndProc便被Windows從窗口類內(nèi)存塊復(fù)制到為新創(chuàng)建窗口分配的窗口內(nèi)存塊中。當(dāng)有消息被發(fā)送到這個(gè)窗口時(shí),Windows檢查該窗口內(nèi)存塊中的窗口函數(shù)地址(lpfnWndProc),并調(diào)用該地址上的函數(shù)來處理這些消息。
所謂窗口子類化,實(shí)際上就是改變窗口內(nèi)存塊中的有關(guān)參數(shù)。由于這種修改只涉及到一個(gè)窗口的窗口內(nèi)存塊,因此它不會影響到屬于同一窗口類的其它窗口的功能和表現(xiàn)。窗口子類化中最常見的是修改窗口內(nèi)存塊中的窗口函數(shù)地址(lpfnWndProc),使其指向一個(gè)新的窗口函數(shù),從而改變原窗口函數(shù)的處理方法,改進(jìn)其功能。
3. 實(shí)現(xiàn)過程
首先利用MFC建立一個(gè)基于對話框的應(yīng)用程序NewDialg; 在對話框中添加一個(gè)ID為IDC_DEIT1的編輯控件資源。
并派生一個(gè)自己的類CNewEdit (它的基類為CEdit)。通過這個(gè)類實(shí)現(xiàn)對該編輯控件的限制輸入。
?。?) 處理CNewEdit的消息函數(shù)OnChar:
void CNewEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
TCHAR ch[20];
GetWindowText(ch,20);
//處理只能輸入A,B,C,D的情況
if (strlen(ch) == 1 && (nChar <= ‘D‘ && nChar >= ‘A‘)) return;
if (nChar != ‘A‘ && nChar != ‘B‘ && nChar != ‘C‘ && nChar!= ‘D‘ ) return;
CEdit::OnChar(nChar, nRepCnt, nFlags);
}
(2) 為CNewDialogDlg類中添加一個(gè)數(shù)據(jù)成員CNewEdit m_edit,并在CtestDlg::OnInitDialog( )中加入下面代碼:
m_edit.SubclassDlgItem(IDC_EDIT1,this);
m_edit.SetWindowText("<請輸入A、B、C、D>"); //提示可以輸入的內(nèi)容
?。?)處理ID為IDC_EDIT1的控件向?qū)υ捒虬l(fā)送的通知消息:EN_SETFOCUS:
void CNewDialogDlg::OnSetfocusEdit1( ) {
// TODO: Add your control notification handler code here
m_edit.SetWindowText("");
m_edit.SetFocus( );}
4 過程分析
下面我們看一下m_edit如何控制程序中資源編號為:IDC_EDIT1的控件的。
大家都知道,控制Windows窗口、控件、資源……都是通過它們的句柄來實(shí)現(xiàn),如HHANDLE、HWND、HDC都是句柄,它表現(xiàn)為一個(gè)32位長整形數(shù)據(jù),存放于Windows中的特定區(qū)域,我們可以把它理解為指向我們想控制的窗口、控件、資源的索引,有了它,我們就可以控制我們想要控制的對象。
那么CNewEdit的數(shù)據(jù)成員m_edit要想控制IDC_EDIT1,也要通過它的句柄,這就要通過SubclassDlgItem函數(shù)來實(shí)現(xiàn)。
BOOL CWnd::SubclassDlgItem(UINT nID, CWnd* pParent){
ASSERT(pParent != NULL);
ASSERT(::IsWindow(pParent->m_hWnd));
// check for normal dialog control first
HWND hWndControl = ::GetDlgItem(pParent->m_hWnd, nID);
if (hWndControl != NULL)
return SubclassWindow(hWndControl);
……
}
SubclassDlgItem函數(shù)是CWnd的一個(gè)成員函數(shù),它開始時(shí)對傳入的父窗口做些檢查,然后就是先用hWndControl得到我們IDC_EDIT1控件的句柄,再調(diào)用SubclassWindow函數(shù),這個(gè)函數(shù)是實(shí)現(xiàn)的關(guān)鍵。
BOOL CWnd::SubclassWindow(HWND hWnd){
if (!Attach(hWnd))
return FALSE;
……
WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)AfxGetAfxWndProc());
……
}
在這個(gè)函數(shù)中,首先調(diào)用了Attach函數(shù),將hwand這個(gè)窗口句柄與一個(gè)窗口對象連接,在Attach函數(shù)中,有重要的一句: pMap->SetPermanent(m_hWnd = hWndNew, this); 顯然只要把窗口的句柄保存下來,就可以在系統(tǒng)中唯一地指定一個(gè)窗口,然后對該窗口進(jìn)行操作。在Attach 函數(shù)中把IDC_EDIT1 的句柄保存在了CnewEdit的成員變量m_hWnd 中,那么在m_edit.SetWindowText("<請輸入A、B、C>")中,正是通過這個(gè)數(shù)據(jù)成員m_hWnd實(shí)現(xiàn)對IDC_EDIT1控制的:
void CWnd::SetWindowText(LPCTSTR lpszString){
ASSERT(::IsWindow(m_hWnd));
if (m_pCtrlSite == NULL)
::SetWindowText(m_hWnd, lpszString);
else
m_pCtrlSite->SetWindowText(lpszString);
}
雖然通過句柄實(shí)現(xiàn)了m_edit對IDC_EDIT1的控制,對CNewEdit的WM_CHAR的處理是還要通過SubclassWindow函數(shù)來實(shí)現(xiàn),在SubclassWindow中有這樣一句:
WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)AfxGetAfxWndProc());
其中AfxGetAfxWndProc()是我們自己的窗口處理函數(shù),在其中處理過我們感興趣的消息后,通過返回的原窗口處理函數(shù)指針oldWndProc來把其它消息按標(biāo)準(zhǔn)方法處理掉,這樣當(dāng)程序收到發(fā)給Edit的WM_CHAR時(shí),本應(yīng)調(diào)用EDIT標(biāo)準(zhǔn)窗口處理函數(shù),現(xiàn)在被改為調(diào)用LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)了,然后WM_CHAR消息進(jìn)行一系列的過程,最終成功到達(dá)我們的處理函數(shù)CNewEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags), 實(shí)現(xiàn)對編輯控件的限制輸入。
參考文獻(xiàn):
[1] aaaaaaaaaawww.msdn.com
[2] 楊曉鵬 《Visual C++ 7.0 實(shí)用編程技術(shù)》 北京 中國水利水電出版社。