CWnd 在 MFC 階層中的地位
CWnd 是所有視窗類別(CFrameWnd、CDialog、CView、CButton、CEdit、CListCtrl 等)的基類。掌握 CWnd 的核心成員函式,等於掌握了 MFC 視窗操作的通用鑰匙。

視窗狀態控制
| 函式 | 用途 | 範例 |
EnableWindow(BOOL bEnable = TRUE) | 啟用/禁用視窗 | pBtn->EnableWindow(FALSE); 灰化按鈕 |
IsWindowEnabled() | 查詢啟用狀態 | if (pEdit->IsWindowEnabled()) ... |
ModifyStyle(DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0) | 修改視窗風格 | pWnd->ModifyStyle(0, WS_BORDER); 加邊框 |
ModifyStyleEx(...) | 修改擴充風格 | pWnd->ModifyStyleEx(0, WS_EX_CLIENTEDGE); |
IsWindowVisible() | 檢查是否可見 | 含 WS_VISIBLE 且非被遮蓋 |
ShowWindow(int nCmdShow) | 顯示/隱藏/最小化/最大化 | 見下表 nCmdShow 常用值 |
ShowWindow 常用 nCmdShow 值
| 常數 | 效果 |
SW_HIDE | 隱藏視窗 |
SW_SHOW | 以目前大小/位置顯示 |
SW_SHOWMAXIMIZED | 最大化顯示 |
SW_SHOWMINIMIZED | 最小化到工作列 |
SW_RESTORE | 還原(從最小/最大化返回) |
SW_MINIMIZE | 最小化但不啟用 |
視窗位置與大小
| 函式 | 用途 | 備註 |
MoveWindow(LPCRECT lpRect, BOOL bRepaint = TRUE) | 移動並調整大小 | 座標為父視窗客戶區座標 |
GetWindowRect(LPRECT lpRect) | 取得螢幕座標矩形 | 含標題列、邊框 |
GetClientRect(LPRECT lpRect) | 取得客戶區矩形 | 左上角永遠 (0,0) |
SetWindowPos(const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags) | 綜合調整 Z-order、位置、大小 | 最強大,可單獨改一項 |
IsIconic() | 是否最小化 | |
IsZoomed() | 是否最大化 | |
SetWindowPos 常用 nFlags 組合
SWP_NOSIZE | SWP_NOMOVE:僅改 Z-order(pWndInsertAfter 用 HWND_TOP/HWND_BOTTOM/HWND_TOPMOST)。
SWP_NOZORDER | SWP_NOSIZE:僅移動位置。
SWP_FRAMECHANGED:強制重繪非客戶區(改變風格後常需加此旗標)。
無效區與重繪
| 函式 | 用途 |
Invalidate(BOOL bErase = TRUE) | 整個客戶區標記為無效,觸發 WM_PAINT |
InvalidateRect(LPCRECT lpRect, BOOL bErase = TRUE) | 指定矩形區域無效 |
InvalidateRgn(CRgn* pRgn, BOOL bErase = TRUE) | 指定區域無效 |
ValidateRect(LPCRECT lpRect) | 將區域標記為有效(取消重繪) |
UpdateWindow() | 立即發送 WM_PAINT(跳過訊息佇列) |
RedrawWindow(...) | 進階重繪控制 |
bErase = TRUE:WM_ERASEBKGND 會被發送,背景會被重繪;FALSE:保留現有像素,僅重繪前景。
視窗查找與導航
| 函式 | 用途 | 備註 |
static CWnd* FindWindow(LPCTSTR lpszClassName, LPCTSTR lpszWindowName) | 全系統查找頂層視窗 | 參數可為 NULL 表任意匹配 |
GetParent() | 取得父視窗指標 | 對話框的父視窗通常是框架視窗 |
GetDlgItem(int nID) | 依 ID 取得子視窗指標 | 返回 CWnd*,需轉型 |
GetDlgItemInt(int nID, BOOL* lpTrans, BOOL bSigned) | 直接取得子視窗整數內容 | 自動轉換文字→數值 |
GetDlgItemText(int nID, CString& rString) | 直接取得子視窗文字 | |
GetDlgCtrlID() | 取得自身控件 ID | 常用於訊息處理函式識別來源 |
static CWnd* WindowFromPoint(POINT point) | 螢幕座標點命中視窗 | 返回最上層視窗 |
static CWnd* FromHandle(HWND hWnd) | HWND → CWnd* 暫時包裝 | 返回臨時物件,**不可長期保存** |
IsChild(const CWnd* pWnd) | 判斷是否為子視窗(含孫視窗) | |
計時器
// 建立計時器
UINT_PTR nTimerID = SetTimer(nIDEvent, nElapse, NULL); // 回呼為 NULL → 收到 WM_TIMER
// nIDEvent: 自訂 ID,nElapse: 毫秒間隔
// 銷毀計時器
KillTimer(nTimerID); // 或 KillTimer(nIDEvent)
// 訊息映射
ON_WM_TIMER()
void CMyWnd::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == MY_TIMER_ID) { /* 定期任務 */ }
}
注意:SetTimer 回呼函式模式(非 NULL)在 MFC 中較少用,因需靜態函式且難以存取類別成員。
可覆寫的虛擬函式(訊息處理入口)
| 虛擬函式 | 觸發時機 | 典型用途 |
OnCreate(LPCREATESTRUCT) | 視窗建立時(WM_CREATE) | 建立子視窗、初始化 GDI 物件、啟動計時器 |
OnDestroy() | 視窗銷毀時(WM_DESTROY) | 釋放資源、殺計時器、儲存設定 |
OnSize(UINT nType, int cx, int cy) | 視窗大小改變(WM_SIZE) | 重新佈局子視窗、調整捲軸範圍 |
OnMove(int x, int y) | 視窗移動(WM_MOVE) | 儲存視窗位置 |
OnPaint() | 需重繪(WM_PAINT) | View 類別勿覆寫此函式,改用 OnDraw |
OnGetMinMaxInfo(MINMAXINFO*) | 查詢最大/最小追蹤尺寸 | 限制視窗可調整範圍 |
OnSetFocus(CWnd* pOldWnd) | 獲得輸入焦點 | 顯示游標、啟用編輯模式 |
OnKillFocus(CWnd* pNewWnd) | 失去輸入焦點 | 隱藏游標、儲存編輯內容 |
OnSysCommand(UINT nID, LPARAM lParam) | 系統選單命令(最大化、關閉等) | 攔截 SC_CLOSE 等 |
OnHScroll/OnVScroll(...) | 捲軸操作 | 處理獨立捲軸控件 |
實用小技巧
- 螢幕座標 ↔ 客戶區座標:
ScreenToClient() / ClientToScreen()(點或矩形皆適用)。
- 取得視窗類別名稱:
GetClassName()(MFC 類別通常為 AfxWnd... 或自訂名稱)。
- 安全取得 HWND:
GetSafeHwnd() 返回 m_hWnd 或 NULL(物件未附加時不會崩潰)。
- Attach/Detach:
Attach(hWnd) 將現有 HWND 綁定到 CWnd 物件;Detach() 解除綁定並返回 HWND,物件不銷毀視窗。
- 非同步執行:
PostMessage(WM_USER_DO_WORK) 讓耗時任務在訊息迴圈中執行,避免凍結 UI。