理解 MFC 視窗類別物件 (CWnd 衍生) 與實際 HWND 視窗的關係,是掌握動態建立控件的關鍵。本文以在 CMainFrame 建立按鈕為例,完整說明 CButton 物件建立、Create 關聯 HWND、ShowWindow 顯示、成員變數管理生命週期的完整流程。
CObject
└─ CCmdTarget
└─ CWnd
└─ CButton
CButton 繼承 CWnd,擁有 m_hWnd 儲存視窗控制代碼,所有視窗操作均委派給此 HWND。
BOOL Create(LPCTSTR lpszCaption, DWORD dwStyle,
const RECT& rect, CWnd* pParentWnd, UINT nID);
| 參數 | 說明 |
|---|---|
| lpszCaption | 按鈕顯示文字 |
| dwStyle | 視窗樣式 (WS_CHILD, WS_VISIBLE...) | 按鈕樣式 (BS_PUSHBUTTON...),可位元或組合 |
| rect | 位置與大小,相對父視窗客戶區座標 (CRect 構造) |
| pParentWnd | 父視窗指標,傳遞 this 即為當前框架 |
| nID | 控件 ID,供命令路由識別 |
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
// ... 工具列/狀態列建立 ...
CButton btn; // 區域變數
btn.Create(_T("按鈕"), WS_CHILD | BS_DEFPUSHBUTTON,
CRect(0,0,100,100), this, 123);
return 0; // btn 離開作用域 → 建構函數 → DestroyWindow → 視窗消失
}
原因:CButton 區域變數在 OnCreate 結束時解構,CWnd::~CWnd 偵測 m_hWnd 有效則呼叫 DestroyWindow,導致剛建立的按鈕視窗被銷毀。
ClassView 右鍵 CMainFrame → Add Member Variable... → Type: CButton, Name: m_btn, Access: private。
產生標頭檔宣告:
class CMainFrame : public CFrameWnd
{
private:
CButton m_btn; // 成員變數,生命週期 = CMainFrame
// ...
};
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;
// ... 工具列/狀態列建立代碼 ...
// 1. 建立按鈕視窗,關聯 m_btn 物件
m_btn.Create(_T("按鈕"), WS_CHILD | BS_DEFPUSHBUTTON,
CRect(0, 0, 100, 100), this, 123);
// 2. 顯示按鈕 (若未加 WS_VISIBLE)
m_btn.ShowWindow(SW_SHOWNORMAL);
return 0;
}
| 觀念 | 說明 |
|---|---|
| C++ 物件 ≠ HWND | CButton btn; 只是 C++ 物件,未建立視窗。Create 才建立 HWND 並寫入 m_hWnd。 |
| 解構 = 銷毀視窗 | CWnd::~CWnd 若 m_hWnd != NULL 會呼叫 DestroyWindow。 |
| 成員變數 = 延長生命 | 將 CButton 宣告為成員變數,視窗隨框架視窗銷毀而銷毀,符合預期。 |
| ShowWindow 必要性 | 未指定 WS_VISIBLE 時,Create 只建立不顯示,必須呼叫 ShowWindow。 |
| this 指標作父視窗 | 在 CMainFrame::OnCreate 中傳 this,按鈕成為框架視窗子視窗,座標相對框架客戶區。 |
m_btn.Create(_T("按鈕"), WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
CRect(0,0,100,100), this, 123);
// 省去 ShowWindow 呼叫
編譯執行,按鈕出現在主框架客戶區左上角 (0,0),寬高 100x100 像素,位於工具列區域 (因框架客戶區包含工具列)。
動態建立控件三部曲:成員變數宣告 → Create 關聯 HWND → ShowWindow/WS_VISIBLE 顯示。區域變數會導致視窗隨函數結束而銷毀,這是新手最常犯的錯誤。掌握 C++ 物件與 HWND 的生命週期綁定關係,是 MFC 視窗程式設計的核心功力。