Disable pivot swipe during scrolling on UWP app

2019-05-18 21:57发布

问题:

I'm developing Universal Window App on Windows 10 platform and my page looks like this example :

Main page contain a pivot control with 4 pivot items (PAGE 1, PAGE 2, ...). PAGE 1 contain a red stackpanel which contain a listview with horizontal scrolling.

My problem is, when i want to scroll my red listview horitontaly, my pivot swipe to next page. I want to disable pivot swipe during listview scrolling (But only during listview scrolling).

I tried to get scrollviewer from listview and to listen viewChange from scrollviewer to disable pivot swipe but without success. Everything work but set IsHitTestVisible to false seems to not work. Here is my code :

ScrollViewer scrollViewer = ListViewHelper.GetScrollViewer(myListView);
scrollViewer.ViewChanged += scrollview_ViewChanged;

private void scrollview_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
    {
        var scrollviewer = sender as ScrollViewer;

        if (e.IsIntermediate)
        {
            mainPivot.IsHitTestVisible = false;
        } else
        {
            mainPivot.IsHitTestVisible = true;
        }
    }

And my ListViewHelper class :

public static class ListViewHelper
{
    public static ScrollViewer GetScrollViewer(this DependencyObject element)
    {
        if (element is ScrollViewer)
        {
            return (ScrollViewer)element;
        }

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
        {
            var child = VisualTreeHelper.GetChild(element, i);

            var result = GetScrollViewer(child);
            if (result == null)
            {
                continue;
            }
            else
            {
                return result;
            }
        }

        return null;
    }
}

And my xaml code :

<Pivot  x:Name="mainPivot">

        <PivotItem x:Name="pivot1">

            <!-- Header -->
            <PivotItem.Header>
                <controls:TabHeader x:Uid="pivot1HeaderTitle"
                                    Label=""
                                    Glyph="&#xea34;"/>
            </PivotItem.Header>

            <!-- Content -->
            <Grid>

                <Grid.RowDefinitions>
                    <RowDefinition Height="110" />
                    <RowDefinition Height="30" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>

                <StackPanel     x:Name="localeEditionsFavory"
                                Grid.Row="0">

                    [...]

                    <!-- HERE is my listview -->
                    <ListView   x:Name="localeEditionsFavoryList"
                                Height="80"
                                ItemsSource="{Binding FavoritesItems}"
                               ScrollViewer.HorizontalScrollBarVisibility="Auto"
                                ScrollViewer.HorizontalScrollMode="Enabled">

                        <ListView.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel Orientation="Horizontal">
                                </StackPanel>
                            </ItemsPanelTemplate>
                        </ListView.ItemsPanel>

                        <ListView.ItemContainerStyle>
                            <Style TargetType="ListViewItem">
                                <Setter Property="Padding" Value="0,0,0,0" />
                                <Setter Property="Margin" Value="10" />
                                <Setter Property="HorizontalContentAlignment" Value="Stretch" />
                            </Style>
                        </ListView.ItemContainerStyle>

                        <ListView.ItemTemplate>
                            <DataTemplate>

                                <Grid   x:Name="favoryList">

                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="90" />
                                    </Grid.ColumnDefinitions>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="55" />
                                    </Grid.RowDefinitions>

                                    <Rectangle  x:Name="strokedasharray" 
                                                Grid.Column="0"
                                                Grid.Row="0"
                                                Fill="White"
                                                Opacity="0.2"
                                                RadiusX="5"
                                                RadiusY="5"/>

                                    <TextBlock  Grid.Column="0"
                                                Grid.Row="0"
                                                Text="{Binding FavoriteItem.EditionKey}" 
                                                TextWrapping="WrapWholeWords" 
                                                Height="auto"
                                                Foreground="White"
                                                FontSize="14"
                                                Margin="2" 
                                                HorizontalAlignment="Center"
                                                VerticalAlignment="Center" 
                                                TextTrimming="CharacterEllipsis" 
                                                TextAlignment="Center"/>

                                </Grid>

                            </DataTemplate>
                        </ListView.ItemTemplate>

                    </ListView>

                </StackPanel>

                [...]

                <ScrollViewer VerticalScrollMode="Auto" 
                              VerticalScrollBarVisibility="Auto"
                              Grid.Row="3">

                    <controls:NewsItemControl   x:Name="NewsListControl"
                                                Visibility="{Binding Busy, Converter={StaticResource InverseBoolToVisibilityConverter}}"/>

                </ScrollViewer>

            </Grid>

        </PivotItem>

        [...]

Does any one has an idea to resolve this problem ?

回答1:

The undesired behavior you're seeing is a result of scroll chaining. Both the ListView and Pivot contain ScrollViewers in their control templates making this a nested ScrollViewer situation. When you have a ScrollViewer contained within the tree of another and attempt to scroll via touch beyond the extent of the inner ScrollViewer then the outer ScrollViewer engages and begins scrolling.

With chaining disabled you’d only be able to scroll the outer ScrollViewer by beginning the manipulation on a hit-testable area of the outer ScrollViewer. Chaining is enabled by default on the ScrollViewer so when you attempt to scroll past the end of the list the chaining kicks in and is recognized by the Pivot as an attempt to “scroll” to the next pivot item.

Disable chaining on the ListView's ScrollViewer (and remove the ListViewHelper stuff in code behind) by setting the ScrollViewer.IsHorizontalScrollChainingEnabled="False" attached property:

                <!-- HERE is my listview -->
                <ListView   x:Name="localeEditionsFavoryList"
                            Height="80"
                            ItemsSource="{Binding FavoritesItems}"
                            ScrollViewer.IsHorizontalScrollChainingEnabled="False"
                            ScrollViewer.HorizontalScrollBarVisibility="Auto"
                            ScrollViewer.HorizontalScrollMode="Enabled">


回答2:

If you need to make the ListView horizontal scrolling, you can achieve this just in XAML code by modifying the style of the ListView like this:

<Style x:Key="ListViewStyle" TargetType="ListView">
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled" />
    <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Enabled" />
    <Setter Property="ScrollViewer.IsHorizontalRailEnabled" Value="True" />
    <Setter Property="ScrollViewer.VerticalScrollMode" Value="Disabled" />
    <Setter Property="ScrollViewer.IsVerticalRailEnabled" Value="False" />
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <ItemsStackPanel Orientation="Horizontal" />
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
</Style>

Now if you disable the scrolling of Pivot when you scroll the ListView of "PAGE 1", you can edit your code like this:

public void Page_Loaded(object sender, RoutedEventArgs e)
{
    var scrollViewer = FindChildOfType<ScrollViewer>(mainPivot);
    scrollViewer.HorizontalScrollMode = ScrollMode.Disabled;
}

private void PivotSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (mainPivot.SelectedIndex == 0)
    {
        var scrollViewer = FindChildOfType<ScrollViewer>(mainPivot);
        scrollViewer.HorizontalScrollMode = ScrollMode.Disabled;
    }
}

public static T FindChildOfType<T>(DependencyObject root) where T : class
{
    var queue = new Queue<DependencyObject>();
    queue.Enqueue(root);
    while (queue.Count > 0)
    {
        DependencyObject current = queue.Dequeue();
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(current); i++)
        {
            var child = VisualTreeHelper.GetChild(current, i);
            var typedChild = child as T;
            if (typedChild != null)
            {
                return typedChild;
            }
            queue.Enqueue(child);
        }
    }
    return null;
}