You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MAUI中Mac Catalyst状态栏菜单实现求助

.NET MAUI MacCatalyst 状态栏菜单实现方案

核心误区:别直接用Runtime绑定,托管API更简单

你当前的代码直接通过Runtime.GetNSObject和自定义objc_msgSend调用OC原生方法,不仅容易出错,还完全没必要——.NET MAUI的MacCatalyst平台已经封装了NSMenu、NSMenuItem这些AppKit类的托管API,直接用就行,不需要手动处理指针和选择器。

你的代码无效的关键原因:Runtime.GetNSObject(Class.GetHandle("NSMenu"))拿到的是NSMenu的类对象,而initWithTitle:是实例方法,必须先创建实例才能调用。

完整实现示例

下面是极简的状态栏菜单实现代码,放在Platforms/MacCatalyst目录下的类中(比如TrayService.cs):

using AppKit;
using Foundation;
using CoreGraphics;

namespace YourAppNamespace.Platforms.MacCatalyst
{
    public class TrayService : IDisposable
    {
        private NSStatusItem _statusBarItem;
        private NSMenu _statusMenu;

        public void SetupStatusBar()
        {
            // 1. 获取系统状态栏并创建状态栏项
            var statusBar = NSStatusBar.SystemStatusBar;
            _statusBarItem = statusBar.CreateStatusItem(NSStatusItemLength.Variable);

            // 2. 设置状态栏图标(可选,替换成你的资源文件名)
            if (NSImage.ImageNamed("StatusIcon") is NSImage icon)
            {
                icon.Size = new CGSize(18, 18);
                _statusBarItem.Image = icon;
            }

            // 3. 创建菜单实例
            _statusMenu = new NSMenu("应用菜单");

            // 4. 创建带点击事件的菜单项
            // 示例1:退出应用
            var quitItem = new NSMenuItem("退出", "q", () =>
            {
                NSApplication.SharedApplication.Terminate(this);
            });

            // 示例2:自定义操作菜单项
            var customItem = new NSMenuItem("打开主窗口")
            {
                Action = new ObjCRuntime.Selector("OnCustomItemClicked:"),
                Target = this
            };

            // 5. 添加菜单项到菜单
            _statusMenu.AddItem(customItem);
            _statusMenu.AddItem(NSMenuItem.SeparatorItem); // 添加分隔线
            _statusMenu.AddItem(quitItem);

            // 6. 关联菜单到状态栏项
            _statusBarItem.Menu = _statusMenu;
        }

        // 对应自定义菜单项的点击事件(需和Selector名称匹配)
        [Export("OnCustomItemClicked:")]
        private void OnCustomItemClicked(NSMenuItem sender)
        {
            // 这里写你的自定义逻辑,比如唤起主窗口
            NSApplication.SharedApplication.ActivateIgnoringOtherApps(true);
            // 如需操作MAUI页面,可通过App.Current.MainPage访问
        }

        public void Dispose()
        {
            // 清理资源
            _statusBarItem?.RemoveFromStatusBar();
            _statusBarItem?.Dispose();
            _statusMenu?.Dispose();
        }
    }
}

关键代码解释

  • 创建NSMenu:直接用new NSMenu("菜单标题")即可,托管构造器已封装OC的initWithTitle:方法。
  • 创建NSMenuItem:
    • 简单场景用带委托的构造器(如退出菜单项);
    • 复杂场景用Action+Target搭配[Export]标记的方法处理点击事件。
  • 关联菜单:直接给NSStatusItem.Menu属性赋值,无需手动调用setMenu:。

如何启用

在MacCatalyst的AppDelegate.cs中初始化:

public override void FinishedLaunching(NSNotification notification)
{
    base.FinishedLaunching(notification);
    var trayService = new TrayService();
    trayService.SetupStatusBar();
    // 将trayService存到全局变量,避免被GC回收
}

你的代码无效原因

  1. 你拿到的是NSMenu的类对象而非实例,调用initWithTitle:无法创建有效实例;
  2. 手动调用objc_msgSend易出现参数类型不匹配、内存管理问题,托管API已帮你处理这些细节。

内容的提问来源于stack exchange,提问作者DesmondMilez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 20:50:29