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

.NET MAUI中DataGridGrid的Renderer转Handler显示异常问题

问题描述

我正在将.NET MAUI中用作自定义ListView表头的DataGridGrid控件的Renderer转换为Handler。以下是自定义控件及其Android平台Renderer的代码(该控件是带有列的常规Grid控件):

自定义控件代码

namespace Test.Helpers.DataGrid.Helpers
{
   public class DataGridGrid: Grid
   {
       public Action<bool>? OnResizeToggled;

        public Action<float>? OnResizeCommit { get; set; }
        public Action<float, bool>? OnResizing { get; set; }
        public Action OnSorting { get; set; }
        public int GridIndex { get; set; }
    }
}

原Android Renderer代码

[assembly: ExportRenderer(typeof(DataGridGrid), typeof(DataGridGridRenderer))]

namespace Test.Droid.Helpers
{
    public class DataGridGridRenderer : ViewRenderer<DataGridGrid, View>
    {
        private bool _resizing;
        private float _initialX;
        private bool _handlerAttached;
        private DataGridGrid _prev;

        public DataGridGridRenderer(Context context) : base(context)
        {
        }

        public float ConvertPixelsToDp(float px)
        {
            var density = Context.Resources.DisplayMetrics.Density;
            return px / density;
        }

        protected override void OnElementChanged(ElementChangedEventArgs<DataGridGrid> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement == null) return;

            if (_handlerAttached) return;
            _handlerAttached = true;
            Touch += HandleTouches;
        }

        private void HandleTouches(object sender, TouchEventArgs args)
        {
            // 触摸处理逻辑
        }
    }
}

当前MAUI Handler代码

namespace Test.Platforms.Android.Handlers
{
    public partial class DataGridGridHandler : ViewHandler<DataGridGrid, View>
    {
        private bool _resizing;
        private float _initialX;
        private bool _handlerAttached;
        private DataGridGrid _prev;

        public DataGridGridHandler() : base(DGListViewMapper) { }
        public DataGridGridHandler(IPropertyMapper mapper, CommandMapper? commandMapper = null) : base(mapper, commandMapper) { }

        public static PropertyMapper<DataGridGrid, DataGridGridHandler> DGListViewMapper = new(ViewHandler.ViewMapper)
        {

        };
        public float ConvertPixelsToDp(float px)
        {
            var density = Context.Resources.DisplayMetrics.Density;
            return px / density;
        }

        protected override View CreatePlatformView()
        {
              return new View(Context);// 返回空白视图
        }
    }
}

Handler注册代码(MAUIProgram.cs)

handlers.AddHandler(typeof(DataGridGrid), typeof(DataGridGridHandler));

当前Handler返回空白输出,无法正常显示带列名的网格表头。已知问题源于实例化了新的空白View,想了解如何仅绑定事件并复用原虚拟视图?


解决方案

核心问题在于你的DataGridGrid继承自MAUI的Grid控件,而当前Handler手动创建了一个空白的Android View,完全忽略了MAUI Grid本身的原生渲染逻辑,导致表头的列布局无法被正确渲染。正确的做法是复用MAUI Grid对应的Android原生实现,并仅添加触摸事件绑定逻辑:

修正后的Handler代码

namespace Test.Platforms.Android.Handlers
{
    public partial class DataGridGridHandler : ViewHandler<DataGridGrid, MauiGrid>
    {
        private bool _resizing;
        private float _initialX;
        private bool _handlerAttached;

        // 复用MAUI Grid的PropertyMapper,保留Grid原有渲染逻辑
        public static PropertyMapper<DataGridGrid, DataGridGridHandler> DGListViewMapper = new(GridHandler.Mapper)
        {
            // 如果需要针对DataGridGrid的自定义属性添加映射,在这里补充
        };

        public DataGridGridHandler() : base(DGListViewMapper) { }
        public DataGridGridHandler(IPropertyMapper mapper, CommandMapper? commandMapper = null) : base(mapper, commandMapper) { }

        public float ConvertPixelsToDp(float px)
        {
            var density = Context.Resources.DisplayMetrics.Density;
            return px / density;
        }

        // 创建MAUI Grid对应的Android原生视图MauiGrid
        protected override MauiGrid CreatePlatformView()
        {
            return new MauiGrid(Context);
        }

        protected override void OnElementChanged(ElementChangedEventArgs<DataGridGrid> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null)
            {
                // 移除旧元素的触摸事件,防止内存泄漏
                if (PlatformView != null)
                {
                    PlatformView.Touch -= HandleTouches;
                    _handlerAttached = false;
                }
            }

            if (e.NewElement != null && !_handlerAttached)
            {
                // 给新的原生视图绑定触摸事件
                PlatformView.Touch += HandleTouches;
                _handlerAttached = true;
            }
        }

        protected override void DisconnectHandler(MauiGrid platformView)
        {
            // 销毁时移除事件绑定
            platformView.Touch -= HandleTouches;
            _handlerAttached = false;
            base.DisconnectHandler(platformView);
        }

        private void HandleTouches(object sender, TouchEventArgs args)
        {
            // 保留原Renderer中的触摸处理逻辑
            // 例如:
            // var motionEvent = args.Event;
            // switch (motionEvent.Action)
            // {
            //     case MotionEventActions.Down:
            //         _initialX = motionEvent.RawX;
            //         _resizing = true;
            //         args.Handled = false;
            //         break;
            //     // 其他触摸逻辑...
            // }
        }
    }
}

关键修改点说明

  1. 泛型参数调整:将ViewHandler<DataGridGrid, View>改为ViewHandler<DataGridGrid, MauiGrid>,MauiGrid是MAUI Grid控件在Android平台对应的原生实现类,确保Grid的列布局能被正常渲染。
  2. 复用Grid的PropertyMapper:使用GridHandler.Mapper作为基础创建自定义Mapper,保留MAUI Grid原有的所有渲染逻辑,无需从零实现。
  3. 正确创建原生视图:CreatePlatformView返回MauiGrid而非空白View,让MAUI能够正常渲染Grid的内容。
  4. 事件绑定与清理:在OnElementChanged中为新元素绑定触摸事件,同时为旧元素移除事件;在DisconnectHandler中销毁事件绑定,避免内存泄漏。

这样修改后,Handler会复用MAUI Grid的原生渲染逻辑,同时添加你需要的触摸事件处理,表头就能正常显示列名,并且保留原有的调整大小、排序等交互逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 13:25:22