mauicustom-renderer

Migrate simple Custom Renderer from Xamarin.Forms to .NET MAUI


I'm trying to migrate a Xamarin.Forms app to .NET MAUI. So far I've managed to port almost everything, but I'm having a hard time trying to understand how to rewrite a custom renderer for a ScrollView using the new MAUI paradigm (handlers). The Xamarin renderer is very simple. I just need to override OnIntereptTouchEvent and OnTouchEvent:

    class MyScrollViewRenderer : ScrollViewRenderer
    {
        public MyScrollViewRenderer(Context context) : base(context)
        {
            /* nothing here */
        }

        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);
        }

        public override bool OnInterceptTouchEvent(MotionEvent ev)
        {
            if (((MyScrollView)Element).ScrollingEnabled)
            {
                return base.OnInterceptTouchEvent(ev);
            }
            else
            {
                return false;
            }
        }

        public override bool OnTouchEvent(MotionEvent ev)
        {
            if (((MyScrollView)Element).ScrollingEnabled)
            {
                return base.OnTouchEvent(ev);
            }
            else
            {
                return false;
            }
        }
    }

The meaning of this renderer is to disable the ScrollView scrolling while the user interacts with a custom component to trace some lines on the screen. On iOS this is pretty simple. It's just a matter of setting handler.PlatformView.ScrollEnabled = false;. On Android this property do not exist, so, the need to override OnInterceptTouchEvent and OnTouchEvent.

I've read through the MAUI docs, but just found ways to map property changes with handlers. Couldn't find any guidance on how to implement and register a method override using the same approach (I really don't want to use the compatibility namespace, if possible). Any clue on this regard will be much appreciated!


Solution

  • The custom handler doesn't support to override the platform view's method. If you want to override it, you need to create a custom platform view. Here is my code:

    namespace MauiApp10.Platforms.Android
    {
        // declare the custom platform view
        public class MyScrollView : MauiScrollView
        {
            public bool scroallable = true;
            public MyScrollView(Context context) : base(context){}
            public MyScrollView(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr) { }
            public override bool OnTouchEvent(MotionEvent e)
            {
               switch (e.Action)
               {
                 case MotionEventActions.Down: return scroallable&& base.OnTouchEvent(e);
                 default: return base.OnTouchEvent(e);
               }
            }
            public override bool OnInterceptTouchEvent(MotionEvent ev)
            {
              return scroallable && base.OnInterceptTouchEvent(ev);
            }
        }
    
        // declare the custom handler
        public class MyHandler : ScrollViewHandler
        {
            protected override MauiScrollView CreatePlatformView()
            {
                var scrollView = new MyScrollView(
    new ContextThemeWrapper(MauiContext!.Context, Resource.Style.scrollViewTheme), null!,
    Resource.Attribute.scrollViewStyle)
                {
                    ClipToOutline = true,
                    FillViewport = true
                };
                return scrollView;
            }
           
        }
    }
    

    And use the custom handler in the program.cs:

                      .ConfigureMauiHandlers(handlers =>
                      {
    #if ANDROID
                            handlers.AddHandler(typeof(ScrollView), typeof(MyHandler));
    #endif
                      });
    

    And disable the scrollview in the button click event:

        private void Button_Clicked(object sender, EventArgs e)
        {
    #if ANDROID
                var myscrollview = scrollview.Handler.PlatformView as MyScrollView;
              // the scrollview is the x:Name in the xaml
                myscrollview.scroallable = false;
    #endif
          }
    

    And I see you used custom control and custom renderer in the xamarin.forms, you can create the property mapper to map the custom control's scrollable property to the andorid platform view.