Search code examples
wpfchromiumcefsharpchromium-embedded

How can the 'fling/flick scrolling' be disabled in CefSharp.Wpf?


Is there a way to disable the 'touch fling scrolling' behaviour in CefSharp.Wpf (or potentially via Cef, Chromium, etc)?

I appreciate it's an unusual request as touch fling scrolling is a great feature. But unfortunately, I need to integrate with a dodgy touch screen hw/driver which works nicely for drag.. but fails miserably when flick scrolling as it consistently 'over scrolls'. e.g. a gentle flick on the screen causes the page content to scroll multiple pages (instead of just a few lines).


Solution

  • There's no CefSharp/Cef/Chromium flag or public method available to cancel Chromium's fling behavior.

    Fortunately though, Chromium does internally cancel the fling behavior under certain scenarios (refer Chromium's fling_controller.cc). One such scenario is a user touch down event.

    So the trick is to simulate a touch down cancel event pair. Because CefSharp doesn't offer a 'post touch down' event for users to implement the logic (i.e. too much of an edge case), the easiest way is to implement the feature via a derived class.

    Here's a complete example (with dependency property binding) in case it helps anyone else look for the same solution..

    public class ChromiumWebBrowserEx : ChromiumWebBrowser
    {
        public static readonly DependencyProperty IsFlingDisabledProperty = DependencyProperty.Register("IsFlingDisabled", typeof(bool), typeof(ChromiumWebBrowserEx));
    
        public bool IsFlingDisabled
        {
            get => (bool) GetValue(IsFlingDisabledProperty);
            set => SetValue(IsFlingDisabledProperty, value);
        }
    
        protected override void OnTouchUp(TouchEventArgs e)
        {
            base.OnTouchUp(e);
    
            if (IsFlingDisabled)
                CancelFling(e);
        }
    
        private void CancelFling(TouchEventArgs e)
        {
            // cancel the fling by creating a pseudo touch down followed by a cancellation
            var touchPoint = e.GetTouchPoint(this);
            if (touchPoint.Action == TouchAction.Up)
            {
                var touchEvent = new TouchEvent
                    {
                        Id = e.TouchDevice.Id,
                        X = (float) touchPoint.Position.X,
                        Y = (float) touchPoint.Position.Y,
                        PointerType = PointerType.Touch,
                        Modifiers = WpfExtensions.GetModifierKeys(),
                        Type = TouchEventType.Pressed
                    };
    
                GetBrowser().GetHost().SendTouchEvent(touchEvent);
    
                touchEvent.Type = TouchEventType.Cancelled;
                GetBrowser().GetHost().SendTouchEvent(touchEvent);
            }
        }
    }