none
[UWP] Only the ManipulationStarting events fire

    Question

  • I have a simple UWP app with a ScrollViewer and ListBox that I am testing with the "Mobile Emulator WVGA 4 inch 512MB" and I want to capture when the user is trying to scroll the listbox up or down:

     <RelativePanel x:Name="thisRelativePanel" SizeChanged="thisRelativePanel_SizeChanged">
      <ScrollViewer x:Name="thisScrollViewer" BorderThickness="1,1,1,1" ManipulationMode="All" [plus handling all manipulation events]>
       <ListBox x:Name="thisListBox" SizeChanged="thisListBox_SizeChanged" BorderThickness="1,1,1,1" ManipulationMode="All" [plus handling all manipulation events]>
        <TextBlock Margin="-1,-10,-1,-10" Width="12" FontSize="12" LineStackingStrategy="BlockLineHeight" LineHeight="20"/>
       </ListBox>
      </ScrollViewer>
     </RelativePanel>
    

    I am handling all of the manipulation events in code to display a message to the Output window when any event is fired:

      private void ThisListBox_ManipulationStarting(object sender, ManipulationStartingRoutedEventArgs e)
      {
       System.Diagnostics.Debug.WriteLine("ThisListBox_ManipulationStarting");
      }
    
      private void ThisListBox_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
      {
       System.Diagnostics.Debug.WriteLine("ThisListBox_ManipulationStarted");
      }
    
      private void ThisListBox_ManipulationInertiaStarting(object sender, ManipulationInertiaStartingRoutedEventArgs e)
      {
       System.Diagnostics.Debug.WriteLine("ThisListBox_ManipulationInertiaStarting");
      }
    
      private void ThisListBox_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
      {
       System.Diagnostics.Debug.WriteLine("ThisListBox_ManipulationDelta");
      }
    
      private void ThisListBox_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
      {
       System.Diagnostics.Debug.WriteLine("ThisListBox_ManipulationCompleted");
      }
    
      private void ThisListBox_PointerMoved(object sender, PointerRoutedEventArgs e)
      {
       System.Diagnostics.Debug.WriteLine("ThisListBox_PointerMoved");
      }
    
      private void ThisListBox_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
      {
       System.Diagnostics.Debug.WriteLine("ThisListBox_PointerWheelChanged");
      }
    
      private void ThisScrollViewer_DirectManipulationStarted(object sender, object e)
      {
       System.Diagnostics.Debug.WriteLine("ThisScrollViewer_DirectManipulationStarted");
      }
    
      private void ThisScrollViewer_DirectManipulationCompleted(object sender, object e)
      {
       System.Diagnostics.Debug.WriteLine("ThisScrollViewer_DirectManipulationCompleted");
      }
    
      private void ThisScrollViewer_ManipulationStarting(object sender, ManipulationStartingRoutedEventArgs e)
      {
       System.Diagnostics.Debug.WriteLine("ThisScrollViewer_ManipulationStarting");
      }
    
      private void ThisScrollViewer_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
      {
       System.Diagnostics.Debug.WriteLine("ThisScrollViewer_ManipulationStarted");
      }
    
      private void ThisScrollViewer_ManipulationInertiaStarting(object sender, ManipulationInertiaStartingRoutedEventArgs e)
      {
       System.Diagnostics.Debug.WriteLine("ThisScrollViewer_ManipulationInertiaStarting");
      }
    
      private void ThisScrollViewer_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
      {
       System.Diagnostics.Debug.WriteLine("ThisScrollViewer_ManipulationDelta");
      }
    
      private void ThisScrollViewer_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
      {
       System.Diagnostics.Debug.WriteLine("ThisScrollViewer_ManipulationCompleted");
      }
    
      private void ThisScrollViewer_PointerMoved(object sender, PointerRoutedEventArgs e)
      {
       System.Diagnostics.Debug.WriteLine("ThisScrollViewer_PointerMoved");
      }
    

    I only get 2 each of the PointerMoved events when I start swiping the listbox so that it scrolls up...the listbox does scroll up each time I swipe it, but I only get the first two PointerMoved events.  The ManipulationStarting events are useless in determining what the user is trying to do.  Here's the output from my code as I swipe up on the listbox.

    ThisListBox_PointerMoved
    ThisScrollViewer_PointerMoved
    ThisListBox_PointerMoved
    ThisScrollViewer_PointerMoved
    ThisListBox_ManipulationStarting
    ThisScrollViewer_ManipulationStarting
    [from here on I only get the ManipulationStarting events from both controls]

    The only time I get any other events is if I hold the pointer down on the listbox and slowly move it up...then I get a whole lot of PointerMoved events.


    Steven Edward Wood Computer Engineer

    Tuesday, January 8, 2019 1:43 AM

