Search code examples
wpfxamlsliding

Sliding images with finger holding and moving in WPF, just like smartphone


This is my code:

 <Grid Grid.Row="1" >
     <Image Stretch="Fill" Source="{Binding Path=MyImage}"/>
 </Grid>

I have an image in my view, and I set its surce by binding.

Outside of this image, I have two arrows (left arrow and right). If the user clicks on the right arrow, I'm showing him the next image, if he clicks on the right arrow again - the next one, etc. This works very well.

Now I want to do it to work just like we slide images in our smartphones, by using our thumb, without using arrows. So, I need to make the images slide by clicking the mouse button, holding it and then moving it left or right.

How can I do that?


Solution

  • You could create a Behavior that will handle the scrolling:

    public class ScrollViewerTouchScrollBehaviour : Behavior<ScrollViewer>
        {
            private double? _horizontalOffset;
            private Point? _position;
            private double _sensitivity = 1;
    
            public double Sensitivity
            {
                get { return _sensitivity; }
                set { _sensitivity = value; }
            }
    
            protected override void OnAttached()
            {
                base.OnAttached();
                AssociatedObject.PreviewMouseLeftButtonDown += PreviewMouseLeftButtonDown;
                AssociatedObject.PreviewMouseMove += PreviewMouseMove;
                AssociatedObject.PreviewMouseLeftButtonUp += PreviewMouseLeftButtonUp;
            }
    
            protected override void OnDetaching()
            {
                base.OnDetaching();
                AssociatedObject.PreviewMouseLeftButtonDown -= PreviewMouseLeftButtonDown;
                AssociatedObject.PreviewMouseMove -= PreviewMouseMove;
                AssociatedObject.PreviewMouseLeftButtonUp -= PreviewMouseLeftButtonUp;
            }
    
            private void PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                _horizontalOffset = AssociatedObject.HorizontalOffset;
                _position = e.GetPosition(AssociatedObject);
            }
    
            private void PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
            {
                AssociatedObject.ReleaseMouseCapture();
            }
    
            private void PreviewMouseMove(object sender, MouseEventArgs e)
            {
                if (e.LeftButton != MouseButtonState.Pressed || !_horizontalOffset.HasValue || !_position.HasValue)
                {
                    return;
                }
    
                var point = e.GetPosition(AssociatedObject);
    
                var diff = point.X / Sensitivity - _position.Value.X / Sensitivity;
    
                AssociatedObject.ScrollToHorizontalOffset(_horizontalOffset.Value - diff);
            }
        }
    

    The Sensitivity property allows you to tweek the speed of the scrolling. Experiment with different values to see what best suits your application.

    Then in your xaml place the content that you want to be able to scroll inside a ScrollViewer and add the behavior:

    <ScrollViewer CanContentScroll="True"
                  HorizontalScrollBarVisibility="Hidden"
                  VerticalScrollBarVisibility="Hidden"
                  VerticalAlignment="Stretch">
        <!-- Your content here -->
        <i:Interaction.Behaviors>
            <behaviours:ScrollViewerTouchScrollBehaviour Sensitivity="1" />
        </i:Interaction.Behaviors>
    </ScrollViewer>
    

    You will of course need to add a reference to Systems.Windows.Interactivty in your behavior and in the xaml.

    You could alternatively just handle the events in code-behind if you don't want to use a behavior.