WPF 視窗左上角圖示點擊彈出的系統選單,有時需移除特定項目(如「關閉」、「最大化」)或全部禁用。本文提供靜態工具類 SystemMenuManager,透過 Win32 API GetSystemMenu、RemoveMenu 實現精確控制,並處理 WPF 視窗句柄取得時機問題。
[DllImport("user32.dll", EntryPoint = "GetSystemMenu")]
private static extern IntPtr GetSystemMenu(IntPtr hwnd, int revert);
[DllImport("user32.dll", EntryPoint = "GetMenuItemCount")]
private static extern int GetMenuItemCount(IntPtr hmenu);
[DllImport("user32.dll", EntryPoint = "RemoveMenu")]
private static extern int RemoveMenu(IntPtr hmenu, int npos, int wflags);
[DllImport("user32.dll", EntryPoint = "DrawMenuBar")]
private static extern int DrawMenuBar(IntPtr hwnd);
private const int MF_BYPOSITION = 0x0400;
private const int MF_DISABLED = 0x0002;
public static void RemoveWindowSystemMenu(Window window) {
if (window == null) return;
window.SourceInitialized += window_SourceInitialized;
}
static void window_SourceInitialized(object sender, EventArgs e) {
var window = (Window)sender;
var helper = new WindowInteropHelper(window);
IntPtr hmenu = GetSystemMenu(helper.Handle, 0);
int cnt = GetMenuItemCount(hmenu);
for (int i = cnt - 1; i >= 0; i--) {
RemoveMenu(hmenu, i, MF_DISABLED | MF_BYPOSITION);
}
}
在 SourceInitialized 事件中執行,確保視窗句柄已建立。反向迴圈避免索引位移問題。
public static void RemoveWindowSystemMenuItem(Window window, int itemIndex) {
if (window == null) return;
window.SourceInitialized += delegate {
var helper = new WindowInteropHelper(window);
IntPtr hmenu = GetSystemMenu(helper.Handle, 0);
RemoveMenu(hmenu, itemIndex, MF_DISABLED | MF_BYPOSITION);
DrawMenuBar(helper.Handle); // 重繪選單列
};
}
itemIndex 為 0 基底索引,對應選單項目順序(還原、移動、大小、最小化、最大化、分隔線、關閉...)。
移除選單項目會同步禁用其關聯功能:
若僅需阻止關閉而保留 UI,請改用 Closing 事件設定 e.Cancel = true。
WindowStyle=None),需隱藏原生系統選單