本次又遇到了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原生按鈕,這讓這個問題更難理解了)
最后,希望有了解的大神幫我解釋這個問題,從而得到不惡心人的解決方法。。