e.IsInertial almost always false WPF
Asked Answered
Z

1

8

I have the following XAML for my WPF app, which I am converting from UWP:

<ScrollViewer Name="scv_main" Grid.Row="2" ScrollViewer.VerticalScrollBarVisibility="Auto" 
PanningMode="VerticalFirst">
    <TextBlock Name="txbl_display" Grid.Row="2"  Foreground="White"
     FontFamily="Lucida Console" FontSize="22" Text="{Binding Path=ContentPath, Mode=OneWay, 
     UpdateSourceTrigger=PropertyChanged}" Padding="20" TextWrapping="Wrap" 
     IsManipulationEnabled="True" ManipulationDelta="manipulationDeltaHandler"/>
</ScrollViewer>

What I'm finding is, the event handler's e.IsInertial is always false.

I thought it might be the ScrollViewer handling the inertial events, but when I remove the ScrollViewer I still can't get any Inertial events to come through. I have also tried putting the ManipulationDelta on the ScrollViewer, with the same result.

My ultimate aim is that I want the ScrollViewer to scroll on a non-inertial move, but let me control what happens on an inertial swipe.

I managed this effect in the UWP app which I am porting to WPF, as follows:

<TextBlock ... ManipulationMode="All" ManipulationDelta="TextBlock_ManipulationDelta/>"

and then in the code-behind:

if (e.IsInertial) // inertial = swipe, rather than slide
{
    // X-Translation - change contents of textblock
    if (e.Cumulative.Translation.X <= -500) //500 is the threshold value, where you want to trigger the swipe right event
    {
        showNext();
        e.Complete();
    }
    else if (e.Cumulative.Translation.X >= 500)
    {
        showPrevious();
        e.Complete();
    }

    // Y translation - move the scrollbar
    else if (e.Cumulative.Translation.Y <= -500)
    {
        scv_main.ChangeView(null, scv_main.VerticalOffset + (-1 * e.Cumulative.Translation.Y), null);
        e.Complete();
    }
    else if (e.Cumulative.Translation.Y >= 500)
    {
        scv_main.ChangeView(null, Math.Min(scv_main.VerticalOffset + (-1 * e.Cumulative.Translation.Y), 0), null);
        e.Complete();
    }
}
else // slide,rather than swipe - scroll as the finger moves
{
    if (e.Delta.Translation.Y != 0)
    {
        scv_main.ChangeView(null, scv_main.VerticalOffset + (-1 * e.Delta.Translation.Y), null);
    }
}

But I can't repeat this behaviour in WPF. Any thoughts?

---UPDATE---

I have since found that, by swiping in a large arc from top-right to bottom-left, with the screen full-screened, I can just about trigger an e.IsInertial = True event. When this happens, the ManipulationDelta event is triggered twice - the first time with e.IsInertial = false and the second with e.IsInertial = true. I'm not sure why this happens; and anyway this isn't the behaviour I'm looking for.

Anyone got any more thoughts? I have tried various things, including putting a panel ontop of everything and putting the manipulation handlers on that; but I was still having the same issues.

I am using Visual Studio 2017 and Windows 10. The device I am coding and testing on is a Microsoft Surface Book


What seems to be happening is, the non-inertial move is happening first; then once that's finished the inertial is firing. I put some outputs in the events, and got the following when I swiped:

ManipulationStarting Fired
ManipulationDelta e.IsInertial = False. X,Y:-5.14288330078125,-1.14288330078125
ManipulationDelta e.IsInertial = False. X,Y:-16.5714111328125,0
ManipulationDelta e.IsInertial = False. X,Y:-16.5714111328125,0
ManipulationDelta e.IsInertial = False. X,Y:-89.1428833007813,1.14288330078125
ManipulationDelta e.IsInertial = False. X,Y:-224,2.28570556640625
ManipulationDelta e.IsInertial = False. X,Y:-224,2.28570556640625
ManipulationDelta e.IsInertial = False. X,Y:-384.571441650391,4
ManipulationDelta e.IsInertial = False. X,Y:-622.285705566406,4
ManipulationDelta e.IsInertial = False. X,Y:-622.285705566406,4
ManipulationDelta e.IsInertial = False. X,Y:-622.285705566406,4
ManipulationDelta e.IsInertial = False. X,Y:-732.571411132813,6.28570556640625
ManipulationDelta e.IsInertial = False. X,Y:-732.571411132813,6.28570556640625
ManipulationDelta e.IsInertial = False. X,Y:-732.571411132813,6.28570556640625
ManipulationDelta e.IsInertial = False. X,Y:-732.571411132813,6.28570556640625
ManipulationInertiaStarting Fired

