MFC 文檔/視圖架構複雜,初學者常在「程式碼放哪裡」上踩坑。本文透過兩個經典案例——設定視圖背景色、動態修改對話框標題——揭示 MFC 訊息迴路與視窗生命週期的關鍵時機差異。
SetSysColors修改 COLOR_WINDOW 會影響**全系統**所有視窗,造成視覺混亂,不可取。
OnDraw 中繪製背景void CTestView::OnDraw(CDC* pDC)
{
CRect rectClient; GetClientRect(rectClient);
CBrush brush; brush.CreateSolidBrush(RGB(255,0,0));
pDC->DPtoLP(rectClient);
pDC->FillRect(rectClient, &brush);
// ... 其他繪圖
}
後果:OnDraw 因視窗縮放、資料更新(Invalidate)頻繁被呼叫。每次先由系統用 WM_ERASEBKGND 以預設白色擦除,再由 OnDraw 重疊紅色,**必然產生閃爍**。
WM_ERASEBKGND在 OnDraw 之前,Windows 會發送 WM_ERASEBKGND 要求擦除背景。預設由視窗類別的 hbrBackground 畫刷處理(通常是 COLOR_WINDOW)。我們應在此訊息中完成背景填充,並回傳 TRUE 告知系統「已處理」,避免預設擦除動作。
COLORREF m_viewBkColor 及存取函式,支援序列化(開啟文檔自動還原背景色)。WM_ERASEBKGND 新增 OnEraseBkgnd:BOOL CTestView::OnEraseBkgnd(CDC* pDC)
{
CRect rect; pDC->GetClipBox(&rect); // 僅取得需重繪區域
CBrush brush; brush.CreateSolidBrush(GetDocument()->GetViewBkColor());
pDC->FillRect(&rect, &brush);
return TRUE; // 關鍵:阻止預設擦除
}void CTestView::OnChangeViewBkcolor()
{
CColorDialog dlg;
if (dlg.DoModal() == IDOK) {
GetDocument()->SetViewBkColor(dlg.GetColor());
InvalidateRect(NULL); // 觸發 WM_ERASEBKGND → OnEraseBkgnd
}
}優勢:無閃爍、僅重繪裁剪區域、背景色隨文檔持久化。
DoModal 前呼叫 SetWindowTextCTestDialog dlg;
dlg.SetWindowText(_T("標題-1")); // 無效!
dlg.DoModal();
原因:MFC 對話框為**動態建立**。CTestDialog dlg 僅構造 C++ 物件,真正的視窗控制代碼(HWND)在 DoModal() 內部呼叫 CreateDialogIndirect 時才建立。視窗未建立前呼叫 SetWindowText 無效;DoModal 返回後視窗已銷毀,同樣無效。
OnInitDialog 中設定public: CString m_strCaption;CTestDialog dlg;
dlg.m_strCaption = _T("標題-1");
dlg.DoModal();OnInitDialog:BOOL CTestDialog::OnInitDialog()
{
CDialog::OnInitDialog(); // 必須呼叫基類
SetWindowText(m_strCaption); // 此時 HWND 已存在
return TRUE;
}原理:DoModal 內部流程:建立視窗 → 發送 WM_INITDIALOG → MFC 對映至 OnInitDialog → 顯示視窗 → 進入訊息迴圈。此時設定標題剛好在視窗可見前完成。
兩個技巧的共同點:**相同的程式碼,放在正確的訊息/生命週期節點,效果截然不同**。掌握 MFC 訊息對映順序、視窗建立/銷毀時機、Invalidate/WM_PAINT/WM_ERASEBKGND 互動關係,是寫出高效、無閃爍、行為正確 VC++ 程式的關鍵。