2
votes

I'm kinda new to this WPF world. And I'm kinda confused on why my scrollview is consuming my touch event.

My current situation is this:

I have this ScrollViewer with an ItemsControl. In this ItemsControl I'm using a Wrappanel to show a number of Rectangles. My ScrollViewer should make it possible to scroll vertically to show the Rectangles that are wrapped downward. On each of these Rectangles I made a CustomBehaviour with all kinds of handlers. One of these handlers is a 'creatively' made way to handle LongPressGestures.

The problem is as follows, before my longpress is detected by the behaviour, my ScrollViewer is capturing my TouchDevice in its PreviewTouchMove handler.

How can I prevent my ScrollViewer from capturing my TouchDevice too early? How can I make sure that I can scroll my ScrollViewer and do my LongPress, DoubleClick (which still works), SingleClick (which still works as well) and other gestures I might add to this custom behaviour?

I've found similar questions on stackoverflow and google that I just not figure out for my specific case.

Something with a CustomThumb <-- This link solves the problem by making a CustomThumb. Can I somehow re-use this for my behaviour? By capturing the TouchDevice early in my behaviour handlers?

If all things fail. Is there an alternative to this ScrollViewer & CustomBehaviour combination?


EDIT:

In the meantime. I retried the CustomThumb-method. I got the longpress to work now from my CustomBehaviour while the UIElements (with that behaviour) are located on a ScrollViewer. However the scroll functionality of the ScrollViewer still does not work. The bounty I added will also be awarded to the person that helps me get that to work properly again (since the answer should lie in the same direction as this CustomThumb solution).

1
Umm, cuz bubbling and tunneling events?UIlrvnd
Yes, this is probably the reason this is happening, but in some instances I've experienced the event that has bubbled to be passed on and not consumed. But in this case the ScrollViewer decides to hold the event and 'forgets' to pass it on to the view I had expected the event to happen. How can I make sure that my longpress still happens. So i can both scroll my scrollview when not longpressing the rectangles I made my behaviours on?Totumus Maximus

1 Answers

4
votes

I had a similar issue before. I could not use scrolling, touch and style in the same time. At the end I developped a custom ScrollViewer. It is easier than you think, since you only need to watch / change some basic methods.

In your case you can check whether the user pressed on an empy surface or a list item. If it is a list item, you need to check whether it was a short press (so, touchup also occured after touchdown) or a long one.

Scrolling can be configured with PanningMode. It allows you to scroll with finger all over the usercontrol.

Here is my version of scrollviewer. It turns scrolling mode off when user pressed a button and turned on afterwards.

public class ScrollViewerWithTouch : ScrollViewer   
{
      /// <summary>
      /// Original panning mode.
      /// </summary>
      private PanningMode panningMode;

  /// <summary>
  /// Set panning mode only once.
  /// </summary>
  private bool panningModeSet;

  /// <summary>
  /// Initializes static members of the <see cref="ScrollViewerWithTouch"/> class.
  /// </summary>
  static ScrollViewerWithTouch()
  {
     DefaultStyleKeyProperty.OverrideMetadata(typeof(ScrollViewerWithTouch), new FrameworkPropertyMetadata(typeof(ScrollViewerWithTouch)));
  }

  protected override void OnManipulationCompleted(ManipulationCompletedEventArgs e)
  {
     base.OnManipulationCompleted(e);

     // set it back
     this.PanningMode = this.panningMode;
  }

  protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
  {
     // figure out what has the user touched
     var result = VisualTreeHelper.HitTest(this, e.ManipulationOrigin);
     if (result != null && result.VisualHit != null)
     {
        var hasButtonParent = this.HasButtonParent(result.VisualHit);

        // if user touched a button then turn off panning mode, let style bubble down, in other case let it scroll
        this.PanningMode = hasButtonParent ? PanningMode.None : this.panningMode;
     }

     base.OnManipulationStarted(e);
  }

  protected override void OnTouchDown(TouchEventArgs e)
  {
     // store panning mode or set it back to it's original state. OnManipulationCompleted does not do it every time, so we need to set it once more.
     if (this.panningModeSet == false)
     {
        this.panningMode = this.PanningMode;
        this.panningModeSet = true;
     }
     else
     {
        this.PanningMode = this.panningMode;
     }

     base.OnTouchDown(e);         
  }

  private bool HasButtonParent(DependencyObject obj)
  {
     var parent = VisualTreeHelper.GetParent(obj);

     if ((parent != null) && (parent is ButtonBase) == false)
     {
        return HasButtonParent(parent);
     }

     return parent != null;
  }
}