After that, I don't get any more deltas firing. So what's consuming them; and why am I getting non-inertial ones first?

One way I've found to get this kind-of working is to change PanningMode="VerticalFirst" to PanningMode="None". then handle the scrolling myself (which seems to be what the UWP version was doing anyway)... but I still have to get rid of the "e.IsInertial" check. So something is causing inertial deltas to be consumed before my event handler gets them

Zo answered 22/4, 2018 at 18:45 Comment(11)
Have you tried to look what is goint on in ManipulationStarting event?Marquise
Yeah, I'll check when I get home on Tuesday but I think manipulation starting gets called and e. Isinertial is false there tooZo
@AvdotiyFedorov What specifically do you recommend I look at in ManipulationStarting?Zo
ManipulationStarting fires regularly; but ManipulationInertiaStarting only fires if I do a really massive swipe in an arc (e.g. top-right to bottom-left of screen) - in which case actually e.IsInertial is True in the ManipulationDelta event handler too - but this is rare and not at all the same behaviour as the UWP app, which seems to have a much better threshold for what is inertialZo
I have put a breakpoint in ManipulationDelta... For the scenario above, where ManipulationInertiaStarting fires, the ManipulationDelta gets called twice - once with e.IsInertial = False, followed by e.IsInertial = True. Something weird is going on hereZo
superuser.com/questions/524862/…Metallo
@HansPassant altering that parameter didn't make any differenceZo
it is not obvious to me why the snippet calls e.Complete() only when e.IsInertia == true. With the numbers you listed in your edit, it will be instantly over since -732.571411132813 < -500. Realistically you would want it to always complete as soon as it gets less than -500. Basic problem seems to be a misunderstanding what IsInertia really means. All it indicates is that the user stopped moving his finger and the remaining motion is now auto-generated and decelerated by the "friction". So it is always false at first, user moving his finger, and gets true after he stopped.Metallo
@HansPassant Interesting, OK. What you describe is the behaviour I want - I want to be able to know when the user finished swiping (lifted their finger); and if they moved with a sufficient velocity during the swipe then change the view. So I want to say "if is inertial (if they've lifted their finger) and the velocity is high enough; then change view and don't check any more (otherwise we'll change views lots of times)" - but I'm only seeing the values for the main swipe, not the inertial after-effects. Does that make sense now?Zo
That wasn't very obvious. Consider the StylusSystemGesture event to detect a SystemGesture.Flick. No direction, so you still need to monitor which way it is going.Metallo
...I'm not sure I understand your point. we do have direction here - we know the X and YZo
U
1

To make sure that the manipulation flow works as intended you want to handle more of the manipulation events and react accordingly. Here's the overview from MS (Input overview - Touch and manipulations) but in general, you usually want to:

  • handle ManipulationStarting and set the proper manipulation container for your manipulation.
  • handle ManipulationStarted if you want to store some data like the origin of the manipulation for your own calculations.
  • handle Delta while the user has the finger(s) down
  • handle InertiaStarting - here you should set the deceleration values.
  • handle Delta while the user has lifted his fingers (Inertial) - from my experience you'll get these events only if you specify inertial values.
  • handle ManipulationCompleted if you need to know when and where the input process has finished.

From your post I'm not sure whether you know this or not but the InertiaStarting will only happen once the user has lifted his finger(s). And then - if you set the values when handling InertiaStarting properly - you will get a few more ManipulationDeltas incoming (with the Inertial flag).

After that, I don't get any more deltas firing. So what's consuming them; and why am I getting non-inertial ones first?

You have completed the manipulation process by calling complete as the if check below has been fulfilled! So the 'engine' has skipped all the rest of the events it would otherwise fire and completed the manipulation process:

if (e.Cumulative.Translation.X <= -500)
{
    showNext();
    e.Complete();
}

Once the manipulation has been completed you won't get any more events of course.

Unsophisticated answered 2/8, 2019 at 8:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.