添加滑动手势以打开 SplitView 窗格

Posted

技术标签:

【中文标题】添加滑动手势以打开 SplitView 窗格【英文标题】:Adding a swipe gesture to open SplitView Pane 【发布时间】:2015-11-13 12:17:34 【问题描述】:

我正在尝试向 UWP 的 SplitView 控件(又名“汉堡菜单”)添加滑动手势,类似于 Pivot 控件的向左/向右滑动。如何设置手势来更改它的显示模式?

ios 8 及更高版本中,我可以使用 UISplitViewController 并设置 PresentWithGesture 属性来做到这一点,但在 WinRT 中没有类似的东西。

现在看完这篇博客:http://blogs.msdn.com/b/cdndevs/archive/2015/07/10/uwp-new-controls-part-2-splitview.aspx,我意识到SplitView控件中有DisplayMode属性,我应该使用VisualStateManager来改变它的状态但是我怎样才能使用vsm来平移左窗格呢?我不知道这可以通过 vsm 实现。

任何帮助/提示将不胜感激。

【问题讨论】:

【参考方案1】:

有趣的问题! :)

我最近创建了一个SwipeableSplitView,它扩展了SplitView 控件以在DisplayMode 设置为Overlay 时启用从左边缘滑动 手势(因为我没有看到在其他模式下拥有它的意义,但随时需要扩展它)。

我所做的只是在控件的样式中,在PaneRoot 层之上创建另一个层并处理那里的所有手势。

<Grid x:Name="PaneRoot" ManipulationMode="TranslateX" Grid.ColumnSpan="2" HorizontalAlignment="Left" Background="TemplateBinding PaneBackground" Width="Binding TemplateSettings.OpenPaneLength, RelativeSource=RelativeSource Mode=TemplatedParent">
    <Grid.Clip>
        <RectangleGeometry x:Name="PaneClipRectangle">
            <RectangleGeometry.Transform>
                <CompositeTransform x:Name="PaneClipRectangleTransform" />
            </RectangleGeometry.Transform>
        </RectangleGeometry>
    </Grid.Clip>
    <Grid.RenderTransform>
        <CompositeTransform x:Name="PaneTransform" TranslateX="Binding RenderTransform.TranslateX, ElementName=PanArea" />
    </Grid.RenderTransform>
    <Border Child="TemplateBinding Pane" />
    <Rectangle x:Name="HCPaneBorder" Fill="ThemeResource SystemControlForegroundTransparentBrush" HorizontalAlignment="Right" Visibility="Collapsed" Width="1" x:DeferLoadStrategy="Lazy" />
</Grid>

<!--a new layer here to handle all the gestures -->
<Grid x:Name="OverlayRoot" Grid.ColumnSpan="2">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Binding TemplateSettings.OpenPaneGridLength, RelativeSource=RelativeSource Mode=TemplatedParent" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <!--the actual element for panning, manipulations happen here-->
    <Rectangle x:Name="PanArea" Fill="Transparent" ManipulationMode="TranslateX" Width="Binding PanAreaThreshold, RelativeSource=RelativeSource Mode=TemplatedParent" Grid.Column="1">
        <Rectangle.RenderTransform>
            <CompositeTransform TranslateX="Binding PanAreaInitialTranslateX, RelativeSource=RelativeSource Mode=TemplatedParent" />
        </Rectangle.RenderTransform>
    </Rectangle>
    <!--this is used to dismiss this swipeable pane-->
    <Rectangle x:Name="DismissLayer" Fill="Transparent" Grid.Column="2" />
</Grid>

在更新新层变换对象的TranslateX 的同时,我也在更新PaneRoot 以保持它们的位置同步。

void OnManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)

    _panAreaTransform = PanArea.RenderTransform as CompositeTransform;
    _paneRootTransform = PaneRoot.RenderTransform as CompositeTransform;

    if (_panAreaTransform == null || _paneRootTransform == null)
    
        throw new ArgumentException("Make sure you have copied the default style to Generic.xaml!!");
    


void OnManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)

    var x = _panAreaTransform.TranslateX + e.Delta.Translation.X;

    // keep the pan within the bountry
    if (x < PanAreaInitialTranslateX || x > 0) return;

    // while we are panning the PanArea on X axis, let's sync the PaneRoot's position X too
    _paneRootTransform.TranslateX = _panAreaTransform.TranslateX = x;


void OnManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)

    var x = e.Velocities.Linear.X;

    // ignore a little bit velocity (+/-0.1)
    if (x <= -0.1)
    
        CloseSwipeablePane();
    
    else if (x > -0.1 && x < 0.1)
    
        if (Math.Abs(_panAreaTransform.TranslateX) > Math.Abs(PanAreaInitialTranslateX) / 2)
        
            CloseSwipeablePane();
        
        else
        
            OpenSwipeablePane();
        
    
    else
    
        OpenSwipeablePane();
    

请记住,因为IsPaneOpen 属性不是虚拟的,所以我必须创建另一个IsSwipeablePaneOpen 来包裹前者。因此,当您想使用 IsPaneOpen 属性时,请改用 IsSwipeablePaneOpen

这就是它在我在 GitHub 中创建的演示应用程序中的工作方式。你可以找到完整的源代码here。


学分

SplitView 模板是由 Koen Zwikstra 的出色 Visual Studio UWP templates 生成的。 页面动画和其他一些实现的灵感来自this post 来自杰里尼克松。

【讨论】:

我最近更新了 GitHub 存储库,添加了另一个很酷的功能 - IsPanSelectorEnabled 属性,它允许您通过在窗格底部区域向上/向下平移来选择菜单项。我相信这将对使用大型手机的用户有所帮助,因为他们不再需要伸展手指即可到达顶部区域!现场演示:youtube.com/watch?v=K47MHJFe4dQ @JerryNixon-MSFT 我想这取决于。将它与Pivot 或启用水平滑动的页面一起使用可能不是一个好主意(例如,向左/向右滑动以删除/完成项目)。但我认为至少应该有一个选项供开发人员选择适合他们的应用程序的任何东西。 :) @JustinXL 恕我直言,这整个滑动打开应该按照它在 android 上的方式实现 - 如果滑动事件来自屏幕外部,或者从最左边的几个像素列开始(我认为在 Android ,在高清屏幕上为 12 或 24 像素),将其作为抽屉/窗格的手势处理,否则将其作为通用页面手势处理。这样可以避免与可滑动的标签混淆。 @fonix232 这就是为什么在源代码中有一个名为 PanAreaThreshold 的 dp。例如,您可以将其更改为 12 或 24 像素。 @Jerry:是的,当然。也就是说,作为在切换到 WP 之前在 iOS 上经常使用此手势的人,我很乐意看到它在所有应用程序中实现。 (在相关的说明中,VS 甚至没有提供与 SplitView 一起使用的内置汉堡控件,而您必须在 Template10 中滚动自己的控件,这令人难以置信 - 难怪汉堡控件 UX 如此可怕地不一致甚至是内置应用程序?!)【参考方案2】:

嗯,vsm 用于在该博客中制作响应式 UI。要在 SplitView 中添加滑动手势,我是这样做的:

在SplitView 内容的根面板上检测手势,并添加一些涉及它的Manipulatioin 事件处理程序。 在 Manipulation 事件中处理 SplitView 的 IsPaneOpen 属性。

【讨论】:

This 是我几个月前为演示此功能所做的演示。这是一个 Windows Phone 8.1 项目,当然我没有使用 SplitView 来实现该功能。相反,该演示包含一个带有一些 PivotItems 的 Pivot 控件和一个我制作的汉堡菜单。 嘿杰瑞! @JerryNixon-MSFT 你应该看看我的答案。 :)

以上是关于添加滑动手势以打开 SplitView 窗格的主要内容,如果未能解决你的问题,请参考以下文章

在 Ionic 2 中禁用侧面菜单滑动以打开登录页面(或任何页面)的手势

从一个视图控制器访问一组图像到另一个视图控制器,以添加滑动手势识别器

添加手势识别器以允许在 Swift 中的 MapView 上水平滑动

将向上滑动手势识别器添加到模态视图

在不干扰平移的情况下向 MKMapView 添加左滑动手势

具有屏幕边缘滑动手势的水平 uitableview