如何在MAUI Windows托盘图标中添加右键上下文菜单?
在MAUI Windows托盘图标中添加右键上下文菜单
问题背景
基于MAUI的WeatherTwentyOne示例实现Windows系统托盘图标后,需要为其添加右键上下文菜单功能。尝试在WindowsTrayIcon类中添加ContextMenu属性后,无法将菜单关联到托盘图标,同时遇到ContextMenu无MenuItem定义的问题。
原WindowsTrayIcon核心代码:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Hardcodet.Wpf.TaskbarNotification.Interop { public class WindowsTrayIcon { private readonly object lockObject = new object(); private NotifyIconData iconData; private readonly WindowMessageSink messageSink; public Action LeftClick { get; set; } public Action RightClick { get; set; } public bool IsTaskbarIconCreated { get; private set; } public WindowsTrayIcon(string iconFile) { messageSink = new WindowMessageSink(); iconData = NotifyIconData.CreateDefault(messageSink.MessageWindowHandle, iconFile); CreateTaskbarIcon(); messageSink.MouseEventReceived += MessageSink_MouseEventReceived; messageSink.TaskbarCreated += MessageSink_TaskbarCreated; } private void MessageSink_TaskbarCreated() { RemoveTaskbarIcon(); CreateTaskbarIcon(); } private void MessageSink_MouseEventReceived(MouseEvent obj) { if (obj == MouseEvent.IconLeftMouseUp) { LeftClick?.Invoke(); } else if (obj == MouseEvent.IconRightMouseUp) { RightClick?.Invoke(); } } private void CreateTaskbarIcon() { lock (lockObject) { if (IsTaskbarIconCreated) return; const IconDataMembers members = IconDataMembers.Message | IconDataMembers.Icon | IconDataMembers.Tip; var status = WriteIconData(ref iconData, NotifyCommand.Add, members); if (!status) return; SetVersion(); IsTaskbarIconCreated = true; } } private void RemoveTaskbarIcon() { lock (lockObject) { if (!IsTaskbarIconCreated) return; WriteIconData(ref iconData, NotifyCommand.Delete, IconDataMembers.Message); IsTaskbarIconCreated = false; } } private void SetVersion() { iconData.VersionOrTimeout = (uint)0x4; bool status = WinApi.Shell_NotifyIcon(NotifyCommand.SetVersion, ref iconData); if (!status) Debug.Fail("Could not set version"); } public static readonly object SyncRoot = new object(); public static bool WriteIconData(ref NotifyIconData data, NotifyCommand command) { return WriteIconData(ref data, command, data.ValidMembers); } public static bool WriteIconData(ref NotifyIconData data, NotifyCommand command, IconDataMembers flags) { data.ValidMembers = flags; lock (SyncRoot) { return WinApi.Shell_NotifyIcon(command, ref data); } } } }
原TrayService代码:
public class TrayService : ITrayService { WindowsTrayIcon tray; public Action ClickHandler { get; set; } public void Initialize() { tray = new WindowsTrayIcon("Platforms/Windows/trayicon.ico"); var contextMenu = new ContextMenu(); contextMenu.MenuItems.Add(new MenuItem()); contextMenu.MenuItems.Add(new MenuItem()); tray.ContextMenu = contextMenu; tray.LeftClick = () => { WindowExtensions.BringToFront(); ClickHandler?.Invoke(); }; } }
解决方案
1. 修改WindowsTrayIcon类,添加上下文菜单支持
首先给项目安装System.Windows.Forms NuGet包,然后修改类代码:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; // 新增引用 namespace Hardcodet.Wpf.TaskbarNotification.Interop { public class WindowsTrayIcon { private readonly object lockObject = new object(); private NotifyIconData iconData; private readonly WindowMessageSink messageSink; public Action LeftClick { get; set; } public Action RightClick { get; set; } public bool IsTaskbarIconCreated { get; private set; } // 新增上下文菜单属性,使用ContextMenuStrip替代旧的ContextMenu public ContextMenuStrip ContextMenuStrip { get; set; } public WindowsTrayIcon(string iconFile) { messageSink = new WindowMessageSink(); iconData = NotifyIconData.CreateDefault(messageSink.MessageWindowHandle, iconFile); CreateTaskbarIcon(); messageSink.MouseEventReceived += MessageSink_MouseEventReceived; messageSink.TaskbarCreated += MessageSink_TaskbarCreated; } private void MessageSink_TaskbarCreated() { RemoveTaskbarIcon(); CreateTaskbarIcon(); } private void MessageSink_MouseEventReceived(MouseEvent obj) { if (obj == MouseEvent.IconLeftMouseUp) { LeftClick?.Invoke(); } else if (obj == MouseEvent.IconRightMouseUp) { // 右键点击时显示上下文菜单 ContextMenuStrip?.Show(Cursor.Position); RightClick?.Invoke(); } } // 其余原有方法保持不变 private void CreateTaskbarIcon() { lock (lockObject) { if (IsTaskbarIconCreated) return; const IconDataMembers members = IconDataMembers.Message | IconDataMembers.Icon | IconDataMembers.Tip; var status = WriteIconData(ref iconData, NotifyCommand.Add, members); if (!status) return; SetVersion(); IsTaskbarIconCreated = true; } } private void RemoveTaskbarIcon() { lock (lockObject) { if (!IsTaskbarIconCreated) return; WriteIconData(ref iconData, NotifyCommand.Delete, IconDataMembers.Message); IsTaskbarIconCreated = false; } } private void SetVersion() { iconData.VersionOrTimeout = (uint)0x4; bool status = WinApi.Shell_NotifyIcon(NotifyCommand.SetVersion, ref iconData); if (!status) Debug.Fail("Could not set version"); } public static readonly object SyncRoot = new object(); public static bool WriteIconData(ref NotifyIconData data, NotifyCommand command) { return WriteIconData(ref data, command, data.ValidMembers); } public static bool WriteIconData(ref NotifyIconData data, NotifyCommand command, IconDataMembers flags) { data.ValidMembers = flags; lock (SyncRoot) { return WinApi.Shell_NotifyIcon(command, ref data); } } } }
2. 修改TrayService,创建并绑定上下文菜单
使用ToolStripMenuItem替代旧的MenuItem,并为菜单项添加点击事件:
using System.Windows.Forms; // 新增引用 public class TrayService : ITrayService { WindowsTrayIcon tray; public Action ClickHandler { get; set; } public void Initialize() { tray = new WindowsTrayIcon("Platforms/Windows/trayicon.ico"); // 创建上下文菜单 var contextMenu = new ContextMenuStrip(); // 添加"显示主窗口"菜单项 var showWindowItem = new ToolStripMenuItem("显示主窗口"); showWindowItem.Click += (sender, e) => { WindowExtensions.BringToFront(); ClickHandler?.Invoke(); }; // 添加"退出"菜单项 var exitItem = new ToolStripMenuItem("退出"); exitItem.Click += (sender, e) => { Microsoft.Maui.Controls.Application.Current.Quit(); }; contextMenu.Items.Add(showWindowItem); contextMenu.Items.Add(exitItem); // 绑定到托盘图标 tray.ContextMenuStrip = contextMenu; tray.LeftClick = () => { WindowExtensions.BringToFront(); ClickHandler?.Invoke(); }; } }
关键说明
- 选择
ContextMenuStrip而非旧的ContextMenu,因为前者是Windows Forms推荐的上下文菜单控件,提供更丰富的功能和更好的兼容性。 - 需要确保
System.Windows.FormsNuGet包已安装,否则无法使用相关控件。 - 菜单显示位置使用
Cursor.Position,确保菜单在鼠标右键点击的位置弹出。
内容的提问来源于stack exchange,提问作者10101
相关产品推荐
相关产品推荐

