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);
// x, y 為繪圖位置,必要時用迴圈重複繪製
pDC->BitBlt(x, y, BmpInfo.bmWidth, BmpInfo.bmHeight, pDisplayMemDC, 0, 0, SRCCOPY);
pDisplayMemDC->SelectObject(pOldBitmap);
pBitmap->DeleteObject();
ReleaseDC(pDisplayMemDC);
delete pDisplayMemDC;
delete pBitmap;
關鍵點:建立相容 DC → 選入位圖 → BitBlt 繪製 → 還原釋放資源。座標系統(客戶區/螢幕/視窗座標轉換)必須熟稔。
// 標題列滑鼠左鍵按下
void OnNcLButtonDown(UINT nHitTest, CPoint point);
// 標題列滑鼠移動
void OnNcMouseMove(UINT nHitTest, CPoint point);
// 視窗程序核心分派
LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam);
BOOL DrawTitleBar(CDC* pDC); // 返回是否成功繪製
OnNcLButtonDown:判斷點擊位置是否落在關閉/最大化/最小化按鈕區域,觸發對應動作(SendMessage(WM_SYSCOMMAND, SC_CLOSE) 等)。OnNcMouseMove:追蹤滑鼠是否懸停在系統按鈕上,切換按鈕圖片(正常/熱追蹤/按下狀態),呼叫 InvalidateRect 觸發重繪。DrawTitleBar:根據視窗寬度、按鈕狀態,分段 BitBlt 繪製標題列左/中/右圖片、系統按鈕圖片、邊框四角與邊緣。DefWindowProc:攔截 WM_NCPAINT 呼叫 DrawTitleBar,攔截 WM_NCHITTEST 回傳正確 HTCAPTION/HTCLOSE 等讓系統知道滑鼠位置。WM_NCPAINT 等非客戶區訊息)。
單一視窗自繪容易,但多視窗程式統一風格極難。下一篇將探討:如何將自繪視窗子類化、讓所有視窗繼承統一風格、美化標準控件(Edit、ListBox 等)、實現控件隨視窗自動調整大小。敬請期待!