首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# WinForms 自繪 Office 203 風格選單:OwnerDraw MenuItem 完整實現

     編輯:關於C#

自繪選單原理

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_);
    }
}

關鍵繪製細節

  • 頂層選單:預設 `Control` 色背景;Hot/Selected 時套用橙/藍漸層 + 黑色邊框;文字置中。
  • 子選單:白底 + 左側藍灰漸層側邊欄;Selected 時橙黃高亮背景 + 深藍邊框;Grayed 時灰色文字。
  • 勾選框:`Checked` 時在側邊欄繪製橙色背景 + 黑框 + ✓ 符號;`Checked+Selected` 時紅色背景。
  • 圖示:僅子選單、非父項目、有 `IconPath` 時,在側邊欄區域置中繪製。
  • 分隔線:灰色橫線,左側保留側邊欄寬度。

使用範例


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,零修改業務邏輯。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved