【WPF學習】第十八章 多點觸控輸入

来源:https://www.cnblogs.com/Peter-Luo/archive/2020/01/30/12241551.html
-Advertisement-
Play Games

多點觸控(multi-touch)是通過觸摸屏幕與應用程式進行交互的一種方式。多點觸控輸入和更傳統的基於筆(pen-based)的輸入的區別是多點觸控識別手勢(gesture)——用戶可移動多根手指以執行常見操作的特殊方式。例如,在觸摸屏上放置兩根手指並同時移動他們,這通常意味著“放大",而以一根手 ...


  多點觸控(multi-touch)是通過觸摸屏幕與應用程式進行交互的一種方式。多點觸控輸入和更傳統的基於筆(pen-based)的輸入的區別是多點觸控識別手勢(gesture)——用戶可移動多根手指以執行常見操作的特殊方式。例如,在觸摸屏上放置兩根手指並同時移動他們,這通常意味著“放大",而以一根手指為支點轉動另一根手指意味著"旋轉"。並且因為用戶直接在應用程式視窗中進行這些手勢,所以每個手勢自然會被連接到某個特定的對象。例如,簡單的具有多點觸控功能的應用程式,可能會在虛擬桌面上顯示多幅圖片,並且允許用戶拖動、縮放以及旋轉每幅圖片,進而創建新的排列方式。

  在智能手機和平板電腦上,多點觸控屏幕幾乎無處不在。但在普通電腦上,多點觸控屏幕較少見。儘管硬體製造商已經產生了觸摸屏筆記本電腦和LCD顯示器,但傳統的筆記本電腦和顯示器仍占據主導地位。

  這對於希望實驗多點觸控應用程式的開發人員是一個挑戰。到目前位置,最好的方式是投資購買基本的多點觸控筆記本。然而,通過多做一點工作,可使用模擬器模擬多點觸控輸入。基本做飯是為電腦連接多個滑鼠並安裝來自Multi-Touch Visita開源項目(對於Windows 7該項目也能工作)的驅動程式。具體安裝請自覺網上搜著安裝步驟。

一、多點觸控的輸入層次

  正如前兩章所瞭解的,WPF允許使用鍵盤和滑鼠的高層次輸入(例如單擊和文本改變)和低層次輸入(滑鼠事件以及按鍵事件)。這很重要,因為有些應用程式需要加以更精細的控制。多點觸控輸入同樣應用了這種多層次的輸入方式,並且對於多點觸控支持,WPF提供了三個獨立的層次:

  •   原始觸控(raw touch):這是最低級的支持,可訪問用戶執行的每個觸控。缺點是由您的應用程式負責將單獨的觸控消息組合到一起,並對他們進行解釋。如果不准備識別標準觸摸手勢,反而希望創建以獨特方式響應多點觸控輸入的應用程式,使用原始觸控是合理的。一個例子是繪圖程式,例如Windows7畫圖程式,該程式允許用戶同時多根手指在觸摸屏上繪圖。
  •   操作(manipulation):這是一個簡便的抽象層,該層將原始的多點觸控輸入轉換成更有意義的手勢,與WPF控制項將一系列MouseDown和MouseUp事件解釋為更高級的MouseDoubleClick事件很相似。WPF支持的通用手勢包括移動(pan)、縮放(zoom)、選擇(rotate)以及輕按(tap)。
  •   內置的元素支持(built-in element support):有些元素已對多點觸控事件提供了內置支持,從而不需要在編寫代碼。例如,可滾動的控制項支持觸控移動,如ListBox、ListView、DataGrid、TextBox以及ScrollViewer。

二、原始觸控

  與基本的滑鼠和鍵盤事件一樣,觸控事件被內置到低級的UIElement以及ContentElement類。下表列出了所有觸控事件。

表 所有元素的原始觸控事件

