Adding a swipe gesture to open SplitView Pane

后端 未结 2 1433
眼角桃花
眼角桃花 2020-12-02 07:58

I am trying to add a swipe gesture to the SplitView control (aka \"hamburger menu\") of UWP, similar to the swipe left/right of a Pivot control. How can I set a gesture to c

相关标签:
2条回答
  • 2020-12-02 08:50

    Well, vsm is used in making Responsive UI in that blog. To add a swipe gesture in SplitView, here's what I did:

    • Detect gesture on your root panel of SplitView's Content, and add some Manipulatioin involved event handler of it.
    • Handle SplitView's IsPaneOpen property in Manipulation event.
    0 讨论(0)
  • 2020-12-02 08:51

    Interesting question! :)

    I recently created a SwipeableSplitView which extends the SplitView control to enable a swipe from left edge gesture when the DisplayMode is set to Overlay (as I don't see the point to have it in other modes, but feel free to extend it whenever needed).

    All I am doing is, inside the control's style, create another layer on top of the PaneRoot layer and handle all the gestures there.

    <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>
    

    While updating the TranslateX of the new layer's transform object, I am also updating the PaneRoot's to keep their position in sync.

    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();
        }
    }
    

    Keep in mind that because the IsPaneOpen property is not virtual, I have to create another one IsSwipeablePaneOpen to wrap the former around. So whenever you feel like using the IsPaneOpen property, use IsSwipeablePaneOpen instead.

    This is how it works in a demo app I created in GitHub. You can find the full source code here.


    Credits

    • The SplitView template was generated from Koen Zwikstra's awesome Visual Studio UWP templates.
    • Page animations and some other implementations were inspired by this post from Jerry Nixon.
    0 讨论(0)
提交回复
热议问题