Answers

  • CancelDirectManipulations cancels the currently ongoing manipulation. It doesn't cancel future manipulations. You'll need to cancel it after detecting the start of the manipulation you want to override.

    Cancels ongoing direct manipulation processing (system-defined panning/zooming) on any ScrollViewer parent that contains the current UIElement.

    Saturday, January 12, 2019 1:34 AM
    Moderator

All replies

  • Update2: It turns out that the ListBox control has a bug or just undesirable behavior which prevents subsequent PointMoved events from firing no matter what container it is sitting in.  I have adjusted my workaround to eliminating using ListBox controls from my solutions until/if the behavior/bug is restored.  I'll go directly to using TextBlocks which I have tested and do fire all PointerMoved events as expected.

    Update1: I downloaded the sample linked to the following documentation https://docs.microsoft.com/en-us/windows/uwp/design/input/handle-pointer-input which wouldn't even build.  But, I created a new project and copied some of the xaml and code and got it running.  The sample also handles the PointerMoved event and to my surprise it gets triggered with each pointer activity. 

    The sample uses a Rectangle for the target control, so I changed it to a ListBox and sure enough the PointerMoved events only trigger with the first pointer activity and do not fire for any subsequent PointerMoved events.  So, it appears that there is a bug in the ListBox control...I'll submit a problem report.

    I'm going to try a workaround by placing the ListBox in a rectangle or other control which doesn't fail to fire PointerMoved events.


    Steven Edward Wood Computer Engineer


    Tuesday, January 8, 2019 4:59 PM
  • This is expected behavior. The ScrollViewer takes over the input when it starts scrolling. I'm on my phone so I can't provide links, but the keyword is DirectManipulation. I wrote a detailed entry on this in the 8.1 days on blogs.msnd.microsoft.com/b/wsdevsol which is still mostly relevant, but there are some new features in the UWP Scroll Viewer to help you interact better with Direct Manipulation.
    Also check out the microsoft-ui-xaml depot on github for some discussion of future features proposaled in this area.
    Tuesday, January 8, 2019 5:22 PM
    Moderator
  • I found your blog post: Where did all my gestures go?

    I added VerticalScrollMode="Disabled" to the ScrollViewer Xaml.  I also had to add VerticalScrollBarVisibility="Disabled" so that the non-enabled scrollbar wouldn't pop out.  Plus, I added ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Disabled" to the ListBox's Xaml definition.

    While this does work for Windows Desktop I'm still only getting the PointerEntered, ManipulationStarting, and PointerCaptureLost on the mobile emulator...gesturing still causes scrolling on the mobile emulator.  I don't have an actual Windows Phone in order to test if the problem is the emulator, but I have to make decisions based on the evidence that I have.

    So, since that doesn't work...how do I disable Direct Manipulation everywhere?  I've already added ManipulationMode="All" to all the Xaml including the page definition.  Is there some other way to disable Direct Manipulation everywhere?


    Steven Edward Wood Computer Engineer

    Friday, January 11, 2019 4:12 PM
  • UWP added new methods to manage DM on Scrollviewer and UIElement. Look for something like CancelDirectManipulation. Names are approximate since I'm on my phone, but they should be close enough for you to find the right ones.
    If the goal is just to detect scrolling then there are events specifically for that on the Scrollviewer.
    Friday, January 11, 2019 6:28 PM
    Moderator
  • Hi Steven,

    UIElement.CancelDirectManipulation will cancel DirectManpulation so you can do custom processing. After processing, the app can call UIElement.TryStartDirectManipulation to hand back to the DM engine.

    See the remarks in CancelDirectManipulation docs:

    You might call this method if you want the target UIElement to be able to process ongoing manipulations through the lower-level pointer events (PointerPressed, PointerMoved and so on). By default, if the target UIElement is contained in a ScrollViewer, that ScrollViewer parent would handle translation manipulations directly at the system level, treating them as pan or zoom. Manipulation handling by the ScrollViewer parent prevents the contained UIElement from receiving the pointer events (they would be marked as handled). Call CancelDirectManipulations to override this default behavior for an ongoing manipulation, and then you'll be able to handle manipulations at a non-system level for the individual UIElement target.

    And in the UIElement.TryStartDirectManipulation docs

    By default, touch input interactions in ScrollViewer elements are handled by the Direct Manipulation engine off the UI thread. An app cannot directly process the associated pointer events after Direct Manipulation processing starts. You can call CancelDirectManipulations at the start of a ScrollViewer interaction and handle the pointer events on the UI thread, which gives you the opportunity to do custom input handling in a ScrollViewer.

    If you cancel Direct Manipulation processing at the start of a ScrollViewer interaction, you can call TryStartDirectManipulation to resume having Direct Manipulation process the input stream. This lets you do custom input processing first, and then resume Direct Manipulation handling to make your app more responsive to touch interactions like scrolling and zooming.

    Only active touch contacts can be passed to Direct Manipulation. Using non-active or non-touch contacts causes an exception to be thrown.

    Specifying a touch contact to pass to Direct Manipulation results in the framework walking up the parent chain and setting the contact on the Direct Manipulation viewport of each ScrollViewer encountered in order, until the walk reaches any element (including the original target element) that does not have a ManipulationMode that contains ManipulationModes.System. A given touch contact can only be associated with a single chain of visuals at a time. Calling TryStartDirectManipulation more than once on the same contact results in any previous chain being released.

    Friday, January 11, 2019 10:38 PM
    Moderator
  • I added the following:

      private void Page_Loaded(object sender, RoutedEventArgs e)
      {
       this.thisListBox.CancelDirectManipulations();
       this.thisScrollViewer.CancelDirectManipulations();
       this.thisRelativePanel.CancelDirectManipulations();
       this.CancelDirectManipulations();
      }
    
    But, no change.  The mobile emulator still only raises the PointerEntered, ManipulationStarting, and PointerCaptureLost events.  And, the RoutedEventArgs for the PointerCaptureLost event is the same as the RoutedEventArgs for the PointerEntered event, so I can't even use those events as a rough estimate...I would have no clue what the user was trying to do.


    Steven Edward Wood Computer Engineer

    Saturday, January 12, 2019 12:33 AM
  • CancelDirectManipulations cancels the currently ongoing manipulation. It doesn't cancel future manipulations. You'll need to cancel it after detecting the start of the manipulation you want to override.

    Cancels ongoing direct manipulation processing (system-defined panning/zooming) on any ScrollViewer parent that contains the current UIElement.

    Saturday, January 12, 2019 1:34 AM
    Moderator
  • That was it!  Thanks.  I can now drop my efforts to use TextBoxes and TextBlocks and return to using a ListBox to display large file contents.

    Steven Edward Wood Computer Engineer

    Update: While I am now receiving the PointerMoved events from the ListBox the PointerPressed events are not...the PointerPressed events are instead being raised by the ListBox's TextBlocks but being blocked by the ListBox.  It would have been nice to know before I spent the day building prototypes trying to get the PointerPressed events to fire from the ListBox.
    Tuesday, January 15, 2019 4:31 PM