承接上一篇「還原視窗本貌」,本文解決自繪視窗常見的閃爍問題,並示範如何實作類似 Foxmail 風格的自訂標題列按鈕 (最小化、最大化、關閉) 與彩色主題繪製。
WinForms 自繪視窗閃爍主因在於 OnPaint / WM_PAINT 過度觸發與繪製區域過大。優化方向:
WS_EX_COMPOSITED 雙緩衝樣式、覆寫 CreateParams 設定 WS_CLIPCHILDREN / WS_CLIPSIBLINGS。NcPaint (非客戶區繪製) 中精確計算 Region,排除客戶區,僅繪製邊框、標題列、按鈕。BufferedGraphics 先繪製至記憶體位圖,再一次性 Blit 至螢幕 DC。主視窗 (MainForm) 採用完整自繪非客戶區 (Non-Client Area);子視窗則保留系統預設標題列,僅套用皮膚色彩。核心程式碼結構:
// 非客戶區繪製入口
private bool NcPaint(SkinningForm form, SkinEngine engine)
{
// 1. 取得視窗矩形、建立 Graphics (GetDCEx + DCX_CACHE | DCX_CLIPSIBLINGS | DCX_WINDOW)
// 2. 計算裁剪區域:排除客戶區,僅保留邊框與標題列區域
// 3. 離螢幕緩衝:BufferedGraphicsContext.Allocate
// 4. 依序繪製:
// - 標題列背景 (engine.SkinAppearance.DrawCaptionBackground)
// - 視窗圖示 (DrawIcon)
// - 標題文字 (DrawCaptionText)
// - 控制盒按鈕 (DrawCaptionControlBox) ← 自訂按鈕邏輯核心
// - 視窗邊框 (engine.SkinAppearance.DrawBorder)
// 5. 清理資源 (ReleaseDC, Dispose Region/Graphics)
}
Rectangle[] captionButtons = { closeBtn, maxBtn, minBtn }。WM_NCMOUSEMOVE, WM_NCMOUSELEAVE, WM_NCLBUTTONDOWN, WM_NCLBUTTONUP 更新按鈕狀態 (Normal/Hover/Pressed)。子視窗 (ChildForm) 不繁複自繪非客戶區,僅:
FormBorderStyle = None 並自行繪製單色邊框。SkinEngine.SkinColor.Back 控制。

下一篇將實作「動態換膚」:執行時期切換 SkinEngine 色彩方案,驗證架構擴充性。