金山詞霸、QuickTime 等軟體擁有獨特介面風格。本文分享自繪非客戶區(標題列、邊框、系統按鈕、控制項)的核心技術與完整範例代碼。

紅框標示區域皆需美工繪製位圖:標題列左/中/右、系統按鈕(一般/滑鼠進入/按下)、邊框左/右/下、底部狀態列等。圖素品質直接決定介面質感。
CBitmap* pBitmap = new CBitmap;
BITMAP BmpInfo;
CBitmap* pOldBitmap;
CDC* pDisplayMemDC = new CDC;
pDisplayMemDC->CreateCompatibleDC(pDC);
pBitmap->LoadBitmap(IDB_TITLE_LEFT);
pOldBitmap = (CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
pBitmap->GetBitmap(&BmpInfo);
pDC->BitBlt(x, y, BmpInfo.bmWidth, BmpInfo.bmHeight, pDisplayMemDC, 0, 0, SRCCOPY);
pDisplayMemDC->SelectObject(pOldBitmap);
pBitmap->DeleteObject();
ReleaseDC(pDisplayMemDC);
delete pDisplayMemDC;
delete pBitmap;
螢幕座標 ↔ 視窗座標 ↔ 客戶區座標轉換必須熟練,否則繪圖位置會錯位。
OnNcLButtonDown:非客戶區左鍵按下(處理系統按鈕點擊)OnNcMouseMove:非客戶區滑鼠移動(按鈕懸停效果)DefWindowProc:攔截 WM_MOVE、WM_PAINT、WM_NCPAINT、WM_NCACTIVATE 等觸發重繪BOOL CTitleBarDlg::DrawTitleBar(CDC* pDC)
{
if (!m_hWnd) return FALSE;
// 1. 計算標題列、按鈕、邊框矩形區域
// 2. 依序繪製:標題列左/中/右、最小化/最大化/關閉按鈕、視窗標題文字
// 3. 繪製左/右/下邊框(平鋪位圖)
// 4. 釋放所有 GDI 物件
// 完整代碼請參考下載專案,此處因篇幅略過
}
void CTitleBarDlg::OnNcMouseMove(UINT nHitTest, CPoint point)
{
CDC* pDC = GetWindowDC();
// 座標轉換:螢幕 → 視窗客戶區
CRect rtWnd; GetWindowRect(&rtWnd);
point.x -= rtWnd.left; point.y -= rtWnd.top;
// 判斷滑鼠在哪個按鈕上,載入對應位圖並重繪
if (m_rtButtExit.PtInRect(point)) { /* 載入關閉按鈕懸停圖 */ }
else if (m_rtButtMin.PtInRect(point)) { /* 載入最小化懸停圖 */ }
else if (m_rtButtMax.PtInRect(point)) { /* 載入最大化/還原懸停圖 */ }
else { /* 載入一般狀態圖 */ }
// 繪製按鈕區域...
ReleaseDC(pDC);
CDialog::OnNcMouseMove(nHitTest, point);
}
void CTitleBarDlg::OnNcLButtonDown(UINT nHitTest, CPoint point)
{
// 座標轉換同 OnNcMouseMove
// 依點擊區域發送對應系統命令:
// m_rtIcon -> 關於訊息
// m_rtButtHelp -> WM_HELP
// m_rtButtExit -> WM_CLOSE
// m_rtButtMin -> SC_MINIMIZE
// m_rtButtMax -> SC_MAXIMIZE / SC_RESTORE
// 其他區域 -> Default() 保留視窗拖曳功能
}
LRESULT CTitleBarDlg::DefWindowProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
LRESULT lr = CDialog::DefWindowProc(msg, wParam, lParam);
if (!::IsWindow(m_hWnd)) return lr;
if (msg == WM_MOVE || msg == WM_PAINT || msg == WM_NCPAINT ||
msg == WM_NCACTIVATE || msg == WM_NOTIFY)
{
CDC* pDC = GetWindowDC();
if (pDC) { DrawTitleBar(pDC); ReleaseDC(pDC); }
}
return lr;
}

單一視窗美化容易,多視窗統一風格較難。後續將探討子類化技術讓所有視窗共享風格、控制項美化與自動佈局。歡迎交流:QQ 34544052 / Email Song_0962@sina.com