[WPF]爲舊版本的應用添加觸控支持

以前作WPF開發時曾經遇到這樣一個需求:爲一個基於 .NET Framework 3.5開發的老舊WPF程序添加觸控支持,以便於大屏觸控展現。程序員

接手以後發現這是一個大坑。c#

項目最初的時候徹底沒考慮過軟件架構設計,業務邏輯基本都寫在後臺代碼中,通過兩代程序員的開發維護(初代開發者已離職,文檔這種東西不存在的),主界面cs代碼已經有上萬行,各類事件註冊的很是雜亂。因爲是作給政府部門用的,穩定性很重要,修修補補不斷的打補丁,程序已經很是難維護了。架構

並且不像最新.net框架下的WPF以及UWP開發中,咱們有Pointer開頭的系列事件能夠統一處理鼠標點擊和觸控。在基於.net框架 4.7如下版本構建的WPF應用裏,鼠標點擊和觸控是獨立的,須要分別處理。框架

這裏有一點須要說明:在單點電阻式觸控屏(除了ATM機之類的特殊用途,基本要被淘汰掉了)下,系統對單點觸控的處理是模擬的鼠標操做,這種狀況下即便不處理觸控事件,程序也能夠正常運行,須要處理觸控事件特指的是支持多點觸控的電容式觸摸屏。測試

當時我接手的WPF應用以前是徹底沒有作過觸控事件處理的,我粗略的查找統計了一下,須要處理的按鈕點擊事件大概有上千個,若是手動處理,將是很是難以接受的重複工做,另外修改後的應用程序也必須完整走一遍測試流程,以防帶來災難性BUG。ui

那麼有沒有一種簡單的方法能夠快速處理呢?this

咱們知道WPF開發中,全部的用戶交互事件都是路由事件,其中帶有Preview前綴的爲隧道路由事件,不帶前綴的爲冒泡路由事件。其區別是:隧道路由事件由根元素傳遞到觸發事件的元素,而冒泡路由事件傳遞方向正好相反。那麼,儘管程序中須要處理觸控事件的地方不少,可是咱們均可以在應用頂層元素中經過冒泡路由事件攔截到。是否是能夠利用這一點作文章呢?.net

個人想法是這樣的:因爲應用已經處理了鼠標交互事件,那咱們徹底能夠將應用的觸控事件轉發給鼠標交互事件的Handler去處理,這樣就避免了咱們作機械的重複操做。架構設計

具體處理步驟以下:設計

  1. 在應用窗口的頂級元素(可視化樹的根節點)上添加觸控事件處理程序,捕獲應用內部觸控事件;

    this.AddHandler(TouchUpEvent, new RoutedEventHandler(GetTouchUp));
    this.AddHandler(TouchDownEvent, new RoutedEventHandler(GetTouchDown));
  2. 獲取引起事件的源控件(本來想經過e.OriginalSource獲取,但測試中發現獲取的有錯誤,因此用UIElement類中的InputHitTest方法傳入觸控點座標,獲取到引起事件的源控件);

    TouchEventArgs te = (TouchEventArgs)e;
    Point p = te.GetTouchPoint(this).Position;//這裏是獲取觸控點相對某個界面元素的座標
    UIElement uiControl = (UIElement)this.InputHitTest(p);
  3. 觸發源控件的鼠標事件(在TouchUp中還同時觸發了Button類的Click事件,用於處理按鈕的點擊事件);

    MouseButtonEventArgs args = new MouseButtonEventArgs(Mouse.PrimaryDevice,te.Timestamp,MouseButton.Left);
    args.RoutedEvent = MouseDownEvent;
    uiControl.RaiseEvent(args);

完整的事件處理代碼以下:

this.AddHandler(TouchUpEvent, new RoutedEventHandler(GetTouchUp));
        this.AddHandler(TouchDownEvent, new RoutedEventHandler(GetTouchDown));
        private void GetTouchDown(object sender, RoutedEventArgs e)
        {
            TouchEventArgs te = (TouchEventArgs)e;
            Point p = te.GetTouchPoint(this).Position;
            UIElement uiControl = (UIElement)this.InputHitTest(p);
            MouseButtonEventArgs args = new MouseButtonEventArgs(Mouse.PrimaryDevice, te.Timestamp, MouseButton.Left);
            args.RoutedEvent = MouseDownEvent;
            uiControl.RaiseEvent(args);
        }
        private void GetTouchUp(object sender, RoutedEventArgs e)
        {
            TouchEventArgs te = (TouchEventArgs)e;
            Point p = te.GetTouchPoint(this).Position;
            UIElement uiControl = (UIElement)this.InputHitTest(p);
            MouseButtonEventArgs args = new MouseButtonEventArgs(Mouse.PrimaryDevice, te.Timestamp, MouseButton.Left);
            args.RoutedEvent = MouseUpEvent;
            uiControl.RaiseEvent(args);
            uiControl.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
        }

要說明的一點是,我這裏的處理是不完善的,僅僅處理了常見的點擊操做。譬如鼠標右鍵(合理的觸控事件應該是長按界面元素一段時間後觸發),鼠標移動,滾輪操做都沒有作處理,這些也是能夠經過相似的方法轉換爲合適的觸控事件觸發的。

結尾

今天文章裏所述的內容其實已是好久之前的東西了,我如今的主要工做方向遠離WPF開發好久了,忽然翻相關的舊文件想起來,因此纔有了這篇文章。好記性不如爛筆頭,知識不用總有忘的一天,不如寫出來貢獻給須要的人,謝謝你們!

相關文章
相關標籤/搜索