預設的 TabControl 外觀由系統主題決定,難以滿足品牌色或狀態指示需求。解決關鍵在於將 DrawMode 設為 TabDrawMode.OwnerDrawFixed,並處理 DrawItem 事件,自行繪製每個索引標籤的背景、文字與圖示。
在表單建構函式或 Load 事件中完成初始化:
this.tabControl1.DrawMode = TabDrawMode.OwnerDrawFixed;
this.tabControl1.DrawItem += new DrawItemEventHandler(this.tabControl1_DrawItem);
以下範例展示如何為選中索引標籤套用漸層背景與粗體字,未選中則使用純色背景:
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
var tabControl = (TabControl)sender;
var tabPage = tabControl.TabPages[e.Index];
var tabRect = tabControl.GetTabRect(e.Index);
var isSelected = (e.Index == tabControl.SelectedIndex);
// 1. 定義字型與筆刷
Font tabFont = isSelected ? new Font(e.Font, FontStyle.Bold) : e.Font;
Brush backBrush = isSelected
? new System.Drawing.Drawing2D.LinearGradientBrush(
tabRect, SystemColors.ControlLightLight, SystemColors.Control, LinearGradientMode.BackwardDiagonal)
: new SolidBrush(Color.White);
Brush foreBrush = isSelected ? Brushes.Black : Brushes.Black; // 可依設計調整
// 2. 繪製背景
e.Graphics.FillRectangle(backBrush, tabRect);
// 3. 計算文字繪製區域(留白 4px 上邊距)
var textRect = new RectangleF(tabRect.X, tabRect.Y + 4, tabRect.Width, tabRect.Height - 4);
var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
// 4. 繪製文字
e.Graphics.DrawString(tabPage.Text, tabFont, foreBrush, textRect, sf);
// 5. 釋放非託管資源
if (backBrush is IDisposable d) d.Dispose();
if (tabFont != e.Font) tabFont.Dispose();
}
DrawItem 會頻繁觸發,建議將 Font、Brush、StringFormat 等可重用物件提升為欄位或靜態唯讀,避免每次重新分配。e.Graphics.DpiX/DpiY 計算縮放比例,或改用 TextRenderer.DrawText 獲得更清銳的文字渲染。TabPage.ImageIndex 有值,可在繪製文字前呼叫 e.Graphics.DrawImage 繪製圖示,並相應調整 textRect 的 X 座標。e.State.HasFlag(DrawItemState.Focus) 為 true 時呼叫 ControlPaint.DrawFocusRectangle。若需在索引標籤右側顯示關閉圖示(僅當索引標籤數 > 1 時),可擴展如下:
private const int CloseBtnSize = 14;
private readonly Image _closeNormal = Properties.Resources.close_normal;
private readonly Image _closeHover = Properties.Resources.close_hover;
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
// ... 前述背景與文字繪製碼 ...
// 繪製關閉按鈕(僅多索引標籤時)
if (tabControl.TabCount > 1)
{
var btnRect = new Rectangle(
tabRect.Right - CloseBtnSize - 4,
tabRect.Y + (tabRect.Height - CloseBtnSize) / 2,
CloseBtnSize, CloseBtnSize);
var img = (e.State & DrawItemState.HotLight) == DrawItemState.HotLight ? _closeHover : _closeNormal;
e.Graphics.DrawImage(img, btnRect);
// 建議將 btnRect 存入 Tag 或 Dictionary 供 MouseDown/MouseMove 判斷點擊區域
}
}
掌握 OwnerDrawFixed 與 DrawItem 事件,即可完全掌控 TabControl 的視覺表現。從簡單的顏色切換到複雜的漸層、圖示、關閉按鈕,皆能在統一邏輯中實現,打造符合產品設計語言的專業介面。