如何更便捷地监听Xamarin.Mac对象的事件?
问题
我正在使用纯C#和Xamarin.Mac开发一款菜单栏应用(不使用Xcode及Storyboard),发现监听NSMenu对象事件的现有方式较为繁琐。目前我是通过以下代码实现事件监听:
public class MenuDelegate : NSMenuDelegate { public class HighlightChangedEventArgs : EventArgs { public NSMenuItem? Item { get; set; } } public event EventHandler<HighlightChangedEventArgs?>? HighlightChanged; public override void MenuWillHighlightItem(NSMenu menu, NSMenuItem item) { HighlightChanged.Invoke(menu, new HighlightChangedEventArgs() { Item = item }); } public event EventHandler? Opened; public override void MenuWillOpen(NSMenu menu) { Opened.Invoke(menu, null); } public event EventHandler? Closed; public override void MenuDidClose(NSMenu menu) { Closed.Invoke(menu,null); } } var menuDelegate = new MenuDelegate(); sbIcon.Menu.Delegate = menuDelegate; menuDelegate.HighlightChanged += (object sender, MenuDelegate.HighlightChangedEventArgs args) => { // 处理逻辑 };
但这种需要单独定义Delegate类的方式太繁琐,想知道有没有更简便的实现方案?
回答
方案1:直接使用匿名NSMenuDelegate子类
Xamarin.Mac对NSDelegate类型提供了语法糖支持,可以直接通过匿名子类的方式重写方法并绑定处理逻辑,省去单独定义Delegate类的步骤:
sbIcon.Menu.Delegate = new NSMenuDelegate { MenuWillHighlightItem = (menu, item) => { // 处理菜单高亮事件 Console.WriteLine($"高亮菜单选项:{item.Title}"); }, MenuWillOpen = (menu) => { // 处理菜单打开事件 Console.WriteLine("菜单即将打开"); }, MenuDidClose = (menu) => { // 处理菜单关闭事件 Console.WriteLine("菜单已关闭"); } };
这种方式直接把事件逻辑嵌入到Delegate赋值中,代码更紧凑,不需要额外定义事件类和Invoke逻辑。
方案2:封装扩展方法实现复用
如果需要在多个菜单中复用类似的监听逻辑,可以封装一个扩展方法,将事件处理逻辑作为参数传入:
public static class NSMenuExtensions { public static void AttachMenuHandlers(this NSMenu menu, Action<NSMenu, NSMenuItem>? onItemHighlighted = null, Action<NSMenu>? onMenuOpened = null, Action<NSMenu>? onMenuClosed = null) { menu.Delegate = new NSMenuDelegate { MenuWillHighlightItem = (m, item) => onItemHighlighted?.Invoke(m, item), MenuWillOpen = m => onMenuOpened?.Invoke(m), MenuDidClose = m => onMenuClosed?.Invoke(m) }; } } // 使用时只需调用扩展方法,传入对应逻辑 sbIcon.Menu.AttachMenuHandlers( onItemHighlighted: (menu, item) => { // 处理高亮逻辑 }, onMenuOpened: menu => { // 处理打开逻辑 }, onMenuClosed: menu => { // 处理关闭逻辑 } );
这种方式既简化了单次使用的代码,又能实现逻辑复用,避免重复编写Delegate类。
方案3:弱引用Delegate避免内存泄漏(可选)
如果菜单对象生命周期较长,需要注意Delegate持有外部对象导致的内存泄漏问题,可以用弱引用封装Delegate:
public class WeakMenuDelegate : NSMenuDelegate { private readonly WeakReference<Action<NSMenu, NSMenuItem>> _onHighlight; private readonly WeakReference<Action<NSMenu>> _onOpen; private readonly WeakReference<Action<NSMenu>> _onClose; public WeakMenuDelegate(Action<NSMenu, NSMenuItem>? onHighlight = null, Action<NSMenu>? onOpen = null, Action<NSMenu>? onClose = null) { _onHighlight = new WeakReference<Action<NSMenu, NSMenuItem>>(onHighlight); _onOpen = new WeakReference<Action<NSMenu>>(onOpen); _onClose = new WeakReference<Action<NSMenu>>(onClose); } public override void MenuWillHighlightItem(NSMenu menu, NSMenuItem item) { if (_onHighlight.TryGetTarget(out var action)) action(menu, item); } public override void MenuWillOpen(NSMenu menu) { if (_onOpen.TryGetTarget(out var action)) action(menu); } public override void MenuDidClose(NSMenu menu) { if (_onClose.TryGetTarget(out var action)) action(menu); } } // 使用方式 sbIcon.Menu.Delegate = new WeakMenuDelegate( onHighlight: (menu, item) => { /* 处理逻辑 */ }, onOpen: menu => { /* 处理逻辑 */ }, onClose: menu => { /* 处理逻辑 */ } );
这种方式适合需要长期监听且对内存占用敏感的场景,避免Delegate持有外部对象导致的内存无法回收。
内容的提问来源于stack exchange,提问作者foxtdev
相关产品推荐
相关产品推荐

