WPF Multitouch - How to get all touchpoints? - c#

We are evaluating touchscreen keyboards and for one we need to track 10 fingers at the same time. The problem is that the touchscreen driver is very wonky (and there is no fixed version). It sends out 2500+ events for the FrameReported event each second for so many fingers. There is just no way to handle all those, even if we discard 90% at the beginning. It's simply impossible to keep up and do anything meaningful with the data.
Instead of System.Windows.Input.Touch.FrameReported, I also tried to use the (Preview) TouchMove events of the window; Same problem here.
So now I wanted, instead of using events, to poll in a separate Thread, but I cannot find information on how to get all the current touchpoints.
The only thing I found is a WinForms hack, but that isn't an option, since then I will be unable to render any WPF controls in my window.
Any solutions?
Edit 1:
This is the code, that handles all the move events:
private void UserControlTouchMove(object sender, TouchEventArgs e)
{
//Update Position of the corresponding point
var touch = e.GetTouchPoint(this);
var id = touch.TouchDevice.Id;
e.Handled = true;
var position = touch.Position;
//update finger on display, quick and dirty
if (m_ShowFingers)
{
foreach (var finger in m_Fingers)
{
if (id == (int)finger.DataContext)
{
finger.RenderTransform = new TranslateTransform(position.X - HalfFingerSize, position.Y - HalfFingerSize);
break;
}
}
}
}

Related

How to route touch events to separate controls UWP C#

