MSDN 杂志:UI 前沿技术 - WPF 中的多点触控操作事件

原文  MSDN 杂志:UI 前沿技术 - WPF 中的多点触控操作事件

UI 前沿技术

WPF 中的多点触控操作事件

Charles Petzold

下载代码示例

就在过去几年,多点触控还只是科幻电影中表现未来主义的一种重要手法,现在俨然已经成为主流的用户界面技术。 多点触控显示屏现在成了新型智能手机和 Tablet 计算机的标准显示屏。 此外,它还可能在公共场所的计算机上普及,例如 Microsoft Surface 率先开发的网亭或桌面计算机。

实际存在的唯一不确定因素是多点触控在常规台式计算机上的普及。 这种普及的最大障碍或许是长时间在垂直屏幕上移动手指所产生的疲劳(称为“大猩猩手臂”)。 我个人希望多点触控的强大功能将切实推进桌面显示屏的重新设计。 我们可以设想台式计算机的显示屏可能类似于配置制图桌,并且可能和制图桌一样大。

但那可能发生在遥远的未来。 目前,开发人员需要掌握新的 API。 Windows 7 中的多点触控支持已通过低级别和高级别的接口渗透并应用到 Microsoft .NET Framework 的各个领域。

了解多点触控支持

如果您考虑到在显示屏上使用多根手指可能引起表达的复杂性,您或许就会了解为何到现在还没有人确切知道多点触控的“正确”编程接口。 这需要一定时间。 同时,您具有若干选择。

Windows Presentation Foundation (WPF) 4.0 为在 Windows 7 下运行的程序提供了两个多点触控接口。 为了专门使用多点触控,程序员希望探索低级别接口,该接口包含由 UIElement 定义的多个路由事件(名为 TouchDown、TouchMove、TouchUp、TouchEnter 和 TouchLeave)以及向下、移动和向上事件的预览版本。 显然,这些事件是根据鼠标事件建模的,但需要一个整数 ID 属性来跟踪显示屏上的多根手指。 Microsoft Surface 在 WPF 3.5 的基础上构建,不过它支持范围更广的低级别触控接口,可区分触控输入的类型和形状。

本专栏的主题是 WPF 4.0 中的高级别多点触控支持,它包含一个名称以“Manipulation”一词开头的事件的集合。 这些操作事件执行多个关键的多点触控作业:

  • 将两根手指的交互合并成单个操作
  • 将一根或两根手指的移动解析成转换
  • 在手指离开屏幕时实现延时

Silverlight 4 文档中列出了部分操作事件,但可能会让读者产生一丝迷惑。 Silverlight 本身不支持这些事件,但针对 Windows Phone 7 编写的 Silverlight 应用程序则支持这些事件。 图 1 列出了这些操作事件。

图 1 Windows Presentation Foundation 4.0 中的操作事件


event

是否受 Windows Phone 7 支持?
ManipulationStarting 不能
ManipulationStarted
ManipulationDelta
ManipulationInertiaStarted 不能
ManipulationBoundaryFeedback
ManipulationCompleted

 

基于 Web 的 Silverlight 4 应用程序将继续使用 Touch.FrameReported 事件,我曾在 2010 年 3 月出版的 MSDN 杂志“手指之舞:探讨 Silverlight 中的多点触控支持”一文中探讨过该事件。

除操作事件本身以外,WPF 中的 UIElement 类还支持与操作事件对应的可覆盖方法,例如,On­ManipulationStarting。
在 Silverlight for Windows Phone 7 中,这些可覆盖方法由 Control 类定义。

多点触控示例

照片查看器可能是多点触控的典型应用,在照片查看器中,您可以在一个平面上移动照片,用两根手指放大或缩小照片以及旋转照片。
这些操作有时称为平移、缩放和旋转,它们分别对应于平移、缩放和旋转的标准图形转换。

很明显,照片查看程序需要维护照片集合,支持添加新照片和删除照片,并且最好能始终在一个较小的图形帧中显示多张照片,但我准备忽略所有这些方面,而着重介绍多点触控交互。
有了操作事件,一切都变得非常简单,这让我感到非常吃惊,我相信你们也会有同感。

