2

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

Community
  • 1
  • 1
Totumus Maximus
  • 7,543
  • 6
  • 45
  • 69
  • Umm, cuz bubbling and tunneling events? – UIlrvnd Nov 08 '13 at 10:26
  • 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 Nov 08 '13 at 10:30

1 Answers1

4

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;
  }
}
Andras Sebo
  • 1,120
  • 8
  • 19
  • Yeah, this is pretty much what I started on as well! It is like 80% of what I wanted now. Do you think there is a way to allow scrolling while starting from the Button (in your case) or any object with my behavior (in my case) ? – Totumus Maximus Nov 11 '13 at 17:33
  • Do you mean, if user touches first a content and start scrolling with fingers? In this case PanningMode and IsManipulationEnabled are supposed to be helpful ( http://msdn.microsoft.com/en-us/library/system.windows.controls.scrollviewer.panningmode%28v=vs.100%29.aspx). Main issue is that touches are going to be a mouse clicks, please check this article: http://nui.joshland.org/2010/04/why-wont-wpf-controls-work-with-touch.html – Andras Sebo Nov 12 '13 at 08:06
  • Yeah. I solved this just now. I made an extra check in my customBehaviour to detect a touchMove. Searched for the scrollviewer from within my VisualTree. Then gave my touchcapture from my behaviour to the scrollviewer manually. This way I can scroll with a touch when starting on an object that implements my custom behaviour. Your help was invaluable tho. So you have my thanks. – Totumus Maximus Nov 12 '13 at 10:41