既然窗口操作是Windows編程的核心內容,那麼窗口基類CWnd在MFC類結構中的核心地位就無可爭議了。它派生於CCmdTarget類,是最基本的GUI對象。我們在屏幕上看到的一切對象都與窗口有關,它們或者派生於CWnd,屬繼承關係,如對話框、工具欄、狀態欄、子控件;或者被CWnd合成,屬服務員與服務對象的關係,如圖標、菜單、顯示設備。
窗口類CWnd與Windows操作系統管理是顯示(或隱藏)給用戶的,作爲應用程序的一種表現形式的窗口是兩個概念。前者通過一個窗口句柄操作後者,不同的操作被封裝爲不同的成員函數。而後者,操作系統爲其開闢了一個內存區,存儲一個數據結構,進行管理。後者包括窗口風格、窗口類、當前狀態等信息。
其實我們可以理解,CWnd是MFC中所有組件對象的父類,GetDlgItem()可以通過組件的ID號得到組件對象的地址,這裏是以父類指針CWnd*返回,如果需要使用可以強轉,CWnd類封裝的窗口操作主要包括窗口的創建和銷燬、操作窗口風格、操作窗口狀態、窗口子類化、獲取指定窗口等。除窗口操作外,CWnd類還實現以下功能。
通過取得窗口的顯示設備上下文,控制窗口的繪製。主要成員函數如下。
■ GetDC():取得客戶區顯示設備上下文。
■ GetWindowDC():取得整個窗口的顯示設備上下文。
■ ReleaseDC(): 釋放設備上下文。
■ BeginPaint():準備繪製客戶區。
■ EndPaint():結束繪製客戶區。
■ PrintClient():繪製或打印客戶區。
■ RedrawWindow():重畫客戶區的某區域。
操作子控件的成員函數如下。
■ GetDlgItem():取得(臨時的)控件對象指針。
■ SetDlgItemText()和GetDlgItemText():設置(取得)控件標題。
■ SubclassDlgItem():將控件句柄與相應類對象關聯。
■ DlgDirList()和DlgDirListComboBox():以文件列表或目錄列表填充(組合框)列表框。
■ CheckDlgButton()和CheckRadioButton():設置複選框(單選按鈕)狀態。
■ GetNextDlgTabItem():取得下一個WS_TABSTOP風格的控件。
可以爲窗口定時器事件設置定時器或銷燬定時器。
■ SetTimer():設置定時器。
■ KillTimer():銷燬定時器。
進行窗口消息處理的函數如下。
■ GetCurrentMessage():取得當前被處理的消息。
■ PreTranslateMessage():可重載的虛函數。被UI線程的消息循環調用,可以過濾窗口收到的消息,過濾出的消息得以分發。
■ SendMessage() :向本窗口發送消息。不通過消息循環,直接調用窗口函數處理消息。窗口函數執行完畢,該函數才返回。
■ PostMessage():向本窗口寄送消息。將消息放入消息隊列,立即返回。
■ Default():爲所有的窗口消息提供默認處理。對無需處理的消息或希望默認處理的消息,可以使用Default()。
前面介紹了消息的默認處理函數Default(),該函數一般要調用一個默認的窗口過程,對當前消息執行默認的系統級處理。例如對於窗口的WM_LBUTTONDOWN消息,默認處理函數要爲該窗口設置焦點。對於許多消息,執行這種默認的處理是完全必要的。這樣,程序員只需在消息到來之際執行相關的用戶級處理,對這些消息的系統級處理不必關心。所以,在一個消息處理函數中,如果允許當前消息正常執行,最終就應該調用默認處理函數Default()。例如,下面要控制一個CEdit控件的接收字符集,不允許輸入0~9的數字,並將輸入的小寫字母轉換爲大寫字母。程序如下:
//class CEditnew :public CEdit
void CEditnew::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
TRACE("%c/n",nChar);
if(nChar>=''0'' && nChar<=''9'')
return; //過濾掉當前消息
if(nChar>=''a'' && nChar<=''z'')
{
nChar-=32;
LPARAM lpara=nFlags<<16|nRepCnt;
//更改消息參數,重新執行該消息處理函數
SendMessage(WM_CHAR,(WPARAM)nChar,lpara);
return;
}
//執行默認的處理過程,將當前字符追加到控件的文本中
Default();
}
然而,在實際的編程中,很少直接調用Default(),更多是調用基類的消息處理函數。例如在上例中,以 CEdit::OnChar(nChar, nRepCnt, nFlags);
代替Default()要更合理。因爲針對大部分消息,CWnd類已經定義了相應的處理函數,封裝對Default()的調用。例如,針對WM_CHAR消息的處理函數這樣定義:
void CWnd::OnChar(UINT, UINT, UINT)
{ Default(); }
同時,對於一些特殊的消息,僅執行系統級的處理是不夠的,框架必須針對消息完成一些例行的操作。例如,收到WM_SYSCOLORCHANGE消息後,要將系統顏色的變化通知所有子窗口;收到WM_NCDESTROY消息,要做必要的清理工作,並使MFC窗口對象與已經關閉的窗口分離。針對這些消息,CWnd類也定義了相應的處理函數,封裝框架的例行處理。下面是針對WM_SYSCOLORCHANGE消息的處理函數。
void CWnd::OnSysColorChange()
{
CWinApp* pApp = AfxGetApp();
if (pApp != NULL && pApp->m_pMainWnd == this)
{
//調整主窗口的控制條(工具條、狀態條等)顏色
afxData.UpdateSysColors();
}
#ifndef _AFX_NO_CTL3D_SUPPORT
if (!afxContextIsDLL)
{
if (AfxGetThread() != NULL && AfxGetThread()->m_pMainWnd == this)
{
_AFX_CTL3D_STATE* pCtl3dState = _afxCtl3dState;
//調整3D顯示
if (pCtl3dState->m_pfnColorChange != NULL)
(*pCtl3dState->m_pfnColorChange)();
}
}
#endif
//通知所有的子窗口
if (!(GetStyle() & WS_CHILD))
SendMessageToDescendants(WM_SYSCOLORCHANGE, 0, 0L, TRUE, TRUE);
//最後執行默認處理
Default();
}