本专栏的所有源代码位于一个名为 WpfManipulationSamples 的可下载解决方案中。
第一个项目是 SimpleManipulationDemo,MainWindow.xaml 文件在图 2 中显示。

图 2 SimpleManipulationDemo 的 XAML 文件

 

<Window x:Class="SimpleManipulationDemo.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Simple Manipulation Demo">

  <Window.Resources>
    <Style TargetType="Image">
      <Setter Property="Stretch" Value="None" />
      <Setter Property="HorizontalAlignment" Value="Left" />
      <Setter Property="VerticalAlignment" Value="Top" />
    </Style>
  </Window.Resources>

  <Grid>
    <Image Source="Images/112-1283_IMG.JPG"
      IsManipulationEnabled="True"
      RenderTransform="0.5 0 0 0.5 100 100" />

    <Image Source="Images/139-3926_IMG.JPG"
      IsManipulationEnabled="True"
      RenderTransform="0.5 0 0 0.5 200 200" />

    <Image Source="Images/IMG_0972.JPG"
      IsManipulationEnabled="True"
      RenderTransform="0.5 0 0 0.5 300 300" />

    <Image Source="Images/IMG_4675.JPG"
      IsManipulationEnabled="True"
      RenderTransform="0.5 0 0 0.5 400 400" />
  </Grid>
  </Window>

首先,请注意所有三个 Image 元素上的设置:

 

IsManipulationEnabled="True"

默认情况下,此属性为 false。 对于您希望在其上获得多点触控输入并生成操作事件的任何元素,您必须将其设置为 true。

操作事件是 WPF 路由事件,这意味着这些事件会使可视化树浮现出来。 在 此程序中,Grid 和 MainWindow 的 IsManipulationEnabled 属性均未设置为 true,但您仍可将操作事件的处理程序附加至 Grid 和 MainWindow 元素,或者在 MainWindow 类中覆盖 OnManipulation 方法。

另请注意,每个 Image 元素将其 Render­Transform 设置为一个六位数的字符串:

 

RenderTransform="0.5 0 0 0.5 100 100"

这是设置已初始化的 MatrixTransform 对象的 RenderTransform 属性的快捷方式。 在此特定示例中,设置为 MatrixTransform 的 Matrix 对象已经过初始化,可执行 0.5 个单位的缩放(使照片缩小至实际大小的一半)和朝右下方的 100 个单位的平移。 该窗口的代码隐藏文件会访问并修改此 MatrixTransform。

图 3 显示了完整的 MainWindow.xaml.cs 文件,该文件仅覆盖两个方法,即 OnManipulationStarting 和 OnManipulationDelta。 这些方法处理由 Image 元素生成的操作。

图 3 SimpleManipulationDemo 的代码隐藏文件

 

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace SimpleManipulationDemo {
  public partial class MainWindow : Window {
    public MainWindow() {
      InitializeComponent();
    }

    protected override void OnManipulationStarting(
      ManipulationStartingEventArgs args) {

      args.ManipulationContainer = this;

      // Adjust Z-order
      FrameworkElement element =
        args.Source as FrameworkElement;
      Panel pnl = element.Parent as Panel;

      for (int i = 0; i < pnl.Children.Count; i++)
        Panel.SetZIndex(pnl.Children[i],
          pnl.Children[i] ==
          element ? pnl.Children.Count : i);

      args.Handled = true;
      base.OnManipulationStarting(args);
    }

    protected override void OnManipulationDelta(
      ManipulationDeltaEventArgs args) {

      UIElement element = args.Source as UIElement;
      MatrixTransform xform =
        element.RenderTransform as MatrixTransform;
      Matrix matrix = xform.Matrix;
      ManipulationDelta delta = args.DeltaManipulation;
      Point center = args.ManipulationOrigin;

      matrix.ScaleAt(
        delta.Scale.X, delta.Scale.Y, center.X, center.Y);
      matrix.RotateAt(
        delta.Rotation, center.X, center.Y);
      matrix.Translate(
        delta.Translation.X, delta.Translation.Y);
      xform.Matrix = matrix;

      args.Handled = true;
      base.OnManipulationDelta(args);
    }
  }
  }

