Al John
Al John

Reputation: 900

Prevent propagation of swipe gestures onto parent controls in .NET MAUI?

Is there a way to prevent the propagation of swipe gestures from a control onto its parent control in .NET MAUI?

For this project I'm using a DXRangeSlider within a TabView. Both are free MAUI controls (at the time of writing) created by DevExpress. Nonetheless, any swipable control that is defined as content for the tab view, causes this issue.

When TabView.SwipeEnabled = True you can swipe left or right on the tab content to navigate to the previous or next tab in the sequence. When any of the tab contents contain swipable controls, you cannot swipe these controls without calling the tab swipe action.

I'd like the TabView to ignore my swipe gesture when the swipe gesture is targeting a child control.

Is there any way to achieve this?

The XAML setup is nothing too complicated:

<dxcontrols:TabView>
    <dxcontrols:TabViewItem>
        ...
    </dxcontrols:TabViewItem>
    <!-- Parent swipable control: -->
    <dxcontrols:TabViewItem>
        ...
        <!-- Child swipable control: -->
        <dxeditors:DXRangeSlider/>
        ...
    </dxcontrols:TabViewItem>
    <dxcontrols:TabViewItem>
        ...
    </dxcontrols:TabViewItem>
</dxcontrols:TabView>

I believe the same issue would exist when using a MAUI Slider or any control with swipe gesture recognizers within a CarouselView that has swipe enabled.

Upvotes: 1

Views: 432

Answers (2)

Al John
Al John

Reputation: 900

In addition to LiyunZhangs' answer:

I figured out specificially for DX Slider controls (thanks to their support center), that they have TapPressed and TapReleased events, that you can use to set the TabView.SwipeEnabled property to false while using the slider and back to true when the tap is released.

1. Simply set the events on the DXSlider or DXRangeSlider control:

<dxcontrols:TabView x:Name="FilterTabView">
    <dxcontrols:TabViewItem>
        <!-- ... content ... -->
    </dxcontrols:TabViewItem>
    <dxcontrols:TabViewItem>

        <dxeditors:DXRangeSlider 
            TapPressed="DXRangeSlider_TapPressed" 
            TapReleased="DXRangeSlider_TapReleased"/>

    </dxcontrols:TabViewItem>
    <dxcontrols:TabViewItem>
        <!-- ... content ... -->
    </dxcontrols:TabViewItem>
</dxcontrols:TabView>

2. Then use the event handlers to change TabView.SwipeEnabled in code-behind:

private void DXRangeSlider_TapPressed(object sender, DevExpress.Maui.Core.DXTapEventArgs e)
{
    FilterTabView.SwipeEnabled = false;
}

private void DXRangeSlider_TapReleased(object sender, DevExpress.Maui.Core.DXTapEventArgs e)
{
    FilterTabView.SwipeEnabled = true;
}

For MVVM you can use MCT EventToCommandBehavior to use commanding instead:

<dxeditors:DXRangeSlider>
    <dxeditors:DXRangeSlider.Behaviors>
        <toolkit:EventToCommandBehavior 
            EventName="TapPressed"
            Command="{Binding TapPressedCommand}"/>
        <toolkit:EventToCommandBehavior 
            EventName="TapReleased"
            Command="{Binding TapReleasedCommand}"/>
    </dxeditors:DXRangeSlider.Behaviors>
</dxeditors:DXRangeSlider>

I believe LiyunZhangs' answer gets closer to the ideal solution, because it is a more generic approach for all MAUI controls. I just thought this might be useful for others who use the same DX controls.

Upvotes: 1

Liyun Zhang - MSFT
Liyun Zhang - MSFT

Reputation: 14434

I believe the same issue would exist when using a MAUI Slider or any control with swipe gesture recognizers within a CarouselView that has swipe enabled.

Yes, there is the same issue. And I can resolve it by adding IOnTouchListener for the android native view. And prevent passing the swipe event from the child view to the parent view.

The Listener:

    public class SliderListener : Java.Lang.Object, IOnTouchListener
    {
        public bool OnTouch(global::Android.Views.View v, MotionEvent e)
        {
            if (e.Action == MotionEventActions.Down || e.Action == MotionEventActions.Move)
            {
                v.Parent.RequestDisallowInterceptTouchEvent(true);
                // prevent the parent view get the swipe event
            }
            else
            {
                v.Parent.RequestDisallowInterceptTouchEvent(false);
            }
            return false;
        }
    }

And then you can add the Listener for the Slider in the code behind or custom handler.

1. In the code behind: the slider have a name in the xaml such as : <Slider x:Name="slider">. And in the code behind:

protected override void OnHandlerChanged()
        {
            base.OnHandlerChanged();
#if ANDROID
            (slider.Handler.PlatformView as Android.Widget.Seekbar)
                     .SetOnTouchListener(new SliderListener());
#endif
        }

2. Set it in the custom handler: You can refer to the official document about Customize controls with handlers. And do this in the Custom Handler's ConnectHandler method.

protected override void ConnectHandler(SeekBar platformView)
{
    base.ConnectHandler(platformView);
    platformView.SetOnTouchListener(new SliderListener());
}

You can check the source code about the DXRangeSlider or just debug to get the platform view. And then using the same method to resolve the issue.

Upvotes: 1

Related Questions