基於Visual C 2010開發Windows7應用 多點觸控圖片處理應用程式 1 同時處理多張圖片
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
windows7的觸控功能開闢了一個電腦的全新世紀。從此,您可以丟掉滑鼠和鍵盤,直接用手在螢幕上玩遊戲、用筆來寫字編輯文件,聊天。
windows7最重要特性之一就是:支援多點觸控。比爾蓋茨說,不久,滑鼠鍵盤會消失。
Windows 7 使使用者無需使用中間裝置,通過手指觸控方式就能夠管理應用程式。與其他指點裝置不同,這種新功能支援在不同指點位置上同時發生多個輸入事件,支援複雜的場景,比如通過十指或由多個並行使用者管理應用程式。然而,要成功實現此功能,我們必須調整應用程式的使用者介面和行為,以支援這種新的輸入模式。
下面我們來看下Windows7下多點觸控圖片處理應用程式的具體酷炫操作介面:
畫布功能的操作介面
多點觸控--將圖片自由縮放
當我們選擇好一張圖片後,通過紅外線觸控功能使用手指可以將圖片移動到螢幕內的任意位置。再通過多點觸控功能,使用兩根手指就可以輕鬆的將圖片進行自由的旋轉、縮小和放大。
選擇多個圖片進行操作
多圖片為一體時任意移動
還可以同時選擇多個圖片一起操作。在自己想操作的所有圖片範圍內,用手指以一個起點開始畫圈,最後終點要與起點重合,這樣就可以自由觸控被選中的所有圖片,不過此時這些圖片是一體化的,如果想取消全選只需要點選另外任何一張圖片或者螢幕內的黑色範圍就可取消。
選擇並拖拽資料夾內的圖片
給圖片加以標籤說明
將標籤匯入圖片上
在螢幕的下半部分,單擊左側的圖片資料夾,會彈出該資料夾內所有的圖片,通過手指上下移動,可以對所有的圖片進行瀏覽,如果選擇好其中的一張圖片,直接用手指拖拽到操作介面上即可。另外通過螢幕中間最下方右邊的標籤功能,再結合觸控式鍵盤輸入資訊,可以給每一張圖片新增標籤說明,而被新增上標籤的圖片會自動儲存到標籤資料夾裡,這樣方便使用者對圖片的統一分配和整理。
好了上面介紹了這麼多關於Windows7的多點觸控圖片應用程式,下面我們來打造自己的多點觸控圖片處理應用程式,目標是將一個基於滑鼠的簡單圖片操作應用程序升級為支援多點觸控的現代應用程式,類似於 Microsoft Surface 行為。
開發多點觸控圖片處理應用程式
要理解如何管理多點觸控輸入,我們首先需要理解如何處理(基於滑鼠的)單點輸入。為此,我們準備了一個基於滑鼠的圖片處理應用程式,就是多點觸控動手實驗初始應用程式。
瞭解解決方案
1. 開啟位於 %TrainingKitInstallDir%/MultiTouch/Ex1-PictureHandling/Begin 下的初始解決方案,選擇想要使用的語言C#。
2. 編譯並執行它。可以進行的操作有:通過單擊挑選一張圖片;按住滑鼠左鍵並移動滑鼠來拖動圖片;使用滑鼠滾輪縮放圖片。每次選擇一張圖片時,該圖片就會出現在最前面。在開始編碼之前,首先了解一下初始應用程式。
該應用程式用於處理圖片。每張圖片由一個 Picture 使用者控制元件表示。這是一個非常簡單的控制元件,它基於 WPF。Picture 使用者控制元件的 XAML 如下:
[xhtml] view plain copy print ?- XAML
- <UserControl x:Class="MultitouchHOL.Picture"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
- <Image Source="{Binding Path=ImagePath}" Stretch="Fill" Width="Auto"
- Height="Auto" RenderTransformOrigin="0.5, 0.5">
- <Image.RenderTransform>
- <TransformGroup>
- <RotateTransform Angle="{Binding Path=Angle}"></RotateTransform>
- <ScaleTransform ScaleX="{Binding Path=ScaleX}"
- ScaleY="{Binding Path=ScaleY}">
- </ScaleTransform>
- <TranslateTransform X="{Binding Path=X}" Y="{Binding Path=Y}"/>
- </TransformGroup>
- </Image.RenderTransform>
- </Image>
- </UserControl>
注意: 此使用者控制元件的程式碼僅包括 ImagePath、Angle、ScaleX、ScaleY、X 和 Y 依賴屬性的宣告。ImagePath 是有效的影象檔案或資源的路徑。Angle 是影象的旋轉角度。ScaleX 和 ScaleY 是影象的縮放係數,而 X、Y 是影象的中心位置。
3. 現在看一下 MainWindow 類。此 XAML 檔案宣告 MainWindow:
[xhtml] view plain copy print ?- XAML
- <Window x:Class="MultitouchHOL.MainWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="MultitouchHOL" Height="300" Width="300" WindowState="Maximized"
- xmlns:mt="clr-namespace:MultitouchHOL">
- <Canvas Name="_canvas">
- </Canvas>
- </Window>
注意:此視窗僅包含一個畫布元素 (_canvas)。畫布是包含 Picture 使用者控制元件例項的面板。
4. 現在開啟 MainWindow.xaml.cs 。如果使用者按住滑鼠左鍵,_picture 成員將擁有當前跟蹤的圖片;否則,它將擁有空值。_prevLocation 是 Mouse Move 事件報告的上一個位置,用於計算偏移量。
5. MainWindow 建構函式建立主視窗,註冊各種事件處理函式。
[c-sharp] view plain copy print ?- public MainWindow()
- {
- InitializeComponent();
- //啟用筆觸事件和載入圖片
- this.Loaded += (s, e) => { LoadPictures(); };
- //註冊滑鼠事件
- MouseLeftButtonDown += ProcessDown;
- MouseMove += ProcessMove;
- MouseLeftButtonUp += ProcessUp;
- MouseWheel += ProcessMouseWheel;
- }
6. LoadPictures() 函式從使用者的圖片資料夾載入圖片,併為所有圖片建立一個 Picture 控制元件。它只在初始化畫布之後才執行此操作。下面看一下 LoadPictures() 程式碼。
7. 現在看一下如何處理滑鼠事件。
[c-sharp] view plain copy print ?- private void ProcessDown(object sender, MouseButtonEventArgs args)
- {
- _prevLocation = args.GetPosition(_canvas);
- _picture = FindPicture(_prevMouseLocation);
- BringPictureToFront(_picture);
- }
按下滑鼠左鍵將啟動一個新的圖片拖動會話。首先我們必須獲得相對於畫布的指標位置。我們將此資訊儲存在 _prevLocation 資料成員中。
8. 下一步是在該位置找到一張圖片。FindPicture() 函式利用 WPF VisualTree 點選測試功能來找到最頂層的圖片。如果滑鼠所在位置沒有圖片,則返回空值。
9. BringPictureToFront() 將所選圖片的 Z 軸次序設定在其他圖片的最頂層。
此處理程式的處理結果是 _picture 資料成員“記住”了所選的圖片,_prevLocation 獲取滑鼠位置的程式碼片段。我們看一下當滑鼠移動時會發生什麼情況:
[c-sharp] view plain copy print ?- private void ProcessMove(object sender, MouseEventArgs args)
- {
- if (args.LeftButton == MouseButtonState.Released || _picture == null)
- return;
- Point newLocation = args.GetPosition(_canvas);
- _picture.X += newLocation.X - _prevMouseLocation.X;
- _picture.Y += newLocation.Y - _prevMouseLocation.Y;
- _prevLocation = newLocation;
- }
如果使用者未按下滑鼠左鍵或者未選擇任何圖片,該函式將不執行任何操作。否則,該函式將計算平移量並更新圖片的 X 和 Y 屬性。它還將更新 _prevLocation。
10. 我們需要注意的最後一個函式是 ProcessMouseWheel:
[c-sharp] view plain copy print ?- private void ProcessMouseWheel(object sender, MouseWheelEventArgs args)
- {
- Point location = args.GetPosition(_canvas);
- Picture picture = FindPicture(location);
- if (picture == null)
- return;
- BringPictureToFront(picture);
- double scalingFactor = 1 + args.Delta / 1000.0;
- picture.ScaleX *= scalingFactor;
- picture.ScaleY *= scalingFactor;
- }
將滑鼠事件替換為觸控事件
我們將刪除滑鼠事件並將其替換為觸控事件,以便使用我們的手指處理圖片。
1. 將以下程式碼行新增到 MainWindow.xaml.cs 檔案 (C#) 開頭:
[c-sharp] view plain copy print ?- using Windows7.Multitouch;
- using Windows7.Multitouch.WPF;
- Visual Basic
- Imports Windows7.Multitouch
- Imports Windows7.Multitouch.WPF
2. 我們想要在 WPF 3.5 SP1 中實現多點觸控事件。為此,必須告訴系統以觸筆事件的形式發出觸控事件。Windows 7 Integration Library 的 WPF Factory 類擁有一個函式來實現此功能,那就是 EnableStylusEvent。在 MainWindow Loaded 事件處理程式中新增對此函式的呼叫:
[c-sharp] view plain copy print ?- public MainWindow()
- {
- ...
- //啟用筆觸事件和載入圖片
- this.Loaded += (s, e) => { Factory.EnableStylusEvents(this); LoadPictures(); };
- ...
3. 刪除 ProcessMouseWheel 事件處理程式及相應的事件註冊(我們將在稍後處理縮放)。
4. (僅適用於 C# 使用者)刪除 MouseLeftButtonDown、MouseMove 和 MouseLeftButtonUp 的事件註冊程式碼。MainWindow 建構函式應該類似於以下程式碼:
[c-sharp] view plain copy print ?
- public MainWindow()
- {
- InitializeComponent();
- if (!Windows7.Multitouch.TouchHandler.DigitizerCapabilities.IsMultiTouchReady)
- {
- MessageBox.Show("Multitouch is not availible");
- Environment.Exit(1);
- }
- this.Loaded += (s, e) => { Factory.EnableStylusEvents(this); LoadPictures(); };
- }
5. 更改以下事件處理程式的簽名和程式碼:
注意:此事件處理程式的簽名已經更改。我們使用StylusEventArgs 代替與滑鼠相關的事件引數。
(程式碼片段 – MultiTouch – StylusEventHandlers CSharp)
[c-sharp] view plain copy print ?- public void ProcessDown(object sender, StylusEventArgs args)
- {
- _prevLocation = args.GetPosition(_canvas);
- _picture = FindPicture(_prevLocation);
- BringPictureToFront(_picture);
- }
- public void ProcessMove(object sender, StylusEventArgs args)
- {
- if (_picture == null)
- return;
- Point newLocation = args.GetPosition(_canvas);
- _picture.X += newLocation.X - _prevLocation.X;
- _picture.Y += newLocation.Y - _prevLocation.Y;
- _prevLocation = newLocation;
- }
- public void ProcessUp(object sender, StylusEventArgs args)
- {
- _picture = null;
- }
6. 註冊觸筆事件。
[c-sharp] view plain copy print ?- public MainWindow()
- {
- ...
- //註冊(觸控)事件
- StylusDown += ProcessDown;
- StylusUp += ProcessUp;
- StylusMove += ProcessMove;
- }
7. 編譯並執行。使用手指代替滑鼠!
注意: 如果嘗試使用多個手指會發生什麼情況?為什麼?
同時處理多張圖片
在本任務中,我們將新增多點觸控支援。觸控式螢幕幕的每個手指都會獲得一個唯一的觸控 ID。只要這根手指繼續觸控式螢幕幕,系統就會將相同的觸控 ID 與該手指關聯。當手指離開螢幕表面時,該觸控 ID 將被系統釋放並可被硬體再次使用。在我們的示例中,當一根手指觸控圖片時,應該將該手指的唯一觸控 ID 與該圖片關聯,直到該手指離開螢幕。如果兩個或更多手指同時觸控式螢幕幕,那麼每個手指都可以操作相關的圖片。
當使用 Stylus 事件作為觸控事件時,可以從 Stylus 事件引數中提取出觸控 ID:args.StylusDevice.Id
WPF 將使用相關的 StylusDevice.Id(觸控 ID)不斷為每個觸控式螢幕幕的手指觸發事件。
1. 我們需要同時跟蹤多張圖片。對於每張圖片,觸控 ID、上一個位置與圖片使用者控制元件之間必須保持關聯。我們將首先新增一個新的 PictureTracker 類:
注意:PictureTracker 類也在 %TrainingKitInstallDir%/MultiTouch/Assets/PictureHandling下以實驗資源的形式提供,請選擇您想要使用的語言(C#)。
[c-sharp] view plain copy print ?
- /// <summary>
- /// 跟蹤單個圖片
- /// </summary>
- class PictureTracker
- {
- private Point _prevLocation;
- public Picture Picture { get; set; }
- public void ProcessDown(Point location)
- {
- _prevLocation = location;
- }
- public void ProcessMove(Point location)
- {
- Picture.X += location.X - _prevLocation.X;
- Picture.Y += location.Y - _prevLocation.Y;
- _prevLocation = location;
- }
- public void ProcessUp(Point location)
- {
- //什麼都不做,可能有另一個觸控ID仍下跌
- }
- }
2. 現在我們需要一個詞典,以將活動的觸控 ID 對映到相應的 PictureTracker 例項。我們將建立一個 PictureTrackerManager 類來包含該詞典並處理各種觸控事件。無論何時觸發了觸控事件,PictureTrackerManager 都將嘗試找到關聯的 PictureTracker 例項並要求它處理該觸控事件。換言之,PictureTrackerManager 將獲得觸控事件。它尋找作為實際事件目標的 PictureTracker 例項並將觸控事件分派給它。現在的問題是如何找到正確的 PictureTracker 例項。我們需要考慮一些不同的場景:
a. 發生 ProcessDown 事件時,有 3 種選擇:
i. 手指觸控一個空位置。不會發生任何事件。
ii. 手指觸控新圖片。必須建立一個新 PictureTracker 例項,必須在觸控 ID 對映中建立一個新條目。
iii. 第 2 個(或更多)手指觸控已經被跟蹤的圖片。我們必須將新的觸控 ID 與相同的 PictureTracker 例項相關聯。
b. 發生 ProcessMove 事件時,有 2 種選擇:
i. 手指的觸控 ID 未與一個 PictureTracker 相關聯。不應該發生任何事件。
ii. 手指的觸控 ID 與一個 PictureTracker 關聯。我們需要將事件轉發給它。
c. 發生 ProcessUp 事件時,有 2 種選擇:
i. 刪除了一個手指觸控 ID,但是至少還存在一個相關的觸控 ID。我們需要從對映中刪除此條目。
ii. 刪除了最後一個相關的觸控 ID。我們需要從對映中刪除該條目。圖片跟蹤器不再使用並且會被當作垃圾收集走。
3. 通過分析這些情形,我們可以定義 PictureTrackerManager 的設計條件:
a. 它必須擁有一個對映:觸控 ID PictureTracker
[c-sharp] view plain copy print ?- private readonly Dictionary<int, PictureTracker> _pictureTrackerMap
4. 新增以下 PictureTrackerManager 類:
注意:PictureTrackerManager 類也以實驗資產的形式在 %TrainingKitInstallDir%/MultiTouch/Assets/PictureHandling 下提供,
[c-sharp] view plain copy print ?- class PictureTrackerManager
- {
- //圖片之間的接觸和ID跟蹤
- private readonly Dictionary<int, PictureTracker> _pictureTrackerMap = new Dictionary<int, PictureTracker>();
- private readonly Canvas _canvas;
- public PictureTrackerManager(Canvas canvas)
- {
- _canvas = canvas;
- }
- public void ProcessDown(object sender, StylusEventArgs args)
- {
- Point location = args.GetPosition(_canvas);
- PictureTracker pictureTracker = GetPictureTracker(args.StylusDevice.Id, location);
- if (pictureTracker == null)
- return;
- pictureTracker.ProcessDown(location);
- }
- public void ProcessUp(object sender, StylusEventArgs args)
- {
- Point location = args.GetPosition(_canvas);
- PictureTracker pictureTracker = GetPictureTracker(args.StylusDevice.Id);
- if (pictureTracker == null)
- return;
- pictureTracker.ProcessUp(location);
- _pictureTrackerMap.Remove(args.StylusDevice.Id);
- }
- public void ProcessMove(object sender, StylusEventArgs args)
- {
- PictureTracker pictureTracker = GetPictureTracker(args.StylusDevice.Id);
- if (pictureTracker == null)
- return;
- Point location = args.GetPosition(_canvas);
- pictureTracker.ProcessMove(location);
- }
- private PictureTracker GetPictureTracker(int touchId)
- {
- PictureTracker pictureTracker = null;
- _pictureTrackerMap.TryGetValue(touchId, out pictureTracker);
- return pictureTracker;
- }
- private PictureTracker GetPictureTracker(int touchId, Point location)
- {
- PictureTracker pictureTracker;
- //我們已經根據筆觸ID追蹤到了圖片
- if (_pictureTrackerMap.TryGetValue(touchId, out pictureTracker))
- return pictureTracker;
- //獲取圖片下的觸控位置
- Picture picture = FindPicture(location);
- if (picture ==