I am facing a major problem for custom touch event handling.
The goal is to route the touch event to different controls, depending on how many fingers are used.
For example:
We have a scrollview with many webviews arranged in it.
The zoomfactor is set to present one webview to fullscreen.
Currently I am using the pointer pressed / released functions like this, to configure the controls, so the event will be catched by the right one:
int pointerCount = 0;
private void ScrollView_PointerPressed(object sender, PointerRoutedEventArgs e)
{
try
{
pointerCount++;
if (pointerCount > 3)
pointerCount = 0;
switch (pointerCount)
{
case 1:
// I don't do anything, so it goes to the webview anyway
break;
case 2:
EnableScrolling();
break;
case 3:
ZoomInOut();
break;
default:
return;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
private void EnableScrolling()
{
ScrollViewer.ZoomMode = ZoomMode.Enabled;
ScrollViewer.VerticalScrollMode = ScrollMode.Enabled;
ScrollViewer.HorizontalScrollMode = ScrollMode.Enabled;
}
1-finger events should go to the webview // this works
2-finger events should go to the ScrollView // this is not working, because the webview grabs the touch event and will not release it again
3-finger events should zoom out // this works
The PointerPressed is always called, but the PointerReleased is not called when the PointerPressed was on the webview.
This also results in the effect of not decreasing the pointerCount, so it is possible to do a 1-finger tap 3 times and it results in the 3-finger event and zooms out, that should not happen.
Hopefully you can see the problem and help to resolve it.
If you think this is a way too wrong approach, feel free to show an alternative that works out better.
Well, I couldn't find a proper solution, but I was able to remove the unwanted side effect with upcounting the pointers event if they were released.
private void ScrollViewer_PointerReleased(object sender, PointerRoutedEventArgs e)
{
pointerCount = 0;
}
So the right direction handling is working fine now, but I still can't do something like:
currentWebView.CapturePointer(e.Pointer);
Because it won't root the pointerEvent into it's content, it will call the pointer events of the WebView, but it won't root it into it's html & js content.
I also tried to use
.... wb.ManipulationStarted += Wb_ManipulationStarted; ....
private void Wb_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
{
// something here
}
but this also will not arrive in the content.
Any ideas for another approach?
I figured out how to handle that, and it is so simple that I could cry...
I just added this line of code to tell the webviews js that there was a event...
await webView.InvokeScriptAsync("eval", new string[] { "document.elementFromPoint(" + pointX + ", " + pointY + ").click();" });
And the webview handles the rest of it. So there is no custom invoke as I was worried about, it's just the standard function invoked, that is always invoked when a touch/click is performed.

How can I detect when scrolling has started in a ListView on Windows 10 UWP?

I'd like to subscribe to an event which tells me that scrolling has started in a ListView and get the direction of scrolling.
Is there any way to do this in Windows 10 UWP API?
Thanks
You should first obtain the ScrollViewer inside the ListView and then subscribe to its DirectManipulationStarted event.
However, to get the direction of the scrolling can be tricky. I'd suggest you to have a look at the new Windows Composition API where there's a way to use ExpressionAnimation to link the ScrollViewer's translation to a value of your choice.
A good start is to have a look at this demo from James Clarke.
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
CompositionPropertySet scrollerViewerManipulation = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(myScroller);
Compositor compositor = scrollerViewerManipulation.Compositor;
ExpressionAnimation expression = compositor.CreateExpressionAnimation("ScrollManipululation.Translation.Y * ParallaxMultiplier");
expression.SetScalarParameter("ParallaxMultiplier", 0.3f);
expression.SetReferenceParameter("ScrollManipululation", scrollerViewerManipulation);
Visual textVisual = ElementCompositionPreview.GetElementVisual(background);
textVisual.StartAnimation("Offset.Y", expression);
}
Update
Actually just thought of an easier way to detect the scrolling direction. Just subscribe whenever the VerticalOffset is changed and compare it to its previous values.
private double _previousOffset;
sv.RegisterPropertyChangedCallback(ScrollViewer.VerticalOffsetProperty, (s, dp) =>
{
if (Math.Abs(sv.VerticalOffset - _previousOffset ) < 3)
{
// ignore when offset difference is too small
}
else if (sv.VerticalOffset > _previousOffset)
{
Debug.WriteLine($"up {sv.VerticalOffset - _previousOffset}");
}
else {
Debug.WriteLine($"down {sv.VerticalOffset - _previousOffset}");
}
_previousOffset = sv.VerticalOffset;
});

How to Disable Multitouch in windows phone?

I had used mouse events, using TouchFrameReported, I wanted it to be single touch, but it is supporting multitouch, how to disable multitouch, in touch frame reported, or is there any idea to implement so that multitouch is not supported..
void Touch_FrameReported(object sender, TouchFrameEventArgs e)
{
foreach (TouchPoint touchPoint in e.GetTouchPoints(this.mainGrid))
{
if (touchPoint.Action == TouchAction.Down)
{
currentPoint.X = touchPoint.Position.X;
currentPoint.Y = touchPoint.Position.Y;
glowDot();
}
else if (touchPoint.Action == TouchAction.Up)
{
circPathGlow.Visibility = Visibility.Collapsed;
}
else if (touchPoint.Action == TouchAction.Move)
{
}
}
}
You can find more information on:
http://social.msdn.microsoft.com/Forums/windowsapps/en-US/123e9381-fc0b-441e-a738-dcd35f526a6e/how-to-disable-multitouch
I wouldn't try to fiddle with the touch messages here. If the goal is
to limit the dragging to one control at a time then limit it to the
controls. Once one is moving, don't move the others.
At the pointer message level you can track the PointerId in
PointerPressed and ignore other PointerIds until you get a
PointerReleased or PointerCaptureLost:
Question: Do you want to disable certain multi-gestures or all?
After reading http://www.wintellect.com/blogs/jprosise/building-touch-interfaces-for-windows-phones-part-2
I came to know that I was using e.GetTouchPoints
Instead of e.GetPrimaryTouchPoint,,
Now, I use e.GetPrimaryTouchPoint, which capture only the first touch points that is being touched,,
TouchPoint touchPoint = e.GetPrimaryTouchPoint(this.mainGrid);
and rest of the code,, this solve my problem.

Touch DragDrop - Blocks all inputchanges

I'm trying to handle a touch drag and drop operation with DragDrop.DoDragDrop. As I read in this post (I, too, can't use the surface drag framework for this) I also have to implement a QueryContinueDragHandler, which I have with the following implementation:
(in the mainwindow constructor)
this.QueryContinueDrag += (obj, e) =>
{
if(_touchDevice.IsActive)
e.Action = DragAction.Continue;
else
e.Action = DragAction.Drop;
};
(on some TouchDown Eventhandler)
Object data = new Object();
_touchDevice = e.TouchDevice;
DragDrop.DoDragDrop(this, data, DragDropEffects.Link);
However, with this the drag action never ends, as _touchDevice.IsActive will always be true, no matter if the TouchDevice is actually still "touching".
This question is also related to mine, but I find the answer unsatisfactory and I didn't want to turn the thread into a zombie.
How can I detect when/if the TouchDevice will be inactive?

Is there anyway to know when the screen has been updated/refreshed (OpenGL or DirectX maybe?)

I currently have an application I'm writing in c# (using .NET) that requires me to start a timer as soon as a user sees an image on screen up until they respond with a key press.
Now I realise that practically this is very difficult given the monitor input lag and response time, the time the keyboard takes to physically send the message, the OS to process it, etc.
But I'm trying my best to reduce it down to mostly a constant error (the response time results will be used to compare one user to the next so a constant error isn't really an issue). However annoying hurdle is the variable caused by the monitor refresh rate, as I gather when my onPaint message is called and done with, it doesn't mean the image has actually been processed and sent from the graphics buffer?
Unfortunately time restrictions and other commitments would realistically restrict me to continuing this task in c# for windows.
So what I was wondering was if either handling all the drawing in OpenGL or DirectX or better still for me if it is possible to just using either OpenGL or DirectX to create an event when the screen is updated?
Another suggestion given to me previously was regarding V-Sync, if I switch this off is the image sent as soon as it is drawn? as opposed to sending images at a set rate synchronised to the monitor refresh rate?
You must render your graphic in a separate thread in order to:
Use vertical synchronisation to have a precise timing of the effective display of your image.
Get the precise timing of your user input (since user interface is not on the same thread than the render loop.
Initialise Direct3D to enable the VSync during render :
// DirectX example
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.BackBufferCount = 1;
presentParams.PresentationInterval = PresentInterval.One;
device = new Device(...
Perform the render in a separate thread:
Thread renderThread = new Thread(RenderLoop);
renderThread.Start();
shouldDisplayImageEvent = new AutoResetEvent();
Then use the following render loop:
void RenderLoop()
{
while(applicationActive)
{
device.BeginScene();
// Other rendering task
if (shouldDisplayImageEvent.WaitOne(0))
{
// Render image
// ...
userResponseStopwatch = new Stopwatch();
userResponseStopwatch.Start();
}
device.EndScene();
device.Present();
}
}
Then handle the user input :
void OnUserInput(object sender, EventArgs e)
{
if (userResponseStopwatch != null)
{
userResponseStopwatch.Stop();
float userResponseDuration = userResponseStopwatch.ElapsedMillisecond - 1000 / device.DisplayMode.RefreshRate - displayDeviceDelayConstant;
userResponseStopwatch = null;
}
}
You now use the shouldDisplayImageEvent.Set() event trigger to display the image as needed and start the stop watch.
First enable the VSync on your application idle loop :
// DirectX example
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.BackBufferCount = 1;
presentParams.PresentationInterval = PresentInterval.One;
device = new Device(...
Application.Idle += new EventHandler(OnApplicationIdle);
// More on this here : http://blogs.msdn.com/tmiller/archive/2005/05/05/415008.aspx
internal void OnApplicationIdle(object sender, EventArgs e)
{
Msg msg = new Msg();
while (true)
{
if (PeekMessage(out msg, IntPtr.Zero, 0, 0, 0))
break;
}
// Clearing render
// ...
if (displayImage)
{
// Render image
// ...
renderTime = DateTime.now();
}
device.Present();
}
With the vsync enabled, the device.Present function block until the next frame synchronisation, so if you compute the time between renderTime and the user input time and remove the display device delay + 16.67ms you should get your user response delay.

Categories

Resources