.NET Framework (WinForms) 提供了極為優雅的自繪模型:**將 MenuItem.OwnerDraw = true,訂閱 DrawItem 與 MeasureItem 事件**,即可完全接管選單項渲染,無需 MFC 那樣複雜的訊息映射與結構體解析。本文深度解析 .NET 選單架構 (MainMenu/ContextMenu/MenuItem)、自繪事件模型,並規劃一個可拖放設計器的 GraphicMenu 元件雛形。
Menu:公共基類。MainMenu:表單頂層選單列。ContextMenu:右鍵彈出選單。MenuItem:單個選單項 (可巢狀形成子選單)。MenuItem 集合,無論顯示位置。
圖一:自繪控制項示意
Sub MakeItemOwnerDraw(ByVal item As MenuItem)
item.OwnerDraw = True
AddHandler item.DrawItem, AddressOf StdDrawItem
AddHandler item.MeasureItem, AddressOf StdMeasureItem
End Sub
| 事件 | 觸發時機 | 開發者任務 |
|---|---|---|
MeasureItem | 選單需計算項目尺寸時 (彈出前) | 根據字體/圖示/文字計算 e.ItemWidth, e.ItemHeight 並賦值 |
DrawItem | 選單需繪製特定項目時 | 在 e.Graphics 上繪製背景、圖示、文字、選中態、勾選框等 |
MeasureItemEventArgs / DrawItemEventArgs 封裝了 Win32 底層 MEASUREITEMSTRUCT / DRAWITEMSTRUCT,提供強型別屬性 (Font, Graphics, Bounds, State 等),極大降低開發難度。
GraphicMenu 外部初始化器而非繼承 MainMenu,作者建議建立獨立類別 GraphicMenu,提供 Initialize(Menu targetMenu) 方法:
targetMenu.MenuItems 所有層級項目。MenuItem.OwnerDraw = true。DrawItem/MeasureItem 至內部處理器。缺點: 需顯式呼叫 Initialize,設計器不支援預覽 (運行時才見效果)。
優點: 與現有選單結構解耦、可動態切換主題、單一職責原則。
e.DrawBackground():一鍵繪製系統預設背景 (含選中藍、勾選框區),可選用。e.State 旗標判斷:DrawItemState.Selected, Disabled, Checked, Default, HotLight, Focus, NoAccelerator 等。e.Graphics.DrawString(text, font, brush, bounds, stringFormat),StringFormat 控制置中/靠右/省略號。e.Graphics.DrawImage(image, rect) 或 ImageList.Draw。e.Graphics 基於 GDI+,原生支援抗鋸齒、Alpha 混合、向量縮放。| 維度 | MFC (C++) | .NET WinForms |
|---|---|---|
| 啟用方式 | MF_OWNERDRAW + 訊息映射 | MenuItem.OwnerDraw = true + 事件訂閱 |
| 尺寸計算 | OnMeasureItem 手寫 MEASUREITEMSTRUCT | MeasureItem 事件 e.ItemWidth/Height |
| 繪製介面 | CDC* + DRAWITEMSTRUCT 指標 | Graphics 物件 + DrawItemEventArgs |
| 狀態判斷 | 位元旗標 ODS_SELECTED 等 | 列舉 DrawItemState 旗標 |
| 圖形能力 | GDI (線/矩形/文字/位圖) | GDI+ (漸層/路徑/抗鋸齒/Alpha/轉換) |
| 設計器支援 | 無 (純代碼) | 可建立元件拖放設計器 (本文目標) |
作者計劃後續文章實現完整的 GraphicMenu 元件:設計器支援、主題序列化、動畫展開、鍵盤導航輔助等。.NET 的自繪模型讓這些進階功能的開發門檻大幅降低。