MAUI 中的处理程序仅在示例项目中起作用。未调用处理程序类中的 Constructor 或 CreatePlatformView 或 ConnectHandler

Handlers in MAUI is working only in sample project. The Constructor or CreatePlatformView or ConnectHandler from handler class is not getting called

提问人:Akshay Kumar 提问时间:7/17/2023 最后编辑:Akshay Kumar 更新时间:7/21/2023 访问量:333

问:

使用 Xamarin Forms 创建了一个 iOS 应用(该应用仅适用于 iOS)。Xamarin 项目具有用于显示上下文菜单的 CustomRenderer。如下图所示enter image description here

升级到 MAUI 后,我尝试重用相同的 CustomRenderer。 它未在 MAUI 项目中显示上下文菜单。使用 CustomRenderer 时,它甚至没有调用 OnElementChanged 方法。

我决定为此创建一个处理程序。它也不会显示上下文菜单。甚至处理程序类中的 Constructor 或 CreatePlatformView 或 ConnectHandler 也没有被调用。 我不确定出了什么问题。

处理程序代码如下所述。

在 MauiProgram 类中,我使用以下行注册了 Handler:

handlers.AddHandler(typeof(ContextMenuView), typeof(ContextMenuViewHandler));

自定义控件定义如下:

    public class ContextMenuView : View
    {
        public MenuRequested MenuRequested;

        public void RequestMenu(object sender, double x, double y, double width, double height)
        {
            MenuRequested?.Invoke(sender, new MenuRequestedEventArgs(x, y, width, height));
        }

        public bool CanCut { get; set; } = true;
        public bool CanCopy { get; set; } = true;
        public bool CanDelete { get; set; } = true;
        public bool HasProperties { get; set; } = true;

        public Action OnCopy { get; set; }
        public Action OnDelete { get; set; }
        public Action OnProperties { get; set; }
        public Action OnCut { get; set; }

        public IContextCommand[] Commands { get; set; }
    }

