Question

I'm currently capturing the PointerMoved event on the page to use with a horizontal menu. So the user can swipe left/right and the page will animate accordingly.

This works when the user touches a static element (TextBlock etc.) but if they touch a ListView it captures the touch events.

How can I implement the ListView so when the user scrolls vertically it works as normal, but when the user scrolls horizontally it passes the events to my code?

Was it helpful?

Solution

It is possible, but you will need a small trick. As a refference I put here Rob Caplan's article.

Let's start:

  1. First - where are your events? - answer is simple - while you have ScrollViewer enabled, all events are intercepted by it and handeled. You ListView will get only PointerEntered event and just after it PointerExited, all further proccesing is handeled by ScrollViewer. That is the problem. But as I've said there is a method to do what you want.

  2. For this purpose lets assume that you have defined your ListView only with VerticalScroll:

    <ListView Name="myList" ScrollViewer.HorizontalScrollMode="Disabled">
    

    Of course it is possible to do for both directions, but it's a simple example.

  3. Now let's have a look at constructor of a Page:

    PointerPoint firstPoint = null;
    ScrollViewer listScrollviewer = null;
    
    public MainPage()
    {
      this.InitializeComponent();
      myList.ItemsSource = yourItemSource;
      myList.PointerEntered += myList_PointerEntered;
      myList.PointerMoved += myList_PointerMoved;
    }
    

    Nothing weird here - I just subscribe to events, and declare two variables firstPoint and listScrollviewer, which I'll need later.

  4. We will need also to get our ScrollViewer of our ListView - the following method will do the job:

    public static ScrollViewer GetScrollViewer(DependencyObject depObj)
    {
        if (depObj is ScrollViewer) return depObj as ScrollViewer;
    
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            var child = VisualTreeHelper.GetChild(depObj, i);
    
            var result = GetScrollViewer(child);
            if (result != null) return result;
        }
        return null;
    }
    
  5. Now - to enable our events we will need to disable the ScrollViewer:

    private ScrollViewer DisableScrolling(DependencyObject depObj)
    {
        ScrollViewer foundOne = GetScrollViewer(depObj);
        if (foundOne != null) foundOne.VerticalScrollMode = ScrollMode.Disabled;
        return foundOne;
    }
    
  6. We will disable the ScrollViewer upon PointerEntered event which is fired. In this step we will also remember the pressed PointerPoint - as we have disable Scrollviewer, we will have to scroll it manually - that is what we need this PointerPoint for.

    private void myList_PointerEntered(object sender, PointerRoutedEventArgs e)
    {
        firstPoint = e.GetCurrentPoint(myList);
        if (listScrollviewer == null) listScrollviewer = DisableScrolling(myList);
    }
    
  7. Finally our PointerMoved event, which now wil be fired as we had disabled ScrollViewer - moving ScrollViewer + other code you need to put there:

    private void myList_PointerMoved(object sender, PointerRoutedEventArgs e)
    {
        if (listScrollviewer != null)
        {
            PointerPoint secondPoint = e.GetCurrentPoint(myList);
            double verticalDifference = secondPoint.Position.Y - firstPoint.Position.Y;
            listScrollviewer.ChangeView(null, listScrollviewer.VerticalOffset - verticalDifference, null);
        }
        // some other code you need
    }
    

Few remarks:

  • this method still needs much tuning, but hopefuly will show you how to achieve your goal,
  • you may need also to separate some small horizontal movements from vertical ones,
  • if your ListView or other Control has horizontal scroll, then you will also need to disable and handle it,
  • this method won't probably work so smooth like original ScrollViewer.

I've also put a simple working example here at OneDrive.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top