操作基础知识

操作定义为一根或多根手指触控特定元素的动作。 完整的操作从 Manipulation­Starting 事件开始,紧接着是 ManipulationStarted,并最终以 ManipulationCompleted 结束。 中间可能有多个 ManipulationDelta 事件。

每个操作事件都附带有其自己的事件参数集,该参数集封装在一个根据该事件命名并附加了 EventArgs 的类中,例如,ManipulationStartingEventArgs 和 ManipulationDeltaEventArgs。 这些类从我们熟悉的 InputEventArgs 派生,而后者又从 RoutedEvent­Args 派生。 这些类包括指示事件来源的 Source 和 OriginalSource 属性。

在 SimpleManipulationDemo 程序中,Source 和 Original­Source 均设置为生成操作事件的 Image 元素。 只有 IsManipulation­Enabled 属性设置为 true 的元素才会在这些操作事件中显示为 Source 和 OriginalSource 属性。

此外,与操作事件相关联的每个事件参数类都包括一个名为 Manipulation­Container 的属性。 这是发生多点触控操作的元素。 操作事件中的所有坐标都相对于此容器。

默认情况下,ManipulationContainer 属性设置为与 Source 和 OriginalSource 属性相同的元素,也就是被操作的元素,不过这可能不是您所希望的。 通常,大家不希望操作容器与被操作的元素相同,因为动态移动、缩放和旋转报告触控信息的同一元素需要技巧性很强的交互。 您应将操作容器作为被操作元素的父项,或者作为沿可视化树向上追寻的某个元素。

在大多数操作事件中,ManipulationContainer 属性都是只读属性。 但元素接收的第一个操作事件例外。 在 ManipulationStarting 中,您可以将 ManipulationContainer 更改为更适合的容器。 在 SimpleManipulationDemo 项目中,此工作只需通过一行代码即可完成:

 

args.ManipulationContainer = this;

在所有后续事件中,ManipulationContainer 将是 MainWindow 元素,而不是 Image 元素,并且所有坐标都将相对于该窗口。 由于包含 Image 元素的 Grid 也与该窗口对齐,因此,此方法非常适用。

OnManipulationStarting 方法的其余部分通过重置该 Grid 中所有 Image 元素的 Panel.ZIndex 附加属性,专门用于在前台显示触控 Image 元素。 这是处理 ZIndex 的一种简单方法,但可能不是最好方法,因为它会发生突然变化。

ManipulationDelta 和 DeltaManipulation

SimpleManpulationDemo 处理的另一个唯一事件是 ManipulationDelta。 ManipulationDeltaEventArgs 类定义 ManipulationDelta 类型的两个属性。 (是的,该事件和类具有相同的名称。)这些属性是 DeltaManipulation 和 CumulativeManipulation。 顾名思义,DeltaManipulation 反映了自上一个 ManipulationDelta 事件以来发生的操作,CumulativeManipulation 表示从 ManipulationStarting 事件开始的完整操作。

ManipulationDelta 具有四个属性:

  • Translation 属性,类型为 Vector
  • Scale 属性,类型为 Vector
  • Expansion 属性,类型为 Vector
  • Rotation 属性,类型为 double

Vector 结构定义 double 类型的两个属性,分别名为 X 和 Y。 Silverlight for Windows Phone 7 中的操作支持的一个较显著的差异是缺少 Expansion 和 Rotation 属性。

Translation 属性指示水平方向和垂直方向的移动(或平移)。 对元素的单指操作可生成平移变化,但平移也可以是其他操作的一部分。

Scale 和 Expansion 属性均指示大小变化(缩放),这始终需要两根手指。 Scale 依据乘法进行缩放,Expansion 依据加法进行缩放。 使用 Scale 可设置缩放转换;使用 Expansion 可按照与设备无关的单位增大或减小某个元素的 Width 和 Height 属性。

在 WPF 4.0 中,Scale 矢量的 X 和 Y 值始终是相同的。 操作事件不会提供足够的信息以各向异性的方式(即,在水平方向和垂直方向各不相同)缩放元素。