处理程序定义如下:

    public class ContextMenuViewHandler : ViewHandler<ContextMenuView, ContextMenuiOS>
    {
        private nfloat _height;
        private nfloat _width;

        string ACTION_PREFIX = "custom_action";
        int MAX_CUSTOM_ACTIONS = 4;

        public static IPropertyMapper<ContextMenuView, ContextMenuViewHandler> PropertyMapper =
            new PropertyMapper<ContextMenuView, ContextMenuViewHandler>(ViewHandler.ViewMapper)
            {

            };

        public ContextMenuViewHandler() : base(PropertyMapper)
        {
        }

        protected override ContextMenuiOS CreatePlatformView()
        {
            return new ContextMenuiOS(VirtualView);
        }

        protected override void ConnectHandler(ContextMenuiOS platformView)
        {
            base.ConnectHandler(platformView);

            VirtualView.MenuRequested += OnMenuRequested;
        }

        protected override void DisconnectHandler(ContextMenuiOS platformView)
        {
            base.DisconnectHandler(platformView);

            VirtualView.MenuRequested -= OnMenuRequested;
        }

        private void OnMenuRequested(object _, MenuRequestedEventArgs e)
        {
            try
            {
                var _menu = UIMenuController.SharedMenuController;
                PlatformView.BecomeFirstResponder();
                var items = new List<UIMenuItem>();
                if (VirtualView.Commands?.Any() == true)
                {
                    var index = 0;
                    foreach (var item in VirtualView.Commands.Where(o => o.Command.CanExecute(null)).Take(MAX_CUSTOM_ACTIONS))
                    {
                        items.Add(new UIMenuItem
                        {
                            Title = item.Name,
                            Action = new Selector($"{ACTION_PREFIX}{index++}:")
                        });
                    }
                }
                if (VirtualView.HasProperties)
                {
                    items.Add(new UIMenuItem("Options...", new Selector("Properties:")));
                }
                _menu.MenuItems = items.ToArray();
                _menu.SetTargetRect(new CGRect(e.X, e.Y, e.Width, e.Height), PlatformView);
                _menu.MenuVisible = true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }

ContextMenuiOS 定义如下:

    public class ContextMenuiOS : UIView
    {
        private nfloat _height;
        private nfloat _width;

        const string ACTION_PREFIX = "custom_action";
        const int MAX_CUSTOM_ACTIONS = 4;

        ContextMenuView Mauiview;

        public ContextMenuiOS(ContextMenuView view)
        {
            Mauiview = view;

            _height = UIScreen.MainScreen.Bounds.Height;
            _width = UIScreen.MainScreen.Bounds.Width;
            Frame = new CGRect(0, 0, _width, _height);
        }

        public override bool CanBecomeFirstResponder => true;

        public override bool CanPerform(Selector action, NSObject withSender)
        {
            switch (action.Name)
            {
                case "Properties:":
                    return Mauiview.HasProperties;
                case "cut:":
                    return Mauiview.CanCut;
                case "copy:":
                    return Mauiview.CanCopy;
                case "delete:":
                    return Mauiview.CanDelete;
            }
            if (action.Name.StartsWith(ACTION_PREFIX))
            {
                var indexString = action.Name.Substring(ACTION_PREFIX.Length, action.Name.Length - ACTION_PREFIX.Length - 1);
                var index = int.Parse(indexString);
                return Mauiview.Commands[index].Command.CanExecute(null);
            }
            return true;
        }

        [Export("Properties:")]
        public void Properties(UIMenuController controller)
        {
            Mauiview?.OnProperties();
        }

        [Export("custom_action0:")]
        public void Action0(UIMenuController controller)
        {
            Mauiview.Commands[0].Command.Execute(null);
        }

        [Export("custom_action1:")]
        public void Action1(UIMenuController controller)
        {
            Mauiview.Commands[1].Command.Execute(null);
        }

        [Export("custom_action2:")]
        public void Action2(UIMenuController controller)
        {
            Mauiview.Commands[2].Command.Execute(null);
        }

        [Export("custom_action3:")]
        public void Action3(UIMenuController controller)
        {
            Mauiview.Commands[3].Command.Execute(null);
        }

        public override void Cut(NSObject sender)
        {
            Mauiview?.OnCut();
        }

        public override void Copy(NSObject sender)
        {
            Mauiview?.OnCopy();
        }

        public override void Delete(NSObject sender)
        {
            Mauiview?.OnDelete();
        }
    }

注意:我尝试创建一个示例项目以在 GitHub 中发布。 但在示例项目中,相同的代码显示在上下文菜单中。

可以通过以下链接找到示例项目存储库:The sample project repo can be found with the following link: https://github.com/apy534/MAUI-Handler-test

默认情况下,gitHub 项目使用 CustomRenderer。您可以通过在 MauiProgram 类中将 Render 注册更改为 handler 来从 CustomRenderer 切换到 Handler。

有人面临类似的问题吗?

自定义控件 Maui 处理程序

评论

0赞 Liqun Shen-MSFT 7/18/2023
在 .NET 多平台应用 UI (.NET MAUI) 中,可以在 Mac Catalyst 和 Windows 上添加上下文菜单。你在 iOS 上使用过吗?
0赞 Akshay Kumar 7/20/2023
@LiqunShen-MSFT,我希望你说的是 MenuFlyout。它没有显示在iOS设备上
0赞 Liqun Shen-MSFT 7/20/2023
“当集成到主项目中时,同样不起作用。”是的,您的样品工作正常,但不工作是什么意思?任何错误或上下文未显示或无法自定义?你是如何融入主项目的?最好将这些信息包含在您的问题中。
0赞 Akshay Kumar 7/21/2023
@LiqunShen-MSFT 使用处理程序实现或 CustomRenderer 重用这两种解决方案时,上下文菜单在集成到主项目中时不会显示。CustomRenderer/Handler 根本没有命中。
0赞 Liqun Shen-MSFT 7/21/2023
这很奇怪。您使用处理程序的示例在我这边确实运行良好。我不知道你的主要项目是什么样的。是因为布局不同吗?

答: 暂无答案