當控制項需繪製時,Windows 發送 WM_CTLCOLORXXX (如 WM_CTLCOLOREDIT, WM_CTLCOLORLISTBOX) 給**父視窗**。父視窗可返回 **HBRUSH** (背景筆刷),並在此前設定 CDC::SetTextColor、SetBkColor、SetBkMode。MFC 封裝為虛擬函數 CWnd::OnCtlColor。
// 父視窗類別 (如 CDialog 派生類)
HBRUSH CMyDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // 基類預設處理
// 針對所有編輯控制項設定紅字
if (nCtlColor == CTLCOLOR_EDIT) {
pDC->SetTextColor(RGB(255, 0, 0)); // 紅色文字
// pDC->SetBkColor(RGB(255, 255, 0)); // 黃背景 (需返回對應筆刷)
// return m_yellowBrush; // 需成員變數 CBrush m_yellowBrush
}
// 針對特定控制項 ID
if (pWnd->GetDlgCtrlID() == IDC_SPECIAL_EDIT) {
pDC->SetTextColor(RGB(0, 0, 255)); // 藍字
}
return hbr;
}
| 優點 | 缺點 |
|---|---|
| 集中管理所有子控制項顏色 | 違反封裝:父視窗需知曉子控制項細節 |
| 無需修改控制項類別 | 控制項類別復用性差 (換父視窗顏色失效) |
| 動態依條件變色容易 | 父視窗類別膨脹、耦合度高 |
父視窗 OnCtlColor 未處理 (返回預設) → MFC 將訊息**反射**回控制項 → 控制項處理 WM_CTLCOLOR_REFLECT (映射為 CtlColor 成員函數)。
CMyListBox : public CListBox。
class CMyListBox : public CListBox
{
// ...
private:
COLORREF m_clrFore; // 前景色
COLORREF m_clrBack; // 背景色
CBrush m_brush; // 背景筆刷
};
CMyListBox::CMyListBox()
{
m_clrFore = RGB(255, 255, 0); // 黃字
m_clrBack = RGB(0, 0, 255); // 藍底
m_brush.CreateSolidBrush(m_clrBack);
}
WM_CTLCOLOR (顯示為 =WM_CTLCOLOR) → 生成 CtlColor:
HBRUSH CMyListBox::CtlColor(CDC* pDC, UINT nCtlColor)
{
pDC->SetTextColor(m_clrFore);
pDC->SetBkColor(m_clrBack);
return (HBRUSH)m_brush.GetSafeHandle(); // 返回筆刷句柄
}
CMyListBox (或動態 SubclassDlgItem)。| 優點 | 缺點 |
|---|---|
| 控制項封裝完整,隨處可用 | 需建立自訂控制項類別 |
| 符合 OO 封裝原則 | 每種控制項類型需個別衍生類 |
| 狀態隨控制項生命週期 | 動態換色需額外介面 (SetColor 方法) |
class CMyListBox : public CListBox
{
public:
void SetColors(COLORREF clrFore, COLORREF clrBack)
{
m_clrFore = clrFore;
m_clrBack = clrBack;
m_brush.DeleteObject();
m_brush.CreateSolidBrush(m_clrBack);
Invalidate(); // 觸發重繪
}
// ...
};
| 控制項類型 | WM_CTLCOLOR 代碼 | nCtlColor 值 |
|---|---|---|
| Edit / ComboBox 編輯區 | WM_CTLCOLOREDIT | CTLCOLOR_EDIT |
| ListBox | WM_CTLCOLORLISTBOX | CTLCOLOR_LISTBOX |
| ComboBox 下拉列表 | WM_CTLCOLORLISTBOX | CTLCOLOR_LISTBOX |
| Button (僅 Owner Draw) | WM_CTLCOLORBTN | CTLCOLOR_BTN |
| Static (文字/框架/圖片) | WM_CTLCOLORSTATIC | CTLCOLOR_STATIC |
| Dialog 背景 | WM_CTLCOLORDLG | CTLCOLOR_DLG |
| MessageBox | WM_CTLCOLORMSGBOX | CTLCOLOR_MSGBOX |
| ScrollBar | WM_CTLCOLORSCROLLBAR | CTLCOLOR_SCROLLBAR |
CreateSolidBrush 失敗返回 NULL,返回 NULL 筆刷導致繪製異常。成員 CBrush 自動管理生命週期。SetBkMode(OPAQUE) (預設)。透明模式 TRANSPARENT 會忽略背景色。SS_NOTIFY 樣式或 SS_OWNERDRAW 影響。純文字 Static 用 CTLCOLOR_STATIC 設 SetTextColor 有效。OnCtlColor 必須調用基類實作 (返回預設筆刷),不可直接返回自訂筆刷攔截所有。若父視窗需處理部分控制項,**僅處理目標控制項,其餘調用基類**。WM_SETTINGCHANGE 重建。MFC 訊息反射機制讓控制項「自己負責自己的外觀」,是從「父視窗管一切」向「組件化封裝」演進的關鍵一步。