名稱 路由類型 說明
PreviewTouchDown 隧道 當用戶觸摸元素時發生
TouchDown 冒泡 當用戶觸摸元素時發生
PreviewTouchMove 隧道 當用戶移動放到觸摸屏上的手指時發生
TouchMove 冒泡 當用戶移動放到觸摸屏上的手指時發生
PreviewTouchUp 隧道 當用戶移開手指,結束觸摸時發生
TouchUp 冒泡 當用戶移開手指,結束觸摸時發生
TouchEnter 當出點從元素外進入元素內時發生
TouchLeave 當出點離開元素時發生

  所有這些事件都提供了一個TouchEventArgs對象,該對象提供了兩個重要成員。第一個是GetTouchPoint()方法,該方法返回觸控事件發生位置的屏幕坐標(還有一些不怎麼常用的數據,例如觸點的大小)。第二個是TouchDevice屬性,該屬性返回一個TouchDevice對象。這裡的技巧是將每個出點都視為單獨設備。因此,如果用戶在不同的位置按下兩根手指(同時按下或者先按下一根再按下另一根),WPF將它們作為兩個觸控設備,併為兩個觸控設備指定唯一的ID。當用戶移動這些手指,並且觸控事件發生時,代碼可以通過ToucheDevice.Id屬性區分兩個觸點。

  下麵是一個簡單的示例:

  為了創建這個示例,需要處理TouchDown、TouchMove以及TouchUp事件:

<Window x:Class="Multitouch.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Canvas x:Name="canvas"  Background="LightBlue" 
            TouchDown="Canvas_TouchDown" TouchUp="canvas_TouchUp"
            TouchMove="canvas_TouchMove">
        
    </Canvas>
</Window>

  為了跟蹤所有觸點,需要作為視窗成員變數存儲一個集合。最簡潔的方法是存儲UIElement對象的集合(為每個激活的橢圓存儲一個UIElement對象),是喲該觸控設備的ID(該ID是整數)編寫索引:

private Dictionary<int, UIElement> movingEllipses = new Dictionary<int, UIElement>();

  當用戶按下一根手指時,代碼創建並配置一個新的橢圓元素(該元素看起來像個小圓)。使用觸點在恰當的坐標放置橢圓,並將橢圓元素添加到集合中(根據觸控設備的ID編寫索引),然後再Canvas面板上顯示該橢圓元素:

private void Canvas_TouchDown(object sender, TouchEventArgs e)
        {
            //Create an ellipse to draw at the new contact point
            Ellipse ellipse = new Ellipse();
            ellipse.Width = 30;
            ellipse.Height = 30;
            ellipse.Stroke = Brushes.White;
            ellipse.Fill = Brushes.Green;

            //Position the ellipse at the contact point
            TouchPoint touchPoint = e.GetTouchPoint(canvas);
            Canvas.SetTop(ellipse, touchPoint.Bounds.Top);
            Canvas.SetLeft(ellipse, touchPoint.Bounds.Left);

            //Store the ellipse in the active collection
            movingEllipses[e.TouchDevice.Id] = ellipse;

            //Add the ellipse to the Canvas
            canvas.Children.Add(ellipse);
        }

  當用戶移動按下的手指時,將觸發TouchMove事件。此時,可使用觸控設備的ID確定哪個點正在移動。代碼需要做的全部工作就是查找對相應的橢圓並更新其坐標:

private void canvas_TouchMove(object sender, TouchEventArgs e)
        {
            //Get the ellipse that corresponds to the current contact point
            UIElement element = movingEllipses[e.TouchDevice.Id];

            //Move it to the new contact point
            TouchPoint touchPoint = e.GetTouchPoint(canvas);
            Canvas.SetTop(element, touchPoint.Bounds.Top);
            Canvas.SetLeft(element, touchPoint.Bounds.Left);
        }

  最後,當用戶抬起手指時,從跟蹤集合中移除橢圓。作為一種選擇,您可能也希望從Canvas面板中移除橢圓:

private void canvas_TouchUp(object sender, TouchEventArgs e)
        {
            //Remove the ellipse from the Canvas
            UIElement element = movingEllipses[e.TouchDevice.Id];
            canvas.Children.Remove(element);

            //Remove the ellipse from the tracking collection
            movingEllipses.Remove(e.TouchDevice.Id);
        }

  註意:

    UIElement還添加了CaptureTouch()和ReleaseTouchCapture()方法,這兩個方法與CaptureMouse()和ReleaseMouseCapture()方法類似。當一個元素捕獲觸控輸入後,該元素會接受來自被捕獲的觸控設備的所有觸控事件,即使觸控事件是在視窗的其他地方發生的也是如此。但因為可能有多個觸控設備,所以多個元素可能同時捕獲觸控輸入,只要每一個捕獲來自不同設備的輸入即可。

三、操作

  對於那些以簡明直接的方式使用觸控事件的應用程式,例如上面介紹的拖動橢圓示例或畫圖程式,原始觸控是非常好的。但是,如果希望支持標準的觸控手勢,原始觸控不會簡化該工作。例如,為了支持旋轉,需要探測在同一個元素上的兩個觸點,跟蹤這兩個觸點的移動情況,並使用一些運算確定一個觸點繞另一個觸點的轉動情況。甚至,伺候還需要添加實際應用相應旋轉效果的代碼。

  幸運的是,WPF未將這些工作完全留給你。WPF為手勢提供了更高級別的支持,稱為觸控操作(manipulation)。通過將元素的IsManipulationEnabled屬性設置為True,將元素配置為接受觸控操作。然後可響應4個操作時間:ManipulationStaring、ManipulationStared、ManipulationDelta以及ManipulationCompleted。

  創建一個實例,該例使用基本的安排在Canvas面板上顯示三幅圖像。此後用戶可使用移動、旋轉以及縮放手勢來移動、轉動、縮小或發達圖像。

  創建這個示例的第一步是定義Canvas面板並放置三個Image元素。為簡化實現,當ManipulationStarting和ManipulationDelta事件從適當的Image元素內部向上冒泡後,在Canvas面板中處理這兩個事件:

<Window x:Class="Multitouch.Manipulations"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Manipulations" Height="349" Width="607">
    <Grid>
        <Canvas x:Name="canvas" ManipulationStarting="image_ManipulationStarting"  ManipulationDelta="image_ManipulationDelta">
            <Image Canvas.Top="10" Canvas.Left="10" Width="200" IsManipulationEnabled="True" Source="koala.jpg">
                <Image.RenderTransform>
                    <MatrixTransform></MatrixTransform>
                </Image.RenderTransform>
            </Image>
            <Image Canvas.Top="30" Canvas.Left="350" Width="200" IsManipulationEnabled="True" Source="penguins.jpg">
                <Image.RenderTransform>
                    <MatrixTransform></MatrixTransform>
                </Image.RenderTransform>
            </Image>
            <Image Canvas.Top="100" Canvas.Left="200" Width="200" IsManipulationEnabled="True" Source="tulips.jpg">
                <Image.RenderTransform>
                    <MatrixTransform></MatrixTransform>
                </Image.RenderTransform>
            </Image>
        </Canvas>
    </Grid>
</Window>

  上面的表中有一個新的細節。每個圖像包含一個MatrixTransform對象,該對象為代碼應用移動、旋轉以及縮放操作的組合提供了一種簡易方式。當前,MatrixTransform對象未執行任何操作,但當操作事件發生時,將使用代碼改變。

  當用戶觸摸一幅圖像時,將觸發ManipulationStarting事件。這是,需要設置操作容器,它是在後面將獲得的所有操作坐標的參考點。在該例中,包含圖像的Canvas面板是自然自選。還可根據需要選擇允許的操作類型。如果不選擇操作類型,WPF將監視它識別的所有手勢:移動、縮放以及旋轉。