默认情况下,旋转也需要两根手指,但我们将在稍后介绍如何启用单指旋转。 在任何特定 ManipulationDelta 事件中,可能需要设置所有四个属性。 可使用两根手指放大某个元素,同时旋转该元素并将其移动到另一位置。

缩放和旋转始终相对于某个特定的中心点。 Point 类型的 ManipulationOrigin 属性的 ManipulationDeltaEvent­Args 中也提供了此中心。 此原点相对于在 ManipulationStarting 事件中设置的 ManipulationContainer 而言。

您在 ManipulationDelta 事件中的工作是按以下顺序根据增量值修改被操作对象的 Render­Transform 属性:首先缩放,然后旋转,最后平移。 (事实上,由于水平和垂直缩放比例是相同的,您可以切换缩放转换和旋转转换的顺序,得到的结果仍然相同。)

图 3 中的 OnManipulationDelta 方法显示了一种标准方法。 Matrix 对象从操作的 Image 元素上设置的 MatrixTransform 获取。 该对象通过调用 ScaleAt、RotateAt(二者相对于 ManipulationOrigin)和 Translate 进行修改。 Matrix 是一个结构而不是类,因此您必须用新值替换 MatrixTransform 中的旧值,以此作为结束。

此代码可略作更改。 如下所示,它使用以下语句围绕一个中心进行缩放:

 

matrix.ScaleAt(delta.Scale.X, delta.Scale.Y, center.X, center.Y);

这相当于平移到中心点的相反方向、进行缩放,然后重新平移:

 

matrix.Translate(-center.X, -center.Y);
matrix.Scale(delta.Scale.X, delta.Scale.Y);
matrix.Translate(center.X, center.Y);

同样,RotateAt 方法可以替换为:

 

matrix.Translate(-center.X, -center.Y);
matrix.Rotate(delta.Rotation);
matrix.Translate(center.X, center.Y);

两个相邻的 Translate 调用现在相互抵消,因此最终合成结果为:

 

matrix.Translate(-center.X, -center.Y);
matrix.Scale(delta.Scale.X, delta.Scale.Y);
matrix.Rotate(delta.Rotation);
matrix.Translate(center.X, center.Y);

以上方法的效率可能更高。

图 4 显示了运行中的 SimpleManipulationDemo 程序。

图 4 SimpleManipulationDemo 程序

是否启用容器?

SimpleManpulationDemo 程序的一个有趣功能是您可以同时操作两个甚至更多的 Image 元素,条件是您具备相应的硬件支持和足够多的手指。 每个 Image 元素生成其自己的 ManipulationStarting 事件及其自己的 Manipulation­Delta 事件系列。 代码通过事件参数的 Source 属性有效地区分多个 Image 元素。

因此,很重要的一点是不要在字段中设置暗示一次只能操作一个元素的任何状态信息。

由于每个 Image 元素都将自己的 IsManipulationEnabled 属性设置为 true,因此可以同时操作多个元素。 其中每个元素都可以生成唯一的操作事件系列。

当首次处理这些操作事件时,您可能需要深入研究是在 MainWindow 类还是充当容器的其他元素上将 IsManpulationEnabled 设置为 true。 此功能并非不可以实现,但在实际操作时略显复杂,并且也不是那么强大。 唯一的实际优点是:您不必在 ManipulationStarting 事件中设置 ManipulationContainer 属性。 当您必须在 ManipulatedStarted 事件中使用 ManipulationOrigin 属性对子元素进行点击测试,以确定正在操作哪个元素时,麻烦随之而来。

接下来,您需要将正在操作的元素存储为字段,以便在将来的 ManipulationDelta 事件中使用。 在这种情况下,由于您一次只能操作容器中的一个元素,因此完全可以将状态信息存储在字段中。

操作模式

如上所示,在 ManipulationStarting 事件期间设置的一个关键属性是 ManipulationContainer。 其他属性对于自定义特定操作非常有用。

您可以使用 Manipulation­Modes 枚举的成员初始化 Mode 属性,从而限制可执行操作的类型。 例如,如果您将操作专用于水平滚动,则可能需要将事件仅限制为水平平移。 ManipulationModesDemo 程序通过显示列出各选项的 RadioButton 元素的列表,使您可以动态地设置模式,如图 5 所示。

