本次又遇到了WPF编写触控程序的一个问题,虽然已解决,但原因确搞不太明白,希望有大神看到这篇文章帮我解答。

在项目中实现了自己定义的icommandsource,因为需要对触控有特殊需求,控件对鼠标与触摸有了各自的事件响应,以下代码是原始touchup事件的处理逻辑。

         protected override void OnTouchUp(TouchEventArgs e)
{
if (_deviceId == e.TouchDevice.Id)
{
ReleaseAllTouchCaptures();
ReleaseDevice(); if (!_isDraging)
{
RoutedEventArgs rea = new RoutedEventArgs(Button.ClickEvent, this);
RaiseEvent(rea); if (!rea.Handled && ClickCommand != null)
{
ClickCommand.Execute(ClickCommandParam);
} } base.OnTouchUp(e);
}

使用该控件编写界面,触摸点击按钮后弹出其他窗口,这时神奇的事件发生了:

如果使用的是带触控屏的PC上,新弹出的窗口不会自动激活需要新点击1次(貌似问题不大)

但如果程序运行在一台surface上,新弹出的窗口需要点击10次才会被激活,随后其中的控件才会收到事件响应。(PC与Surface均是win10系统,需要点击10次这个数据很准确,已经过反复测试,就像是谁在代码是设置了计数器一样准确)。

发现问题后感觉特别茫然,也没查到太多有关信息,一顿乱试发现了解决方案。改写后的代码如下:

         protected override void OnTouchUp(TouchEventArgs e)
{
if (_deviceId == e.TouchDevice.Id)
{
ReleaseAllTouchCaptures();
ReleaseDevice(); if (!_isDraging)
{
DelayRaiseClick();
}
} base.OnTouchUp(e);
} private async void DelayRaiseClick()
{
await System.Threading.Tasks.Task.Run(() =>
{
System.Threading.Thread.Sleep();
}); RoutedEventArgs rea = new RoutedEventArgs(Button.ClickEvent, this);
RaiseEvent(rea); if (!rea.Handled && ClickCommand != null)
{
ClickCommand.Execute(ClickCommandParam);
}
}

可以看到,上下代码的唯一区别仅是抛出click事件前异步等待了50ms,但这个变化使得新弹出的窗口可以直接激活,surface与pc上运行效果一致。那么问题就来了,在这异步等待的50ms中发生了什么,我能想到的就是,界面的路由事件可以完整的传递完毕,包括touchup和由触摸引发的mouseup消息。至于为什么消息没有传递完毕就会出问题(把e.Handled置为true可以阻止消息继续传递啊,也没说必须传递完)想不明白,还有,为什么在surface上需要操作10次,这个计数是谁干的,完全没有头续。。。

借用我一个同事的话说,这个解决方案虽然完成了任务,但太恶心了(ps,他也遇到了跟我一样的问题,而具用的是wpf原生按钮,这让这个问题更难理解了)

最后,希望有了解的大神帮我解释这个问题,从而得到不恶心人的解决方法。。

05-11 16:56