I have a WPF application that is capable of being used both with a mouse and using Touch. I disable all windows \"enhancements\" to just have touch events :
It is not completely clear to me if you actually want to avoid touch promotion to mouse or not, since you first ask about how to avoid moving the mouse cursor from touch, but then in a comment you explicitly say that you do want mouse promotion.
If you want to avoid mouse promotion, I posted an answer that works from Windows 8 and later here.
Just had the same problem and I found out here that one can use the PreviewXXX events to determine if the event was initiated by touch or by mouse input. Have a look at the following code:
private void UIElement_OnPreviewMouseMove(object sender, MouseEventArgs e)
{
if (e.StylusDevice != null)
{
AddInfoItem("Stylus or Touch recognized");
e.Handled = true;
return;
}
AddInfoItem("No Stylus or Touch recognized");
}
You can check the StylusDevice
property of the MouseEventArgs
to determine if a touch event was involved previously. If it is not null, you can set e.Handled
to true to prevent that the event that corresponds to the PreviewXXX event is being raised.
Hope that helps. A demo project for this can be downloaded from here (Dropbox link).
Something I used for an application once, was to simply setup a custom cursor whose image was simply a blank .CUR file.
As for the second problem, I would have indeed suggested moving the cursor to another location, but then I saw you'd done the same thing. If you application won't run in full screen, you can just define that safe position to be the lower-right corner of your application window.
Here is the best solution I found from now. Don't hesitate to post your own, especially if it is better.
Using SetWindowsHookEx low level mouse event catching (WH_MOUSE_LL
) and the fact that all events converted from Touch to Mouse are marked as such (The MOUSEEVENTF_FROMTOUCH
flag is set in the event's ExtraInfo, see Microsoft's FAQ) I was able to Globally remove all mouse events comming from the touch panel.
It isn't an ideal solution but it'll do for now in my application when it is running fullscreen (99% of the time as it is a dedicated hardware device).
The second step also good only in fullscreen (That i won't provide code for as it is pretty simple) is just moving the mouse to "safe" position like the bottom right of the screen with SetCursorPos.
If you need the code it is in a Gist on Github and i'll post the current version at the end of this article. To use it :
// As long as the instance is alive the conversion won't occur
var disableTouchMouse = new DisableTouchConversionToMouse();
// To let the conversion happen again, Dispose the class.
disableTouchMouse.Dispose();
Full source code of the class :
namespace BlackFox
{
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security;
/// <summary>
/// As long as this object exists all mouse events created from a touch event for legacy support will be disabled.
/// </summary>
class DisableTouchConversionToMouse : IDisposable
{
static readonly LowLevelMouseProc hookCallback = HookCallback;
static IntPtr hookId = IntPtr.Zero;
public DisableTouchConversionToMouse()
{
hookId = SetHook(hookCallback);
}
static IntPtr SetHook(LowLevelMouseProc proc)
{
var moduleHandle = UnsafeNativeMethods.GetModuleHandle(null);
var setHookResult = UnsafeNativeMethods.SetWindowsHookEx(WH_MOUSE_LL, proc, moduleHandle, 0);
if (setHookResult == IntPtr.Zero)
{
throw new Win32Exception();
}
return setHookResult;
}
delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
var info = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
var extraInfo = (uint)info.dwExtraInfo.ToInt32();
if ((extraInfo & MOUSEEVENTF_MASK) == MOUSEEVENTF_FROMTOUCH)
{
if((extraInfo & 0x80) != 0)
{
//Touch Input
return new IntPtr(1);
}
else
{
//Pen Input
return new IntPtr(1);
}
}
}
return UnsafeNativeMethods.CallNextHookEx(hookId, nCode, wParam, lParam);
}
bool disposed;
public void Dispose()
{
if (disposed) return;
UnsafeNativeMethods.UnhookWindowsHookEx(hookId);
disposed = true;
GC.SuppressFinalize(this);
}
~DisableTouchConversionToMouse()
{
Dispose();
}
#region Interop
// ReSharper disable InconsistentNaming
// ReSharper disable MemberCanBePrivate.Local
// ReSharper disable FieldCanBeMadeReadOnly.Local
const uint MOUSEEVENTF_MASK = 0xFFFFFF00;
const uint MOUSEEVENTF_FROMTOUCH = 0xFF515700;
const int WH_MOUSE_LL = 14;
[StructLayout(LayoutKind.Sequential)]
struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
struct MSLLHOOKSTRUCT
{
public POINT pt;
public uint mouseData;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}
[SuppressUnmanagedCodeSecurity]
static class UnsafeNativeMethods
{
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod,
uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
}
// ReSharper restore InconsistentNaming
// ReSharper restore FieldCanBeMadeReadOnly.Local
// ReSharper restore MemberCanBePrivate.Local
#endregion
}
}
edit: From the comments section of Troubleshooting Applications and System Events and Mouse Messages additional information to disambiguate pen from touch.