图 5 ManipulationModeDemo 显示

当然,RadioButton 是 WPF 4.0 中直接响应触控的众多控件之一。

单指旋转

默认情况下,您需要两根手指才能旋转对象。 不过,如果真实照片位于真实桌面上,您可以将手指放在角上,并将其旋转一圈。 旋转大致上是围绕对象中心进行的。

您可以设置 ManipulationStartingEventArgs 的 Pivot 属性,对操作事件执行此操作。 默认情况下,Pivot 属性为 null;通过设置 ManipulationPivot 对象的该属性,可以启用单指旋转。 ManipulationPivot 的关键属性
是 Center,您可能会考虑将其作为操作元素的中心来计算:

 

Point center = new Point(element.ActualWidth / 2,
                         element.ActualHeight / 2);

不过,此中心点必须相对于操作容器而言,在我向大家展示的程序中,这一容器就是处理事件的元素。 将该中心点从操作元素平移到容器非常简单:

 

center = element.TranslatePoint(center, this);

还需要设置另一条小小的信息。 如果您仅指定中心点,当您将手指恰好放在元素中心时,将会出现问题:丝毫的移动都会导致该元素疯狂地旋转! 因此,ManipulationPivot 还具有 Radius 属性。 如果手指位于中心点的半径单位内,将不会发生旋转。 ManipulationPivotDemo 程序将该半径设置为半英寸:

 

args.Pivot = new ManipulationPivot(center, 48);

现在,单根手指便可执行旋转和平移的组合操作。

深入介绍

至此,本文已介绍了使用 WPF 4.0 操作事件的基础知识。 当然,这些技术存在一些变化,我将在后续专栏中陆续为大家介绍,此外还将介绍操作延时的强大功能。

您还可以看看 Surface Toolkit for Windows Touch,该页为您的应用程序提供了触控优化控件。 特别是有了 ScatterView 控件,就不再需要对诸如操作照片等基本任务直接使用操作事件。 该控件包含一些新效果和行为,可确保您的应用程序的行为与其他触控应用程序相同。

 

Charles Petzold 是《MSDN 杂志》的长期特约编辑。 他目前正在撰写《Programming Windows Phone 7》,该书将在 2010 年秋季作为可免费下载的电子书发布。 现在,已通过其网站 charlespetzold.com 提供了预览版本。

衷心感谢以下技术专家对本文的审阅:Doug Kramer、Robert Levy 和 Anson Tsao

时间: 2024-07-30 15:23:41

MSDN 杂志:UI 前沿技术 - WPF 中的多点触控操作事件的相关文章

UI前沿技术-WPF中的多点触控操作事件

就在过去几年,多点触控还只是科幻电影中表现未来主义的一种重要手法,现在俨然已经成为主流的用户界面技术.多点触控显示屏现在成了新型智能手机和 Tablet 计算机的标准显示屏.此外,它还可能在公共场所的计算机上普及,例如 Microsoft Surface 率先开发的网亭或桌面计算机. 实际存在的唯一不确定因素是多点触控在常规台式计算机上的普及.这种普及的最大障碍或许是长时间在垂直屏幕上移动手指所产生的疲劳(称为"大猩猩手臂").我个人希望多点触控的强大功能将切实推进桌面显示屏的重新设计

手指之舞 – 探讨Silverlight中的多点触控支持

每次去纽约的美国自然历史博物馆,我必定会好好参观一下灵长类馆.灵长类馆选择了大量的骨骼和剥制标本,展示了一幅灵长类动物进化的全景,动物从极小的树鼩.狐猴和绒猴一直到黑猩猩.大猩猩和人类. 这个展览最引人注目的是所有灵长类动物都有着惊人的共性:手的骨骼结构相同,包括一个对生拇指.这种使我们的祖先和远亲可以抓握从而爬上树枝的相同关节排列和数目,让我们的物种可以影响周围的世界和从事建造工作.我们的双手可能源于数百万年前小型灵长类动物的爪子,而双手也是使我们真正成为人类的重要因素. 我们会本能地伸出手指