private void image_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
        {
            // Set the container (used for coordinates.)
            e.ManipulationContainer = canvas;

            // Choose what manipulations to allow.
            e.Mode = ManipulationModes.All;
        }

  當發生操作是(但操作未必結束),觸發ManipulationDelta事件。例如,如果用戶開始選擇一幅圖像,將不斷觸發ManipulationDelta事件,直到用戶選擇結束並且用戶抬起按下的手指為止。

  通過使用ManipulationDelta對象將手勢的當前狀態記錄下來,該對象是通過ManipulationDeltaEventArgs.DeltaManipulation屬性提供的。本質上,ManipulationDelta對象記錄了應當應用到對象的縮放、旋轉以及移動的量,這些信息時通過三個簡單的屬性提供的:Scale、Rotation以及Translation。使用這一信息的技巧是在用戶界面中調整元素。

  理論上,可通過改變元素的大小和位置來處理縮放和移動細節。但這仍不能應用旋轉(而且代碼有些凌亂)。更好的方法是使用變換——通過變換對象可採用數學方法改變任何WPF元素的外觀。基本思路是獲取由ManipulationDelta對象提供的信息,並使用這些信息配置MatrixTransform。儘管這聽起來很複雜,但需要使用的代碼在使用該特性的每個應用程式中本質上時相同的。看起來如下所示:

private void image_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {
            // Get the image that's being manipulated.            
            FrameworkElement element = (FrameworkElement)e.Source;

            // Use the matrix to manipulate the element.
            Matrix matrix = ((MatrixTransform)element.RenderTransform).Matrix;

            var deltaManipulation = e.DeltaManipulation;
            // Find the old center, and apply the old manipulations.
            Point center = new Point(element.ActualWidth / 2, element.ActualHeight / 2);
            center = matrix.Transform(center);

            // Apply zoom manipulations.
            matrix.ScaleAt(deltaManipulation.Scale.X, deltaManipulation.Scale.Y, center.X, center.Y);

            // Apply rotation manipulations.
            matrix.RotateAt(e.DeltaManipulation.Rotation, center.X, center.Y);

            // Apply panning.
            matrix.Translate(e.DeltaManipulation.Translation.X, e.DeltaManipulation.Translation.Y);

            // Set the final matrix.
            ((MatrixTransform)element.RenderTransform).Matrix = matrix;

        }

四、慣性

   WPF還有一層構建在基本操作支持之上的特性,稱為慣性(intertia)。本質上,通過慣性可以更逼真、更流暢地操作元素。

  現在,如果用戶用移動手勢拖動上例中的一幅圖像,當手指從觸摸屏上抬起時圖像會立即停止移動。但如果啟用了慣性特性,那麼圖像會繼續移動非常短的一段時間,正常地減速。該特性為操作提供了勢頭的效果和感覺。當將元素拖動進他們不能穿過的邊界時,慣性還會使元素被彈回,從而使他們的行為像是真實的物理對象。

  為給上一個示例添加慣性特性,只需要處理ManipulationInertiaStarting事件。與其他操作事件一樣,該事件從一幅圖像開始並冒泡至Canvas面板。當用戶結束手勢並抬起手指釋放元素時,觸發ManipulationInertiaStarting事件。這是,可使用ManipulationInertiaStartingEventsArgs對象確定當期速度——當操作結束時元素的移動速度——並設置希望的減速度。下麵的示例為移動、縮放以及旋轉手勢添加了慣性:

private void canvas_ManipulationInertiaStarting(object sender, ManipulationInertiaStartingEventArgs e)
        {
            //If the object is moving,decrease its speed by
            //10 inches per second every second
            //deceleration=10 inches * 96 units per inch /(1000 milliseconds)^2
            e.TranslationBehavior = new InertiaTranslationBehavior();
            e.TranslationBehavior.InitialVelocity = e.InitialVelocities.LinearVelocity;
            e.TranslationBehavior.DesiredDeceleration = 10.0 * 96.0 / (1000.0 * 1000.0);

            //Decrease the speed of zooming by 0.1 inches per second every second.
            //deceleration=0.1 inches * 96 units per inch/(1000 milliseconds)^2
            e.ExpansionBehavior = new InertiaExpansionBehavior();
            e.ExpansionBehavior.InitialVelocity = e.InitialVelocities.ExpansionVelocity;
            e.ExpansionBehavior.DesiredDeceleration = 0.1 * 96 / (1000.0 * 1000.0);

            //Decrease the rotation rate by 2 rotations per second every second.
            //deceleration=2*36 degress /(1000 milliseconds)^2

            e.RotationBehavior = new InertiaRotationBehavior();
            e.RotationBehavior.InitialVelocity = e.InitialVelocities.AngularVelocity;
            e.RotationBehavior.DesiredDeceleration=720/(1000.0*1000.0)
        }

  為使元素從障礙物自然地被彈回,需要在ManipulationDelta事件中檢查是否將元素拖到了錯誤的位置。如果穿過了一條邊界,那麼由你負責通過調用ManipulationDeltaEventArgs.ReportBoundaryFeedback()方法進行報告。

  


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 兩種模式:隊列模式/主題模式 pom.xml <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-all</artifactId> <version>5.15.9</version> </dependenc ...
  • 進入命令行視窗,開始菜單搜索框輸入“cmd”即可,如圖1-18所示。在視窗中輸入命令“java -version ”,回車。出現如下結果,則說明JDK安裝成功。 設置環境變數——驗證JDK安裝和配置是否成功 作者:流浪者 日期:2020-01-30 ...
  • 環境變數是在操作系統中一個具有特定名字的對象, 它包含了一個或者多個應用程式所將使用到的信息。 Path是一個常見的環境變數,它告訴操作系統,當要求系統運行一個程式而沒有告訴它程式所在的完整路徑時,系統除了在當前目錄下尋找此程式外,還應到哪些目錄下尋找。 ·設置Path環境變數的步驟如下 1. 右鍵 ...
  • ·下載JDK 1. 下載地址,點擊進入: www.oracle.com/technetwork/java/javase/downloads/index.html 點擊下載JDK,會出現圖1-9所示的下載列表界面。首先,點擊”Accept License Agreement”,然後選擇對應的版本,下載 ...
  • JVM(Java Virtual Machine)就是一個虛擬的用於執行bytecode位元組碼的”虛擬電腦”。他也定義了指令集、寄存器集、結構棧、垃圾收集堆、記憶體區域。JVM負責將Java位元組碼解釋運行,邊解釋邊運行,這樣,速度就會受到一定的影響。 不同的操作系統有不同的虛擬機。Java 虛擬機機 ...
  • 電腦高級語言的類型主要有編譯型(如:C和C++)和解釋型(如:PHP、Python)兩種,而Java 語言是兩種類型的結合。 Java首先利用文本編輯器編寫 Java源程式,源文件的尾碼名為.java;再利用編譯器(javac)將源程式編譯成位元組碼文件,位元組碼文件的尾碼名為.class; 最後利用 ...
  • 這是Serilog系列的第三篇文章。 1. "第1部分 使用Serilog RequestLogging減少日誌詳細程度" 2. "第2部分 使用Serilog記錄所選的終結點屬性" 3. 第3部分 使用Serilog.AspNetCore記錄MVC屬性(本文) 4. 第4部分 從Serilog請 ...
  • WPF視窗充滿了各種元素,但這些元素中只有一部分是控制項。在WPF領域,控制項通常被描述為與用戶交互的元素——能接收焦點並接受鍵盤或滑鼠輸入的元素。明顯的例子包括文本框和按鈕。然而,這個區別有時有些模糊。將工具提示視為控制項,因為它根據用戶滑鼠的移動顯示或消失。將標簽視為控制項,因為它支持記憶碼(mnemo ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...