基于Visual C#2010开发Windows7应用 多点触摸图片处理应用程序(2)-使用多点触摸操作处理图片 .

到目前为止,使用触摸事件处理图片与使用鼠标功能并没有太大区别。下面我们将:

•               添加使用多个手指操作图片的能力

•               同时平移、缩放和旋转图片

•               同时操作多张图片

我们已经知道如何将正确的事件分派给相应的 PictureTracker,但我们还不知道如何决定在发生多个事件之后需要采取的操作。这正是 Windows 7 多点触摸机制的用武之地。它拥有一个操作处理器来使用触摸 ID 事件并生成合适的操作事件。您只需实例化一个操作处理器,注册其事件,并为它提供触摸 ID + 位置事件对。 

操作处理器是一个 COM 对象。要在 .NET 中使用它,可以使用 Windows 7 Integration Library 示例。ManipulationProcessor .NET 包装器类构造函数获得一个枚举值,该值告诉它要报告哪些操作。在我们的示例中,我们希望报告所有操作。该处理器有 3 个事件:ManipulationStarted、ManipulationCompleted 和 ManipulationDelta。ManipulationDelta 是我们所关注的事件。它提供了平移、旋转和缩放的偏移量。

1.            更改整个 PictureTracker 类。

[c-sharp] view plain copy print ?
  1. class PictureTracker  
  2.   
  3. {  
  4.   
  5.     private readonly ManipulationProcessor _processor =  
  6.   
  7.         new ManipulationProcessor(ProcessorManipulations.ALL);  
  8.   
  9.     public PictureTracker()  
  10.   
  11.     {  
  12.   
  13.         _processor.ManipulationStarted += (s, e) =>  
  14.   
  15.         {  
  16.   
  17.             System.Diagnostics.Trace.WriteLine("Manipulation has started: " + Picture.ImagePath);  
  18.   
  19.         };  
  20.   
  21.         _processor.ManipulationCompleted += (s, e) =>   
  22.   
  23.         {  
  24.   
  25.             System.Diagnostics.Trace.WriteLine("Manipulation has completed: " + Picture.ImagePath);  
  26.   
  27.         };  
  28.   
  29.         _processor.ManipulationDelta += ProcessManipulationDelta;  
  30.   
  31.     }  
  32.   
  33.     public Picture Picture { getset; }  
  34.   
  35.     public void ProcessDown(int id, Point location)  
  36.   
  37.     {  
  38.   
  39.         _processor.ProcessDown((uint)id, location.ToDrawingPointF());  
  40.   
  41.     }  
  42.   
  43.     public void ProcessMove(int id, Point location)  
  44.   
  45.     {  
  46.   
  47.         _processor.ProcessMove((uint)id, location.ToDrawingPointF());  
  48.   
  49.     }  
  50.   
  51.     public void ProcessUp(int id, Point location)  
  52.   
  53.     {  
  54.   
  55.         _processor.ProcessUp((uint)id, location.ToDrawingPointF());  
  56.   
  57.     }  
  58.   
  59.     //Update picture state   
  60.   
  61.     private void ProcessManipulationDelta(object sender, ManipulationDeltaEventArgs e)  
  62.   
  63.     {  
  64.   
  65.         if (Picture == null)  
  66.   
  67.             return;  
  68.   
  69.         Picture.X += e.TranslationDelta.Width;  
  70.   
  71.         Picture.Y += e.TranslationDelta.Height;  
  72.   
  73.         Picture.Angle += e.RotationDelta * 180 / Math.PI;  
  74.   
  75.         Picture.ScaleX *= e.ScaleDelta;  
  76.   
  77.         Picture.ScaleY *= e.ScaleDelta;  
  78.   
  79.     }  
  80.   
  81. }  

2.            将以下命名空间指令添加到 PictureTracker 类中:

[c-sharp] view plain copy print ?
  1. using Windows7.Multitouch.Manipulation;  
  2.   
  3. using Windows7.Multitouch.WPF;  
  4.   
  5. Visual Basic  
  6.   
  7. Imports Windows7.Multitouch.Manipulation  
  8.   
  9. Imports Windows7.Multitouch.WPF  

注意: 通过添加此命名空间,可以使用 ManipulatorProcessor 类和 System.Windows.Point 扩展方法 ToDrawingPointF。


3.            我们实例化了一个新的 ManipulationProcessor,注册了事件处理器,而且最重要的是,通过更新图片用户控件处理了 ManipulationDelta 事件。现在我们需要对 PictureTrackerManager 事件处理代码稍作修改,并转发触摸 ID 和触摸位置。ManipulationProcessor 需要将触摸 ID 作为操作流程的输入。更改 PictureTrackerManager 中的以下代码:

[c-sharp] view plain copy print ?
  1. public void ProcessDown(object sender, StylusEventArgs args)  
  2.   
  3. {  
  4.   
  5.     Point location = args.GetPosition(_canvas);  
  6.   
  7.     PictureTracker pictureTracker = GetPictureTracker(args.StylusDevice.Id, location);  
  8.   
  9.     if (pictureTracker == null)  
  10.   
  11.         return;  
  12.   
  13.     pictureTracker.ProcessDown(args.StylusDevice.Id, location);  
  14.   
  15. }  
  16.   
  17. public void ProcessUp(object sender, StylusEventArgs args)  
  18.   
  19. {  
  20.   
  21.     Point location = args.GetPosition(_canvas);  
  22.   
  23.     PictureTracker pictureTracker = GetPictureTracker(args.StylusDevice.Id);  
  24.   
  25.     if (pictureTracker == null)  
  26.   
  27.         return;  
  28.   
  29.     pictureTracker.ProcessUp(args.StylusDevice.Id, location);  
  30.   
  31.     _pictureTrackerMap.Remove(args.StylusDevice.Id);  
  32.   
  33. }  
  34.   
  35. public void ProcessMove(object sender, StylusEventArgs args)  
  36.   
  37. {  
  38.   
  39.     PictureTracker pictureTracker = GetPictureTracker(args.StylusDevice.Id);  
  40.   
  41.     if (pictureTracker == null)  
  42.   
  43.          return;  
  44.   
  45.      Point location = args.GetPosition(_canvas);  
  46.   
  47.      pictureTracker.ProcessMove(args.StylusDevice.Id, location);  
  48.   
  49. }  

4.            编译并运行代码。尝试同时操作多张图片。

               

任务 6 – 添加 PictureTracker 缓存

 

当用户首次触摸一张图片时,应用程序创建一个新 PictureTracker 实例,该实例然后创建 ManipulationProcessor COM 对象。只要用户移开触摸该图片的最后一个指头(触摸 ID),PictureTracker 实例就会被当作垃圾收集,进而释放底层 COM 对象。分析常见的应用程序使用情形就会发现,只有少数图片可能被同时操作。据此可以得出结论:我们需要 PictureTracker 实例的一个缓存。该缓存将包含空闲的 PictureTracker 实例。当(发生 ProcessDown 事件时)需要新 PictureTracker 实例时,我们将首先尝试从缓存拉取实例,只有当缓存为空时才生成新实例。当完成对图片的操作时,我们将 PictureTracker 实例移入缓存。因为 ManipulationCompleted 是一个 ManipulationProcessor 事件,所以我们将要求 PictureTracker 处理该事件并将其转发给 PictureTrackerManager。这需要一个从 PictureTracker 到它的 PictureTrackerManager 的新引用(我们使用构造函数来传递该引用)。

1.            将堆栈数据成员添加到 PictureTrackerManager 类的开头:

[c-sharp] view plain copy print ?
  1. class PictureTrackerManager  
  2.   
  3. {  
  4.   
  5.     //Cache for re-use of picture trackers    
  6.   
  7.     private readonly Stack<PictureTracker> _pictureTrackers = new Stack<PictureTracker>();  
  8.   
  9. ...  

2.            更改 GetPictureTracker() 函数。我们需要使用缓存,还需要将此引用传递给 PictureTracker 构造函数:

[c-sharp] view plain copy print ?
  1. private PictureTracker GetPictureTracker(int touchId, Point location)  
  2.   
  3. {  
  4.   
  5. ...  
  6.   
  7.    //First time   
  8.   
  9.    if (pictureTracker == null)  
  10.   
  11.     {  
  12.   
  13.         //take from stack   
  14.   
  15.         if (_pictureTrackers.Count > 0)  
  16.   
  17.             pictureTracker = _pictureTrackers.Pop();  
  18.   
  19.         else //create new   
  20.   
  21.             pictureTracker = new PictureTracker(this);  
  22.   
  23.           
  24.   
  25.         pictureTracker.Picture = picture;  
  26.   
  27.         BringPictureToFront(picture);  
  28.   
  29.     }  
  30.   
  31. ...  
  32.   
  33. }  

3.        添加一个逻辑,以在操作完成时将 PictureTracker 实例推回堆栈中。将以下代码粘贴到 PictureTrackerManager 类中。

[c-sharp] view plain copy print ?
  1. C#  
  2.   
  3. //Manipulation is completed, we can reuse the object   
  4.   
  5. public void Completed(PictureTracker pictureTracker)  
  6.   
  7. {  
  8.   
  9.     pictureTracker.Picture = null;  
  10.   
  11.     _pictureTrackers.Push(pictureTracker);  
  12.   
  13. }  

4.            现在需要更改 PictureTracker 类,使其适应 PictureTrackerManager 中的代码更改。

a.            将 PictureTrackerManager 实例获取到构造函数中,然后存储它。

[c-sharp] view plain copy print ?
  1. class PictureTracker  
  2.   
  3. {  
  4.   
  5.     private readonly ManipulationProcessor _processor =  
  6.   
  7.         new ManipulationProcessor(ProcessorManipulations.ALL);  
  8.   
  9.     private readonly PictureTrackerManager _pictureTrackerManager;  
  10.   
  11.     public PictureTracker(PictureTrackerManager pictureTrackerManager)  
  12.   
  13.     {  
  14.   
  15.         _pictureTrackerManager = pictureTrackerManager;  
  16.   
  17. ...  

b.            在 ManipulationCompleted 事件中调用 PictureTrackerManager.Completed 函数:

[c-sharp] view plain copy print ?
  1. public PictureTracker(PictureTrackerManager pictureTrackerManager)  
  2.   
  3. {  
  4.   
  5.     _pictureTrackerManager = pictureTrackerManager;  
  6.   
  7.     _processor.ManipulationCompleted += (s, e) =>   
  8.   
  9.     {  
  10.   
  11.         System.Diagnostics.Trace.WriteLine("Manipulation has completed: " + Picture.ImagePath);  
  12.   
  13.         _pictureTrackerManager.Completed(this);  
  14.   
  15.     };  
  16.   
  17. ...  

5.            编译并运行!

添加惯性

 

只剩最后一项任务了。使用缩放、平移和旋转操作可以提供一种自然的用户体验。在实际生活中,当推动一个物体,然后松开手时,该物体会继续移动,直到因为无法克服摩擦力而停止。可以使用 Inertia 让我们的图片对象拥有相同的行为。Windows 7 多点触摸子系统提供了一个 InertiaProcessor COM 对象。InertiaProcessor 可以发起与 ManipulationProcessor 相同的操作事件。Windows 7 Integration Library 示例提供了一个包装器,它将操作处理器和惯性处理器捆绑在一起。ManipulationInertiaProcessor 可以替代 ManipulationProcessor 并提供额外的 InertiaProcessor 属性来公开 InertiaProcessor 功能。要发起更多事件,ManipulationInertiaProcessor 需要一个计时器。为了克服线程的 UI 相似性问题,我们最好拥有一个基于 GUI 的计时器。Windows 7 Integration Library 可以为我们创建这样的计时器。

当用户的最后一个手指离开图片对象时,ManipulationInertiaProcessor 会发起 OnBeforeInertia 事件。在这里设置 Inertia 开始参数。可以选择一个默认的开始速度,或者跟踪当前的对象速度并从中提取出速度数字。

1.            我们想要跟踪对象的平移、旋转和缩放速度。将以下类添加到 PictureTracker 类中:

 

 

[c-sharp] view plain copy print ?
  1. //Keep track of object velocities   
  2.   
  3. private class InertiaParam  
  4.   
  5. {  
  6.   
  7.     public VectorF InitialVelocity { getset; }  
  8.   
  9.     public float InitialAngularVelocity { getset; }  
  10.   
  11.     public float InitialExpansionVelocity { getset; }  
  12.   
  13.     public System.Diagnostics.Stopwatch _stopwatch = new System.Diagnostics.Stopwatch();  
  14.   
  15.     public void Reset()  
  16.   
  17.     {  
  18.   
  19.         InitialVelocity = new VectorF(0, 0);  
  20.   
  21.         InitialAngularVelocity = 0;  
  22.   
  23.         InitialExpansionVelocity = 0;  
  24.   
  25.         _stopwatch.Reset();  
  26.   
  27.         _stopwatch.Start();  
  28.   
  29.     }  
  30.   
  31.     public void Stop()  
  32.   
  33.     {  
  34.   
  35.         _stopwatch.Stop();  
  36.   
  37.     }  
  38.   
  39.     //update velocities, velocity = distance/time   
  40.   
  41.     public void Update(ManipulationDeltaEventArgs e, float history)  
  42.   
  43.     {  
  44.   
  45.         float elappsedMS = (float)_stopwatch.ElapsedMilliseconds;  
  46.   
  47.         if (elappsedMS == 0)  
  48.   
  49.             elappsedMS = 1;  
  50.   
  51.         InitialVelocity = InitialVelocity * history + ((VectorF)e.TranslationDelta * (1F - history)) / elappsedMS;  
  52.   
  53.         InitialAngularVelocity = InitialAngularVelocity * history + (e.RotationDelta * (1F - history)) / elappsedMS;  
  54.   
  55.         InitialExpansionVelocity = InitialExpansionVelocity * history + (e.ExpansionDelta * (1F - history)) / elappsedMS;  
  56.   
  57.         _stopwatch.Reset();  
  58.   
  59.         _stopwatch.Start();  
  60.   
  61.     }  
  62.   
  63. }  

2.            将 OnBeforeInertia() 事件处理程序添加到 PictureTracker 类中:

[c-sharp] view plain copy print ?
  1. //Fingers removed, start inertia   
  2.   
  3. void OnBeforeInertia(object sender, BeforeInertiaEventArgs e)  
  4.   
  5. {  
  6.   
  7.     //Tell the tracker manager that the user removed the fingers   
  8.   
  9.     _pictureTrackerManager.InInertia(this);  
  10.   
  11.   
  12.     _processor.InertiaProcessor.InertiaTimerInterval = 15;  
  13.   
  14.     _processor.InertiaProcessor.MaxInertiaSteps = 500;  
  15.   
  16.     _processor.InertiaProcessor.InitialVelocity = _inertiaParam.InitialVelocity;  
  17.   
  18.     _processor.InertiaProcessor.DesiredDisplacement = _inertiaParam.InitialVelocity.Magnitude * 250;  
  19.   
  20.     _processor.InertiaProcessor.InitialAngularVelocity = _inertiaParam.InitialAngularVelocity * 20F / (float)Math.PI;  
  21.   
  22.     _processor.InertiaProcessor.DesiredRotation = Math.Abs(_inertiaParam.InitialAngularVelocity *   
  23.   
  24.     _processor.InertiaProcessor.InertiaTimerInterval * 540F / (float)Math.PI);  
  25.   
  26.     _processor.InertiaProcessor.InitialExpansionVelocity = _inertiaParam.InitialExpansionVelocity * 15;  
  27.   
  28.     _processor.InertiaProcessor.DesiredExpansion = Math.Abs(_inertiaParam.InitialExpansionVelocity * 4F);  
  29.   
  30. }  

3.            更改 PictureTracker 类,创建 ManipulationInertiaProcessor 并注册 OnBeforeInertia 事件:

 

[c-sharp] view plain copy print ?
  1. /// <summary>   
  2.   
  3. /// Track a single picture   
  4.   
  5. /// </summary>   
  6.   
  7. class PictureTracker  
  8.   
  9. {  
  10.   
  11. ...  
  12.   
  13.     //Calculate the Inertia start velocity   
  14.   
  15.     private readonly InertiaParam _inertiaParam = new InertiaParam();  
  16.   
  17.     private readonly ManipulationInertiaProcessor _processor = new ManipulationInertiaProcessor(ProcessorManipulations.ALL, Factory.CreateTimer());  
  18.   
  19.     public PictureTracker(PictureTrackerManager pictureTrackerManager)  
  20.   
  21.     {  
  22.   
  23.         _pictureTrackerManager = pictureTrackerManager;  
  24.   
  25.         //Start inertia velocity calculations   
  26.   
  27.         _processor.ManipulationStarted += (s, e) =>  
  28.   
  29.         {  
  30.   
  31.             _inertiaParam.Reset();  
  32.   
  33.         };  
  34.   
  35.         //All completed, inform the tracker manager that the current tracker   
  36.   
  37.         //can be reused   
  38.   
  39.         _processor.ManipulationCompleted += (s, e) =>   
  40.   
  41.         {   
  42.   
  43.             _inertiaParam.Stop();   
  44.   
  45.             pictureTrackerManager.Completed(this);   
  46.   
  47.         };  
  48.   
  49.         _processor.ManipulationDelta += ProcessManipulationDelta;  
  50.   
  51.         _processor.BeforeInertia += OnBeforeInertia;  
  52.   
  53.     }  
  54.   
  55. ...  

4.        我们还需要更改 PictureTrackerManager。在新的条件下,图片可能由惯性处理器使用,即使没有手指在触摸该对象。我们需要在操作完成时立即从映射中删除触摸 ID,但是只有在惯性处理器使图片完全停止时,我们才能够重用 PictureTracker 对象。将 InInertia() 函数添加到 PictureTrackerManager 类中:

 

 

[c-sharp] view plain copy print ?
  1. //We remove the touchID from the tracking map since the fingers are    
  2.   
  3. //no longer touching the picture   
  4.   
  5. public void InInertia(PictureTracker pictureTracker)  
  6.   
  7. {  
  8.   
  9.     //remove all touch id from the map   
  10.   
  11.     foreach (int id in  
  12.   
  13.         (from KeyValuePair<int, PictureTracker> entry in _pictureTrackerMap  
  14.   
  15.          where entry.Value == pictureTracker  
  16.   
  17.          select entry.Key).ToList())  
  18.   
  19.     {  
  20.   
  21.         _pictureTrackerMap.Remove(id);  
  22.   
  23.     }  
  24.   
  25. }  

5.            编译并运行。尝试将图片拉出屏幕。试验各种 Inertia 参数,看它们如何更改图片行为。

 

在此改进了一个基于鼠标的简单图片处理应用程序,将它升级成了类似于 Surface 的成熟的图片操作应用程序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值