ontouchevent-如何关闭在一个应用中的多点触控

问题描述 如何关闭在一个应用中的多点触控 我希望在这个应用中只支持单点触控,或者说屏蔽第二点及以上的触点.因为我的这个应用开发完成后出现一个问题,就是当前界面中同时点击多个控件都会响应.考虑过使用OnTouchEvent这个方法,可是又发现,那样的话,我需要把所有的控件都添加监听.这个工程量很大啊.有没有更好的方法呢?希望大家给指条明路......

UI前沿技术 – WPF应用程序中的MIDI音乐

每一台 PC 都包含一个内置的 16 人乐队,可播放一些音乐.人们不容易注意此乐队的成员,因为它 们表示的可能是 Windows 所支持的声音和视频功能阵列中利用最不充分的组件. 此 16 人乐队是在符合 MIDI(乐器数字接口)标准的硬件或软件中实现的电子音乐合成器.在 Win32 API 中,以单词 midiOut 开头的函数支持使用 MIDI 合成器播放音乐. MIDI 支持不是 .NET Framework 的一部分,但如果要在 Windows 窗体或 Windows Presentat

多点触摸-IE中多点触控如何禁止

问题描述 IE中多点触控如何禁止 现在有一台PC,硬件支持多点触控.安装win7 操作系统.现在安装了一个应用程序,控制层用vc++开发,页面部分用的是html和vb开发.应用程序启动时,浏览器会启动显示.现在希望在IE浏览器中禁止多点触控操作.其他应用程序中都依旧支持触控操作.具体需要做哪些处理呢?各路大神请不吝赐教~ 解决方案 这个最后暂定处理方式:win7 ie支持的四种触摸动作为:ontouchstart ontouchmove ontouchend ontouchcancel 用js实

传苹果要求Google不得使用多点触控技术

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 北京时间2月10日消息,据科技博客VentureBeat报道,日前有Google Android团队成员表示,苹果要求Google不在Android系统中使用多点触控技术,而Google对此表示同意. 首款Android手机HTC G1的触摸屏中并未使用多点触控技术,这引发了用户的不满和疑问.多点触控使用户可同时用多个手指操作触摸屏,支持各种

Android中多点触控以及手势的基础知识

现在一般的android手机都会使用电容触摸屏,所以基本上都会支持多点触控,同样在android系统中应用程序可以使用多点触控的事件来完成各种手势和场景需求,下面简单讲一下如何使用多点触控: 1.相关接口和事件介绍 MotionEvent.getAction() 对于单点触控,我们由MotionEvent.getAction()就可以得到以下几种事件:ACTION_DOWN.ACTION_UP等 MotionEvent.ACTION_MASK 而对于多点触控,我们需要由MotionEvent.g

Win8.1中IE11的触控体验

  解决触控实现悬停操作 随着触控技术和Windows 8的带动下,如今越来越多的PC开始配备触控屏,给传统PC领域带来了全新的交互体验--触控操作.但是目前有个略显尴尬的问题:除了专门针对触控操作优 化的Windows 8应用外,大量传统操作都是基于键盘和鼠标的,比如说我们最常进行的上网.现在的网站几乎都只考虑了鼠标和键盘操作,这就导致在使用触控方式访问网站时会 遇到操作障碍.从网站入手,重新制作兼容触控操作的网页是最彻底的解决方法,但实现难度比较大,因为网站数量是如此的多.另一个解决途径就是

Android多点触控技术实战,自由地对图片进行缩放和移动

在上一篇文章中我带着大家一起实现了Android瀑布流照片墙的效果,虽然这种效果很炫很酷,但其实还 只能算是一个半成品,因为照片墙中所有的图片都是只能看不能点的.因此本篇文章中,我们就来对这一功 能进行完善,加入点击图片就能浏览大图的功能,并且在浏览大图的时候还可以通过多点触控的方式对图片 进行缩放. 如果你还没有看过 Android瀑布流照片墙实现,体验不规则排列的美感 这篇文章,请尽 量先去阅读完再来看本篇文章,因为这次的代码完全是在上次的基础上进行开发的. 那我们现在就开 始动手吧,首先打