在移动设备上忽略网格上的滑动

Swipe on grid is ignored on mobile

当用户从 ComboBox 中选择条目时,我以编程方式填充 ListView。

我想通过允许用户滑动 left/right 作为更改内容的替代方式来提高应用程序的可用性。

更确切地说,当用户执行轻拂或短扫时,列表视图会填充一组不同的项目。我不需要任何操作已经开始的视觉提示(即像 Edge 在使用手势来回导航时所做的那样)

下面是简化的 XAML 结构。

<Grid 
  x:Name="MainGrid" 
  ManipulationMode="TranslateX,  TranslateInertia" 
  Loaded="MainGrid_Loaded"> 

  <ComboBox x:Name="CBMonthPicker" />

  <ListView 
      x:Name="LV" 
      ItemsSource="{Binding CalItems, ElementName=page}"                 
      DataContext="MainPage"                  
      IsItemClickEnabled="True"
      ItemClick="OnItemClick"
      ItemTemplateSelector="{StaticResource MyDataTemplateSelector}">          
  </ListView>
</Grid>

这是我用来检测滑动的代码,它在桌面上有效(如果我用鼠标模拟滑动),但在移动设备上不起作用(Grid_ManipulationDelta 在移动设备上未触发):

    private void MainGrid_Loaded(object sender, RoutedEventArgs e)
    {
        MainGrid.ManipulationDelta += Grid_ManipulationDelta;
    }

    private void Grid_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
    {
        var swiped = e.Cumulative.Translation.X;
        var swipeTriggerThreshhold = 300;
        //if the gesture was not above a set threshold we ignore it
        if (!e.IsInertial || swiped * swiped < swipeTriggerThreshhold * swipeTriggerThreshhold )
            return;            
        //If we got so far, we registered a swipe
        e.Complete(); //end manipulation so we only handle it once  
        if (swiped < 0)  { //we swiped left
            changeDisplayedMonth(-1);
        }
        else{  //we swiped right
            changeDisplayedMonth(1);
        }           
     }

我的猜测是 ListView 或 ListViewItem(s) 以某种方式隐藏了对 Grid 的操作。

我已经尝试使用 ListView 和 ListViewItem 来处理操作,但从未为它们中的任何一个触发 ManipulationDelta,即使我将 ManipulationMode 设置为 TranslateX、TranslateInertia。

SO 上有很多关于滑动 ListViewItem 的问题(如 Outlook 中的特色)。这个问题与那个无关。

因为内容是根据用户输入动态加载的,所以我不想使用 Pivot。即使我可以,这也意味着我的界面会发生重大变化,而我又不想这样做。

您的猜测是正确的 - ListView 有一个 ScrollViewer,它在非常低的水平上工作,提高了性能,但是它首先处理操作,然后 'swallows' 它们。好像前段时间问过a similar question.

有一种方法可以手动处理这种情况 - Rob Caplan's blog post describes it a little, it's quite old, but should bring you some light. You may also take a look at this answer,其中针对 Windows-Runtime 进行了描述,这在 [=20] 上也应该类似=]UWP.

另请注意,如果您将 ListView 放在 Pivot 或带有水平 ScrollViewer,你可以把ListView的ScrollViewer的属性IsHorizontalScrollChainingEnabled改成true ,这会将水平滚动传递给父级。这是一个示例 XAML 代码:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ScrollViewer HorizontalScrollMode="Enabled" HorizontalScrollBarVisibility="Visible">
        <StackPanel Orientation="Horizontal">
            <ListView Width="200" Height="100" ScrollViewer.IsHorizontalScrollChainingEnabled="True">
                <x:String>First Item</x:String>
                <x:String>Item</x:String>
                <x:String>Item</x:String>
                <x:String>Item</x:String>
                <x:String>Last Item</x:String>
            </ListView>
            <Rectangle Width="200" Height="100" Fill="Red"/>
            <Rectangle Width="200" Height="100" Fill="Green"/>
            <Rectangle Width="200" Height="100" Fill="Blue"/>
        </StackPanel>
    </ScrollViewer>
</Grid>

在对 Pivot 进行了大量尝试后,结果完全不尽如人意,我发现了 FlipView,它可以通过一些黑客攻击来实现我想要的。

诀窍是添加两个虚拟 FlipViewItems,一个在包含我们的 ListView 的之前,一个在它之后。下一步是捕获 FlipView 的 SelectionChanged 事件,如果我们的内容之前的 FlipViewItem 已被选中,则触发一个动作,就像向后滑动一样,对于我们内容之后的 FlipViewItem 也是如此。 最后一步是强制选择回到我们的内容 ListViewItem。

以这种方式破解 FlipView 的一个小烦恼是,当强制选择回到我们中间的 FlipViewItem(承载我们内容的那个)时,动画与最终用户期望的相反。这很容易通过在 FlipView 上设置 属性 UseTouchAnimationsForAllNavigation="False" 来解决。

奖励:为了表明在重新填充 ListView 时确实发生了某些事情,我添加了一个进度环。为此,将 FlipView 和 FlipViewItems 以及 ListView 的背景 属性 设置为透明很重要。 ListViewItems 有一个指定的背景来覆盖它。这是实现 ProgressRing 的完全不优雅的解决方案,但是它有效并且很快就想出了 :)。

更新后的 XAML 代码如下所示:

<Grid x:Name="MainGrid" > 
  <ComboBox x:Name="CBMonthPicker" Grid.Row="0"/>

  <ProgressRing  Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" IsActive="True"  />

  <FlipView  x:Name="MainView"  Grid.Row="1"  
               Loaded="MainView_Loaded"
               UseTouchAnimationsForAllNavigation="False" 
               Background="Transparent">

      <FlipViewItem Background="Transparent">               
      </FlipViewItem>

      <FlipViewItem Background="Transparent">
         <ListView  x:Name="LV" 
            ItemsSource="{Binding CalItems, ElementName=page}" DataContext="MainPage"                  
            IsItemClickEnabled="True" ItemClick="OnItemClick"
            ItemTemplateSelector="{StaticResource MyDataTemplateSelector}">          
         </ListView>
      </FlipViewItem>

      <FlipViewItem Background="Transparent">               
      </FlipViewItem>

</Grid>

后面的代码是这样的:

    private void MainView_Loaded(object sender, RoutedEventArgs e)
    {
        MainView.SelectedIndex = 1;
        MainView.SelectionChanged += MainView_SelectionChanged;
    }

    private void MainView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (MainView.SelectedIndex == 0)
        {
            //perform the action for Back Swipe Gesture
            changeDisplayedMonth(-1);

            //force selection back to our content FlipViewItem
            MainView.SelectedIndex = 1;               
        }
        if (MainView.SelectedIndex == 2)
        {
            //perform the action for Forward Swipe Gesture 
            changeDisplayedMonth(1);

            //force selection back to our content FlipViewItem               
            MainView.SelectedIndex = 1;                
        }            
    }