WinForms MenuItem 設定 OwnerDraw = true 後,需重寫:
OnMeasureItem:計算選單項寬高。OnDrawItem:繪製背景、文字、圖示、快捷鍵、勾選框、分隔線等。本實現模仿 Office 2003 風格:頂層選單橫向佈局、子選單左側側邊欄、懸停/選中漸層高亮、圖示支援。
public class OfficeMenuItem : MenuItem
{
// 視覺資源(筆刷、筆、字型、StringFormat)
private readonly Font _font = new Font("微軟正黑體", 9f);
private readonly LinearGradientBrush _topHotBrush, _topSelBrush, _sideBarBrush;
private readonly SolidBrush _textBrush, _selBrush, _checkBrush, _grayBrush;
private readonly Pen _topBorderPen, _selBorderPen;
private readonly StringFormat _centerFmt = new StringFormat { Alignment = StringAlignment.Center };
private readonly StringFormat _farFmt = new StringFormat { Alignment = StringAlignment.Far };
public string IconPath { get; set; }
public OfficeMenuItem(bool useOfficeStyle, string text)
{
Text = text;
OwnerDraw = useOfficeStyle;
}
// 判斷是否為頂層選單(父容器為 MainMenu)
private bool IsTopLevel => Parent is MainMenu;
// 取得快捷鍵顯示字串
private string GetShortcutText()
{
if (!ShowShortcut || IsParent || Shortcut == Shortcut.None) return "";
string s = Shortcut.ToString();
return s.Replace("Ctrl", "Ctrl+").Replace("Alt", "Alt+").Replace("Shift", "Shift+");
}
protected override void OnMeasureItem(MeasureItemEventArgs e)
{
base.OnMeasureItem(e);
if (Text == "-") { e.ItemHeight = 5; e.ItemWidth = 10; return; }
string shortcut = GetShortcutText();
SizeF textSize = e.Graphics.MeasureString(Text + " " + shortcut, _font);
if (IsTopLevel)
{
e.ItemWidth = (int)textSize.Width + 8;
e.ItemHeight = (int)textSize.Height + 6;
}
else
{
e.ItemWidth = (int)textSize.Width + 36; // 左側 26px 側邊欄 + 間距
e.ItemHeight = (int)textSize.Height + 6;
}
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
if (!Visible) return;
base.OnDrawItem(e);
Graphics g = e.Graphics;
Rectangle bounds = e.Bounds;
bool selected = (e.State & DrawItemState.Selected) != 0;
bool hot = (e.State & DrawItemState.HotLight) != 0;
bool grayed = (e.State & DrawItemState.Grayed) != 0;
bool checked_ = (e.State & DrawItemState.Checked) != 0;
// 共用矩形區域
Rectangle sideBar = new Rectangle(bounds.X, bounds.Y, 25, bounds.Height);
Rectangle checkBox = new Rectangle(bounds.X + 1, bounds.Y + 1, 23, bounds.Height - 2);
Rectangle textRect = new Rectangle(bounds.X + 26, bounds.Y + 4, bounds.Width - 36, bounds.Height);
Rectangle shortcutRect = new Rectangle(bounds.X, bounds.Y + 4, bounds.Width - 10, bounds.Height);
if (IsTopLevel)
{
DrawTopLevel(g, bounds, textRect, hot, selected);
}
else if (Text == "-")
{
DrawSeparator(g, bounds, sideBar);
}
else
{
DrawChildItem(g, bounds, sideBar, checkBox, textRect, shortcutRect,
selected, hot, grayed, checked_);
}
}
var mainMenu = new MainMenu();
var fileMenu = new OfficeMenuItem(true, "檔案(&F)");
fileMenu.MenuItems.Add(new OfficeMenuItem(true, "開啟(&O)") { Shortcut = Shortcut.CtrlO, IconPath = "open.png" });
fileMenu.MenuItems.Add(new OfficeMenuItem(true, "儲存(&S)") { Shortcut = Shortcut.CtrlS, IconPath = "save.png" });
fileMenu.MenuItems.Add(new OfficeMenuItem(true, "-")); // 分隔線
fileMenu.MenuItems.Add(new OfficeMenuItem(true, "結束(&X)") { Shortcut = Shortcut.AltF4 });
mainMenu.MenuItems.Add(fileMenu);
this.Menu = mainMenu;
MenuStrip + ToolStripProfessionalRenderer 自訂 ProfessionalColorTable,支援高 DPI、觸控、主題切換。MenuItem,零修改業務邏輯。