Windows Forms 提供 MainMenu(主選單列)、ContextMenu(右鍵彈出選單)、MenuItem(選單項目)三大核心類別,繼承自抽象基類 Menu。本文系統整理類別階層、建構函式多載、屬性配置、動態建立/修改/刪除選單項目、事件繫結模式、完整實戰範例與最佳實務。
Object
└─ MarshalByRefObject
└─ Component
└─ Menu (abstract)
├─ MainMenu // 表單頂端選單列容器
├─ ContextMenu // 右鍵彈出選單容器
└─ MenuItem // 單一選單項目(可巢狀形成子選單)
Menu.MenuItemCollection 實作 IList/ICollection/IEnumerable,提供 Add/AddRange/Remove/RemoveAt/Clear/Contains/IndexOf/CopyTo 等集合操作。
| 建構函式 | 用途 | 範例 |
|---|---|---|
MenuItem() | 空項目,後續設定屬性 | new MenuItem { Text = "&File" } |
MenuItem(string text) | 指定顯示文字 | new MenuItem("&New") |
MenuItem(string, EventHandler) | 文字 + Click 事件 | new MenuItem("&Open", OnOpenClick) |
MenuItem(string, MenuItem[]) | 文字 + 子選單陣列 | new MenuItem("&View", new[]{item1, item2}) |
MenuItem(string, EventHandler, Shortcut) | 文字 + Click + 快捷鍵 | new MenuItem("&Save", OnSave, Shortcut.CtrlS) |
| 屬性 | 型別 | 功能 | 備註 |
|---|---|---|---|
Text | string | 顯示文字,& 前綴字元變為底線(Alt 快捷鍵) | "&File" → File |
Checked | bool | 前方顯示核取記號 (✓) | 互斥選項需自行邏輯控制 |
Enabled | bool | 是否可點選,灰顯不可用 | 預設 true |
Visible | bool | 是否顯示 | 預設 true |
Shortcut | Shortcut | 功能鍵組合(CtrlS、F1 等) | 需 ShowShortcut=true 顯示 |
ShowShortcut | bool | 是否在選單右側顯示快捷鍵文字 | 預設 true |
BarBreak | bool | 換欄並顯示分隔線 | 工具列風格選單 |
Break | bool | 換欄不顯示分隔線 | |
DefaultItem | bool | 粗體顯示,雙擊父選單觸發 | 單一父選單僅一個 |
Index | int | 在父集合中的位置(可設定重新排序) | 唯讀於設計器 |
IsParent | bool | 是否擁有子選單 | 唯讀 |
MenuItems | MenuItemCollection | 子選單集合 | 動態增刪改查 |
Parent | Menu | 父選單容器(MainMenu/ContextMenu/MenuItem) | 唯讀 |
MergeOrder/MergeType | int/MenuMerge | MDI 子表單選單合併行為 | 進階主題 |
Text = "-" → 自動轉為分隔線(不可點選、無事件)。
var mainMenu = new MainMenu();
// 頂層項目
var fileItem = new MenuItem("&File");
var editItem = new MenuItem("&Edit");
var viewItem = new MenuItem("&View");
// File 子選單
fileItem.MenuItems.AddRange(new MenuItem[] {
new MenuItem("&New", OnNewClick, Shortcut.CtrlN),
new MenuItem("&Open", OnOpenClick, Shortcut.CtrlO),
new MenuItem("-"), // 分隔線
new MenuItem("E&xit", OnExitClick)
});
// Edit 子選單
editItem.MenuItems.AddRange(new MenuItem[] {
new MenuItem("&Copy", OnCopyClick, Shortcut.CtrlC),
new MenuItem("&Paste", OnPasteClick, Shortcut.CtrlV)
});
// 加入主選單
mainMenu.MenuItems.AddRange(new MenuItem[] { fileItem, editItem, viewItem });
// 指派給表單
this.Menu = mainMenu;
var contextMenu = new ContextMenu();
contextMenu.MenuItems.AddRange(new MenuItem[] {
new MenuItem("&Refresh", OnRefreshClick),
new MenuItem("&Properties", OnPropertiesClick),
new MenuItem("-"),
new MenuItem("E&xit", OnExitClick)
});
// 指派給表單或任意控制項
this.ContextMenu = contextMenu;
textBox1.ContextMenu = contextMenu; // 控制項層級覆蓋
// 程式碼觸發顯示(滑鼠座標)
contextMenu.Show(this, new Point(e.X, e.Y)); // 在 MouseDown/Click 事件中
// 單一處理器
menuItem.Click += OnMenuClick;
// 多重處理器(依加入順序執行)
menuItem.Click += OnFirstHandler;
menuItem.Click += OnSecondHandler;
// 移除處理器
menuItem.Click -= OnFirstHandler;
private void OnMenuClick(object sender, EventArgs e) {
var item = (MenuItem)sender;
MessageBox.Show($"您點選了:{item.Text}");
}
sender:引發事件的 MenuItem 實例。e:EventArgs(Click 無額外資料),DrawItemEventArgs(OwnerDraw)、MeasureItemEventArgs(自訂量測)。| 事件 | 觸發時機 | EventArgs 型別 | 用途 |
|---|---|---|---|
Click | 使用者點選 | EventArgs | 命令執行 |
Popup | 彈出選單即將顯示 | EventArgs | 動態啟用/停用/可見項目 |
Select | 游標移至項目 | EventArgs | 狀態列提示 |
DrawItem | OwnerDraw 模式繪製 | DrawItemEventArgs | 自訂外觀(圖示、顏色、字型) |
MeasureItem | OwnerDraw 模式量測 | MeasureItemEventArgs | 自訂項目尺寸 |
public partial class MenuDemoForm : Form
{
private MainMenu _mainMenu;
private ContextMenu _contextMenu;
public MenuDemoForm()
{
InitializeComponent();
BuildMainMenu();
BuildContextMenu();
}
private void BuildMainMenu()
{
_mainMenu = new MainMenu();
var file = new MenuItem("&File");
file.MenuItems.Add(new MenuItem("&New", (s,e) => MessageBox.Show("New")));
file.MenuItems.Add(new MenuItem("&Open", (s,e) => MessageBox.Show("Open")));
file.MenuItems.Add(new MenuItem("-"));
file.MenuItems.Add(new MenuItem("E&xit", (s,e) => Close()));
_mainMenu.MenuItems.Add(file);
_mainMenu.MenuItems.Add(new MenuItem("&Edit"));
_mainMenu.MenuItems.Add(new MenuItem("&Help"));
this.Menu = _mainMenu;
}
private void BuildContextMenu()
{
_contextMenu = new ContextMenu();
_contextMenu.Popup += (s, e) => {
// 動態調整:若剪貼簿有文字才啟用 Paste
_contextMenu.MenuItems[1].Enabled = Clipboard.ContainsText();
};
_contextMenu.MenuItems.Add(new MenuItem("&Copy", (s,e) => Clipboard.SetText("Copied!")));
_contextMenu.MenuItems.Add(new MenuItem("&Paste", (s,e) => MessageBox.Show(Clipboard.GetText())));
this.ContextMenu = _contextMenu;
}
// 按鈕動態新增/移除主選單項目
private void btnAddMenu_Click(object sender, EventArgs e)
{
var item = new MenuItem($"Item {_mainMenu.MenuItems.Count + 1}");
item.Click += (s, e) => MessageBox.Show(((MenuItem)s).Text);
_mainMenu.MenuItems.Add(item);
}
private void btnRemoveMenu_Click(object sender, EventArgs e)
{
if (_mainMenu.MenuItems.Count > 0) {
var last = _mainMenu.MenuItems[_mainMenu.MenuItems.Count - 1];
_mainMenu.MenuItems.Remove(last);
}
}
}
MainMenu/ContextMenu,屬性視窗編輯項目、事件,自動產生 InitializeComponent 程式碼。Text、Shortcut 等屬性可透過 .resx 多語系。MergeOrder/MergeType(Add/Replace/MergeItems)實現子表單選單自動合併至父表單。OwnerDraw = true + DrawItem/MeasureItem 事件實作自訂繪製(圖示、漸層、高亮)。KeyPreview = true 統一攔截,或依賴選單原生 Shortcut 自動處理。-=),避免委派持有參考阻礙 GC。Application.SetHighDpiMode(HighDpiMode.PerMonitorV2)。Application.EnableVisualStyles() 仍需在 Main 最前呼叫啟用視覺樣式。Windows Forms 選單系統以 Menu 抽象基類統一 MainMenu/ContextMenu/MenuItem,配合 MenuItemCollection 強型別集合操作,提供直覺且強大的動態選單建構能力。掌握建構函式多載、屬性語意、事件委派模式、OwnerDraw 進階繪製、MDI 合併與動態管理技巧,即能打造專業級桌面應用程式導航體驗。雖然生態逐漸轉向 WPF/MAUI/Blazor Hybrid,但 WinForms 於企業內部工具、舊系統維護、快速原型開發仍具不可替代的生產力優勢。