本次又遇到了WPF编写触控程序的一个问题,虽然已解决,但原因确搞不太明白,希望有大神看到这篇文章帮我解答。
在项目中实现了自己定义的icommandsource,因为需要对触控有特殊需求,控件对鼠标与触摸有了各自的事件响应,以下代码是原始touchup事件的处理逻辑。
1 protected override void OnTouchUp(TouchEventArgs e) 2 { 3 if (_deviceId == e.TouchDevice.Id) 4 { 5 ReleaseAllTouchCaptures(); 6 ReleaseDevice(); 7 8 if (!_isDraging) 9 { 10 RoutedEventArgs rea = new RoutedEventArgs(Button.ClickEvent, this); 11 RaiseEvent(rea); 12 13 if (!rea.Handled && ClickCommand != null) 14 { 15 ClickCommand.Execute(ClickCommandParam); 16 } 17 18 } 19 20 base.OnTouchUp(e); 21 }
使用该控件编写界面,触摸点击按钮后弹出其他窗口,这时神奇的事件发生了:
如果使用的是带触控屏的PC上,新弹出的窗口不会自动激活需要新点击1次(貌似问题不大)
但如果程序运行在一台surface上,新弹出的窗口需要点击10次才会被激活,随后其中的控件才会收到事件响应。(PC与Surface均是win10系统,需要点击10次这个数据很准确,已经过反复测试,就像是谁在代码是设置了计数器一样准确)。
发现问题后感觉特别茫然,也没查到太多有关信息,一顿乱试发现了解决方案。改写后的代码如下:
1 protected override void OnTouchUp(TouchEventArgs e) 2 { 3 if (_deviceId == e.TouchDevice.Id) 4 { 5 ReleaseAllTouchCaptures(); 6 ReleaseDevice(); 7 8 if (!_isDraging) 9 { 10 DelayRaiseClick(); 11 } 12 } 13 14 base.OnTouchUp(e); 15 } 16 17 private async void DelayRaiseClick() 18 { 19 await System.Threading.Tasks.Task.Run(() => 20 { 21 System.Threading.Thread.Sleep(50); 22 }); 23 24 RoutedEventArgs rea = new RoutedEventArgs(Button.ClickEvent, this); 25 RaiseEvent(rea); 26 27 if (!rea.Handled && ClickCommand != null) 28 { 29 ClickCommand.Execute(ClickCommandParam); 30 } 31 }
可以看到,上下代码的唯一区别仅是抛出click事件前异步等待了50ms,但这个变化使得新弹出的窗口可以直接激活,surface与pc上运行效果一致。那么问题就来了,在这异步等待的50ms中发生了什么,我能想到的就是,界面的路由事件可以完整的传递完毕,包括touchup和由触摸引发的mouseup消息。至于为什么消息没有传递完毕就会出问题(把e.Handled置为true可以阻止消息继续传递啊,也没说必须传递完)想不明白,还有,为什么在surface上需要操作10次,这个计数是谁干的,完全没有头续。。。
借用我一个同事的话说,这个解决方案虽然完成了任务,但太恶心了(ps,他也遇到了跟我一样的问题,而具用的是wpf原生按钮,这让这个问题更难理解了)
最后,希望有了解的大神帮我解释这个问题,从而得到不恶心人的解决方法。。