触摸 WPF 按钮有时不会调用 Windows 下的单击处理程序 8
Touching a WPF button does sometimes not invoke the click handler under Windows 8
"When the application window does NOT have the focus, sometimes the click handler is not called on button-touch."
系统:
Windows 8.1 多点触控输入设备。
软件:
一个简单的 WPF 4.5.2 应用程序,只有一个按钮,如果调用了点击处理程序,它会记录下来(只需创建一个新项目并向 MainWindow 添加一个带有点击处理程序的按钮)。
现象:
通常在触摸按钮时会调用其点击处理程序。 但是当应用程序 window 没有焦点时,有时在按钮触摸时不会调用点击处理程序(尽管按钮颜色变为蓝色)。当应用程序 window 已经获得焦点时,触摸按钮一直有效。
这种情况只是有时会发生,但会发生在具有不同触摸硬件的各种系统上(所有 windows 8.1)。我从未在 windows 7 上体验过它。
无论 window 是否聚焦,鼠标都可以使用。
(在发布和调试模式下发生,有和没有附加调试器)
测试:
我检查了 windows 中的各种触摸设置并使用了它。我也重新校准了触摸 -> 没有区别。
检查 WPF 按钮鼠标和触摸事件时,在错误情况下,最后收到的事件是 PreviewMouseUp 但没有 Click!
当我查看收到的 window 消息(spy++)时,我得到以下信息(WM_IME_SETCONTEXT、WM_GETTEXT、WM_NCHITTEST、WM_GETOBJECT 已删除更好的概述):
收到点击:
R WM_POINTERACTIVATE pmsd->lResult:FFFFFFFFFFFFFFFF
S WM_MOUSEACTIVATE hwndTopLevel:000503F8 nHittest:HTCLIENT uMsg:WM_POINTERDOWN
R WM_MOUSEACTIVATE fuActivate:MA_ACTIVATE
S WM_WINDOWPOSCHANGING lpwp:000000E9E3D9E410
R WM_WINDOWPOSCHANGING
S WM_WINDOWPOSCHANGED lpwp:000000E9E3D9E410
R WM_WINDOWPOSCHANGED
S WM_ACTIVATEAPP fActive:True dwThreadID:00000000
R WM_ACTIVATEAPP
S WM_NCACTIVATE fActive:True
R WM_NCACTIVATE
S WM_ACTIVATE fActive:WA_ACTIVE fMinimized:False hwndPrevious:(null)
S WM_IME_NOTIFY dwCommand:IMN_OPENSTATUSWINDOW dwCommand:00000002 dwData:00000000
R WM_IME_NOTIFY
S WM_SETFOCUS hwndLoseFocus:(null)
R WM_SETFOCUS
R WM_ACTIVATE
S message:0x02CC [Unknown] wParam:00000000 lParam:00C50BA8
R message:0x02CC [Unknown] lResult:00000100
P WM_POINTERENTER wPointerID:0099 wFlags:6017
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_LBUTTONDOWN
R WM_SETCURSOR fHaltProcessing:False
P WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPos:145 yPos:62
R WM_POINTERCAPTURECHANGED
P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:145 yPos:62
P WM_POINTERLEAVE wPointerID:0099 wFlags:6000
P WM_LBUTTONUP fwKeys:0000 xPos:145 yPos:62
P WM_MOUSEMOVE fwKeys:0000 xPos:145 yPos:62
S WM_CAPTURECHANGED hwndNewCapture:000503F8
R WM_CAPTURECHANGED
S WM_CAPTURECHANGED hwndNewCapture:00000000
R WM_CAPTURECHANGED
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
R WM_SETCURSOR fHaltProcessing:False
P WM_MOUSEMOVE fwKeys:0000 xPos:145 yPos:62
点击未收到
S message:0x02CC [Unknown] wParam:00000000 lParam:00AE0BA9
R message:0x02CC [Unknown] lResult:00000100
R WM_POINTERACTIVATE pmsd->lResult:FFFFFFFFFFFFFFFF
S WM_MOUSEACTIVATE hwndTopLevel:000503F8 nHittest:HTCLIENT uMsg:WM_POINTERDOWN
R WM_MOUSEACTIVATE fuActivate:MA_ACTIVATE
S WM_WINDOWPOSCHANGING lpwp:000000E9E3D9E410
R WM_WINDOWPOSCHANGING
S WM_WINDOWPOSCHANGED lpwp:000000E9E3D9E410
R WM_WINDOWPOSCHANGED
S WM_ACTIVATEAPP fActive:True dwThreadID:00000000
R WM_ACTIVATEAPP
S WM_NCACTIVATE fActive:True
R WM_NCACTIVATE
S WM_ACTIVATE fActive:WA_ACTIVE fMinimized:False hwndPrevious:(null)
S WM_IME_NOTIFY dwCommand:IMN_OPENSTATUSWINDOW dwCommand:00000002 dwData:00000000
R WM_IME_NOTIFY
S WM_SETFOCUS hwndLoseFocus:(null)
R WM_SETFOCUS
R WM_ACTIVATE
P WM_POINTERENTER wPointerID:0090 wFlags:6017
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_LBUTTONDOWN
R WM_SETCURSOR fHaltProcessing:False
P WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPos:147 yPos:39
R WM_POINTERCAPTURECHANGED
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
R WM_SETCURSOR fHaltProcessing:False
P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:147 yPos:39
P WM_POINTERLEAVE wPointerID:0090 wFlags:6000
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_LBUTTONUP
R WM_SETCURSOR fHaltProcessing:False
P WM_LBUTTONUP fwKeys:0000 xPos:147 yPos:39
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
R WM_SETCURSOR fHaltProcessing:False
P WM_MOUSEMOVE fwKeys:0000 xPos:147 yPos:39
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
R WM_SETCURSOR fHaltProcessing:False
P WM_MOUSEMOVE fwKeys:0000 xPos:147 yPos:39
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
R WM_SETCURSOR fHaltProcessing:False
P WM_MOUSEMOVE fwKeys:0000 xPos:147 yPos:39
错误中有额外的行
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_LBUTTONUP
R WM_SETCURSOR fHaltProcessing:False
在 WM_LBUTTONUP 之前。但我不知道这是否重要。
我已经尝试调试 .net 代码以查看导致不调用 Click 处理程序的条件,但我很快就迷失在 System.Windows.Input.InputManager 和 System.Windows.Input.StylusLogic 的 PromoteMainToMouse() 函数中。
我真的运行不知道要检查什么。
因此,如果有人可以给我建议还需要测试什么或在 .net 中的何处设置断点以找到根本原因,那就太好了。或者更好地找到 solution/workaround.
更新:
我发现如果将鼠标指针移离应用程序 window(至少在我们的平台上),我也可以重现该现象。并且错误发生的可能性似乎随着用户界面的复杂性而增加——我的测试应用程序并不总是无法识别点击,但一个复杂的 WPF 应用程序每次都会失败。
您应该能够在 Windows 8.1 上使用 WPF 开发人员工作室(例如 2013 版)通过触摸设备重现它:
- 让开发者工作室打开,只覆盖一半触摸屏
- 移开移动使光标指向桌面区域
- 触摸按钮(例如“开始调试”——播放按钮):在我的例子中,第一次触摸永远不会导致点击事件。
原来是WPF中的BUG。在发布 KB 之前,有一个解决方法可用。
解决方法是在 MainWindow 受影响的 UIElement 的 PreviewTouchDown
事件处理程序中调用 Mouse.Synchronize();
。
我测试了它,它工作正常。
我遇到了同样的问题,发现禁用 RealTimeStylus(有效地禁用触摸支持)对我有用,导致鼠标事件总是在触摸时引发。
我们在 Windows10.
上使用支持多点触控的工业触摸屏
public static void DisableWPFTabletSupport()
{
// Get a collection of the tablet devices for this window.
TabletDeviceCollection devices = System.Windows.Input.Tablet.TabletDevices;
if (devices.Count > 0)
{
// Get the Type of InputManager.
Type inputManagerType = typeof(System.Windows.Input.InputManager);
// Call the StylusLogic method on the InputManager.Current instance.
object stylusLogic = inputManagerType.InvokeMember("StylusLogic",
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, InputManager.Current, null);
if (stylusLogic != null)
{
// Get the type of the stylusLogic returned from the call to StylusLogic.
Type stylusLogicType = stylusLogic.GetType();
// Loop until there are no more devices to remove.
while (devices.Count > 0)
{
// Remove the first tablet device in the devices collection.
stylusLogicType.InvokeMember("OnTabletRemoved",
BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic,
null, stylusLogic, new object[] { (uint)0 });
}
}
}
}
"When the application window does NOT have the focus, sometimes the click handler is not called on button-touch."
系统:
Windows 8.1 多点触控输入设备。
软件:
一个简单的 WPF 4.5.2 应用程序,只有一个按钮,如果调用了点击处理程序,它会记录下来(只需创建一个新项目并向 MainWindow 添加一个带有点击处理程序的按钮)。
现象:
通常在触摸按钮时会调用其点击处理程序。 但是当应用程序 window 没有焦点时,有时在按钮触摸时不会调用点击处理程序(尽管按钮颜色变为蓝色)。当应用程序 window 已经获得焦点时,触摸按钮一直有效。 这种情况只是有时会发生,但会发生在具有不同触摸硬件的各种系统上(所有 windows 8.1)。我从未在 windows 7 上体验过它。 无论 window 是否聚焦,鼠标都可以使用。
(在发布和调试模式下发生,有和没有附加调试器)
测试:
我检查了 windows 中的各种触摸设置并使用了它。我也重新校准了触摸 -> 没有区别。
检查 WPF 按钮鼠标和触摸事件时,在错误情况下,最后收到的事件是 PreviewMouseUp 但没有 Click!
当我查看收到的 window 消息(spy++)时,我得到以下信息(WM_IME_SETCONTEXT、WM_GETTEXT、WM_NCHITTEST、WM_GETOBJECT 已删除更好的概述):
收到点击:
R WM_POINTERACTIVATE pmsd->lResult:FFFFFFFFFFFFFFFF
S WM_MOUSEACTIVATE hwndTopLevel:000503F8 nHittest:HTCLIENT uMsg:WM_POINTERDOWN
R WM_MOUSEACTIVATE fuActivate:MA_ACTIVATE
S WM_WINDOWPOSCHANGING lpwp:000000E9E3D9E410
R WM_WINDOWPOSCHANGING
S WM_WINDOWPOSCHANGED lpwp:000000E9E3D9E410
R WM_WINDOWPOSCHANGED
S WM_ACTIVATEAPP fActive:True dwThreadID:00000000
R WM_ACTIVATEAPP
S WM_NCACTIVATE fActive:True
R WM_NCACTIVATE
S WM_ACTIVATE fActive:WA_ACTIVE fMinimized:False hwndPrevious:(null)
S WM_IME_NOTIFY dwCommand:IMN_OPENSTATUSWINDOW dwCommand:00000002 dwData:00000000
R WM_IME_NOTIFY
S WM_SETFOCUS hwndLoseFocus:(null)
R WM_SETFOCUS
R WM_ACTIVATE
S message:0x02CC [Unknown] wParam:00000000 lParam:00C50BA8
R message:0x02CC [Unknown] lResult:00000100
P WM_POINTERENTER wPointerID:0099 wFlags:6017
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_LBUTTONDOWN
R WM_SETCURSOR fHaltProcessing:False
P WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPos:145 yPos:62
R WM_POINTERCAPTURECHANGED
P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:145 yPos:62
P WM_POINTERLEAVE wPointerID:0099 wFlags:6000
P WM_LBUTTONUP fwKeys:0000 xPos:145 yPos:62
P WM_MOUSEMOVE fwKeys:0000 xPos:145 yPos:62
S WM_CAPTURECHANGED hwndNewCapture:000503F8
R WM_CAPTURECHANGED
S WM_CAPTURECHANGED hwndNewCapture:00000000
R WM_CAPTURECHANGED
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
R WM_SETCURSOR fHaltProcessing:False
P WM_MOUSEMOVE fwKeys:0000 xPos:145 yPos:62
点击未收到
S message:0x02CC [Unknown] wParam:00000000 lParam:00AE0BA9
R message:0x02CC [Unknown] lResult:00000100
R WM_POINTERACTIVATE pmsd->lResult:FFFFFFFFFFFFFFFF
S WM_MOUSEACTIVATE hwndTopLevel:000503F8 nHittest:HTCLIENT uMsg:WM_POINTERDOWN
R WM_MOUSEACTIVATE fuActivate:MA_ACTIVATE
S WM_WINDOWPOSCHANGING lpwp:000000E9E3D9E410
R WM_WINDOWPOSCHANGING
S WM_WINDOWPOSCHANGED lpwp:000000E9E3D9E410
R WM_WINDOWPOSCHANGED
S WM_ACTIVATEAPP fActive:True dwThreadID:00000000
R WM_ACTIVATEAPP
S WM_NCACTIVATE fActive:True
R WM_NCACTIVATE
S WM_ACTIVATE fActive:WA_ACTIVE fMinimized:False hwndPrevious:(null)
S WM_IME_NOTIFY dwCommand:IMN_OPENSTATUSWINDOW dwCommand:00000002 dwData:00000000
R WM_IME_NOTIFY
S WM_SETFOCUS hwndLoseFocus:(null)
R WM_SETFOCUS
R WM_ACTIVATE
P WM_POINTERENTER wPointerID:0090 wFlags:6017
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_LBUTTONDOWN
R WM_SETCURSOR fHaltProcessing:False
P WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPos:147 yPos:39
R WM_POINTERCAPTURECHANGED
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
R WM_SETCURSOR fHaltProcessing:False
P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:147 yPos:39
P WM_POINTERLEAVE wPointerID:0090 wFlags:6000
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_LBUTTONUP
R WM_SETCURSOR fHaltProcessing:False
P WM_LBUTTONUP fwKeys:0000 xPos:147 yPos:39
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
R WM_SETCURSOR fHaltProcessing:False
P WM_MOUSEMOVE fwKeys:0000 xPos:147 yPos:39
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
R WM_SETCURSOR fHaltProcessing:False
P WM_MOUSEMOVE fwKeys:0000 xPos:147 yPos:39
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_MOUSEMOVE
R WM_SETCURSOR fHaltProcessing:False
P WM_MOUSEMOVE fwKeys:0000 xPos:147 yPos:39
错误中有额外的行
S WM_SETCURSOR hwnd:000503F8 nHittest:HTCLIENT wMouseMsg:WM_LBUTTONUP
R WM_SETCURSOR fHaltProcessing:False
在 WM_LBUTTONUP 之前。但我不知道这是否重要。
我已经尝试调试 .net 代码以查看导致不调用 Click 处理程序的条件,但我很快就迷失在 System.Windows.Input.InputManager 和 System.Windows.Input.StylusLogic 的 PromoteMainToMouse() 函数中。
我真的运行不知道要检查什么。 因此,如果有人可以给我建议还需要测试什么或在 .net 中的何处设置断点以找到根本原因,那就太好了。或者更好地找到 solution/workaround.
更新:
我发现如果将鼠标指针移离应用程序 window(至少在我们的平台上),我也可以重现该现象。并且错误发生的可能性似乎随着用户界面的复杂性而增加——我的测试应用程序并不总是无法识别点击,但一个复杂的 WPF 应用程序每次都会失败。
您应该能够在 Windows 8.1 上使用 WPF 开发人员工作室(例如 2013 版)通过触摸设备重现它:
- 让开发者工作室打开,只覆盖一半触摸屏
- 移开移动使光标指向桌面区域
- 触摸按钮(例如“开始调试”——播放按钮):在我的例子中,第一次触摸永远不会导致点击事件。
原来是WPF中的BUG。在发布 KB 之前,有一个解决方法可用。
解决方法是在 MainWindow 受影响的 UIElement 的 PreviewTouchDown
事件处理程序中调用 Mouse.Synchronize();
。
我测试了它,它工作正常。
我遇到了同样的问题,发现禁用 RealTimeStylus(有效地禁用触摸支持)对我有用,导致鼠标事件总是在触摸时引发。
我们在 Windows10.
上使用支持多点触控的工业触摸屏public static void DisableWPFTabletSupport()
{
// Get a collection of the tablet devices for this window.
TabletDeviceCollection devices = System.Windows.Input.Tablet.TabletDevices;
if (devices.Count > 0)
{
// Get the Type of InputManager.
Type inputManagerType = typeof(System.Windows.Input.InputManager);
// Call the StylusLogic method on the InputManager.Current instance.
object stylusLogic = inputManagerType.InvokeMember("StylusLogic",
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, InputManager.Current, null);
if (stylusLogic != null)
{
// Get the type of the stylusLogic returned from the call to StylusLogic.
Type stylusLogicType = stylusLogic.GetType();
// Loop until there are no more devices to remove.
while (devices.Count > 0)
{
// Remove the first tablet device in the devices collection.
stylusLogicType.InvokeMember("OnTabletRemoved",
BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic,
null, stylusLogic, new object[] { (uint)0 });
}
}
}
}