【问题标题】:Adding Windows 8 touch support to existing WinForms application向现有 WinForms 应用程序添加 Windows 8 触摸支持
【发布时间】:2012-10-29 20:10:20
【问题描述】:

我有一个面向 .NET Framework 4 的现有 Windows 窗体桌面应用程序,并希望为其添加 Windows 8 触摸支持。

目前该程序在 Windows 8 中运行良好,我可以调整一些元素的大小,使其在触摸设备上更加用户友好。但是,在数据网格上添加手势(例如捏合缩放)以及对其他元素的滑动支持将大大有助于使应用程序在纯触摸环境中更加现代。

我正在投资 Visual Studio 2012,它可以让我瞄准 .NET 4.5 和新的 Windows 8 功能,但是有谁知道任何可以帮助我更新应用程序的资源吗?我特别担心以下几点:

  • 无法在我的非触控开发机器上直接测试应用程序的触控功能。微软的模拟器似乎只支持 Metro 应用程序。我听说诸如 Splashtop 之类的平板电脑应用可以提供帮助(我有一台 Android 平板电脑),但还没有看到针对这种特定场景的任何具体内容
  • WinForms 应用程序是否支持手势。我是否必须将整个 UI 升级到 WPF 才能使其正常工作? (如果我真的走这条路,我相信我也可以针对 Windows 7,因为 WPF 4 支持多点触控)
  • 在运行时检测设备的触控支持并适当缩放/更改 UI,类似于 Microsoft 的 Windows RT Office 应用程序上的触控模式设置。我不想为了添加新功能而分叉项目
  • 自动测试触摸交互

这绝不是一份详尽的清单,但我非常感谢那些过去可能进行过类似升级的人提供的任何建议。

【问题讨论】:

  • 如果你的目标是 WinRT,你将不得不完全重写,因为 WinRT 没有等效的 WinForms。
  • @mydogisbox - 我的目标不是 WinRT,只是想升级现有的 WinForms 桌面应用程序以支持触摸。我应该在我的问题中更清楚地说明这一点。感谢您的意见。
  • [对于需要更新旧的 WinForms 应用以充分利用 Windows 10 触摸屏的任何人],请参阅我在Sheng Jiang's answer 下的评论以获取 Windows 7 SDK 获取手势示例的当前链接(@ 987654322@ 和 rotate)。已验证 C# 解决方案适用于运行 Windows 10 Pro 的 Microsoft Surface Pro (2017)。

标签: .net winforms user-interface windows-8 touch


【解决方案1】:

在运行时检测设备的触控支持是有问题的,因为用户可以随时连接触控设备。如果您在检测到触摸设备连接后调整表单大小并且设备的连接不稳定,您最终可能会得到一个不断调整自身大小的表单。最好为所有输入提供一个稳定的界面(例如,使用功能区而不是小菜单栏)。如果你真的想检测触摸设备,你可以用 SM_DIGITIZER 调用 GetSystemMetrics。

Windows 窗体不支持手势(该功能在 2005 年被冻结)。然而,表单控件仍然可以使用触摸输入,因为默认的触摸处理程序会将触摸转换为鼠标点击。如果您想拥有自己的触摸处理程序,因为触摸输入以 Windows 消息的形式发送,您可以覆盖您的表单或控件的 WndProc 函数来处理手势消息。有关示例代码,请查看 Windows 7 SDK 中的 Windows Touch Samples

要为您的触控功能编写测试代码,您可以调用 InjectTouchInput 来模拟触控输入。完整的示例可以在Input: Touch injection sample找到。

【讨论】:

  • How to add touch to winforms has since been answer 购买 LIEW HON CHIN 在他的论文中:portal.fke.utm.my/libraryfke/files/1387_LIEWHONCHIN2011.pdf,请参阅下面的答案。
  • @FastAl - 谢谢,但是论文作者使用了上面提到的相同的 Windows API。我认为上面链接的 MSDN 页面提供了更好的背景和示例。
  • @Daver,谢谢,但谁能提供示例的链接?请注意,您链接到的页面底部有 5 个以上的用户 cmets 抱怨缺乏完整的示例!我真的希望该页面为 C# 示例提供一个完整的可下载 .sln 文件 - soooo 只是缺少很多,我知道微软可能必须拥有一个 - 甚至还有截图!我只希望我错过了。 (一位用户评论提到的note-download ?id=6450 已失效)。缺少这一点,Liew 的完整 .Net 代码很好(C# 用户可以在 telerik.com 上转换)。
  • @FastAl - 链接不再可用,获取“portal.fke.utm.my 拒绝连接。”
  • [适用于需要为旧的 WinForms 应用程序添加触控支持并在 Windows 10 中运行的任何人]:Windows 触控示例位于 Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1。 C# 源代码包括 .sln 安装到 C:\Program Files\Microsoft SDKs\Windows\v7.0\Samples\Touch\MTGestures\CS示例适用于 Win 10 - 刚刚在运行 Windows 10 Pro 的 Microsoft Surface Pro (2017) 上的 Visual Studio 2010 中进行了测试。
【解决方案2】:

您可以使用组件一中的TouchToolkit for WinForms。但是我认为您必须重写您的应用程序才能使用这些组件。

【讨论】:

  • 奖励赏金,因为这是最好的答案。谢谢!
  • TouchToolkit 链接现在会导致“404 Not Found”。
【解决方案3】:

关于“无法在我的非触摸开发机器上直接测试应用程序的触摸功能。微软的模拟器似乎只支持 Metro 应用程序”的评论 - 我能够运行模拟器,转到模拟器中的桌面并在模拟触摸输入的同时运行任何应用程序 - 包括 WinForms 应用程序。

由于 WinForms 只是 WinAPI 的本机 UI API 的包装器 - 您可以使用 p/Invoke 来使用我认为是在 Vista/Windows 7 时间范围内添加的触摸 API。主要是WM_TOUCHWM_GESTURE 消息。有很多关于 p/Invoking 和使用 protected override void WndProc(ref Message m) 的示例,这是您需要处理触摸的主要内容。除此之外 - 默认情况下,触摸输入在未作为触摸处理时会自动提升为鼠标事件,因此很多事情都可以正常工作。

【讨论】:

    【解决方案4】:

    触摸应该或多或少“正常工作”,但当然,按钮需要更大等。另请参阅here,了解比触摸更复杂的手势。

    【讨论】:

      【解决方案5】:

      为 winforms 添加手势支持 - 已解决:

      http://portal.fke.utm.my/libraryfke/files/1387_LIEWHONCHIN2011.pdf

      'Imports System.Security.Permissions
      'Imports System.Runtime.InteropServices
      
      
        Private first_point As New Point()
        Private second_point As New Point()
        Private iArguments As Integer = 0
        Private Const ULL_ARGUMENTS_BIT_MASK As Int64 = &HFFFFFFFFL
        Private Const WM_GESTURENOTIFY As Integer = &H11A
        Private Const WM_GESTURE As Integer = &H119
        Private Const GC_ALLGESTURES As Integer = &H1
        Private Const GID_BEGIN As Integer = 1
        Private Const GID_END As Integer = 2
        Private Const GID_ZOOM As Integer = 3
        Private Const GID_PAN As Integer = 4
        Private Const GID_ROTATE As Integer = 5
        Private Const GID_TWOFINGERTAP As Integer = 6
        Private Const GID_PRESSANDTAP As Integer = 7
        Private Const GF_BEGIN As Integer = &H1
        Private Const GF_INERTIA As Integer = &H2
        Private Const GF_END As Integer = &H4
        Private Structure GESTURECONFIG
          Public dwID As Integer
          Public dwWant As Integer
          Public dwBlock As Integer
        End Structure
        Private Structure POINTS
          Public x As Short
          Public y As Short
        End Structure
        Private Structure GESTUREINFO
          Public cbSize As Integer
          Public dwFlags As Integer
          Public dwID As Integer
          Public hwndTarget As IntPtr
          <MarshalAs(UnmanagedType.Struct)>
          Friend ptsLocation As POINTS
          Public dwInstanceID As Integer
          Public dwSequenceID As Integer
          Public ullArguments As Int64
          Public cbExtraArgs As Integer
        End Structure
        <DllImport("user32")> _
        Private Shared Function SetGestureConfig(ByVal hWnd As IntPtr, ByVal dwReserved As Integer, ByVal cIDs As Integer, ByRef pGestureConfig As GESTURECONFIG, ByVal cbSize As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean
        End Function
        <DllImport("user32")>
        Private Shared Function GetGestureInfo(ByVal hGestureInfo As IntPtr, ByRef pGestureInfo As GESTUREINFO) As <MarshalAs(UnmanagedType.Bool)> Boolean
        End Function
        Private _gestureConfigSize As Integer
        Private _gestureInfoSize As Integer
        <SecurityPermission(SecurityAction.Demand)>
        Private Sub SetupStructSizes()
          _gestureConfigSize = Marshal.SizeOf(New GESTURECONFIG())
          _gestureInfoSize = Marshal.SizeOf(New GESTUREINFO())
        End Sub
        <PermissionSet(SecurityAction.Demand, Name:="FullTrust")>
        Protected Overrides Sub WndProc(ByRef m As Message)
          Dim handled As Boolean
          Select Case m.Msg
            Case WM_GESTURENOTIFY
              Dim gc As New GESTURECONFIG()
              gc.dwID = 0
              gc.dwWant = GC_ALLGESTURES
              gc.dwBlock = 0
              Dim bResult As Boolean = SetGestureConfig(Handle, 0, 1, gc, _gestureConfigSize)
              If Not bResult Then
                Throw New Exception("Error in execution of SetGestureConfig")
              End If
              handled = True
            Case WM_GESTURE
              handled = DecodeGesture(m)
            Case Else
              handled = False
          End Select
          MyBase.WndProc(m)
          If handled Then
            Try
              m.Result = New IntPtr(1)
            Catch excep As Exception
              Debug.Print("Could not allocate result ptr")
              Debug.Print(excep.ToString())
            End Try
          End If
        End Sub
        Private Function DecodeGesture(ByRef m As Message) As Boolean
          Dim gi As GESTUREINFO
          Try
            gi = New GESTUREINFO()
          Catch excep As Exception
            Debug.Print("Could not allocate resources to decode gesture")
            Debug.Print(excep.ToString())
            Return False
          End Try
          gi.cbSize = _gestureInfoSize
          If Not GetGestureInfo(m.LParam, gi) Then
            Return False
          End If
          Select Case gi.dwID
            Case GID_BEGIN, GID_END
            Case GID_TWOFINGERTAP
              'Receipt.Show()
              'Invalidate()
            Case GID_ZOOM
              Select Case gi.dwFlags
                Case GF_BEGIN
                  iArguments = CInt(Fix(gi.ullArguments And
                  ULL_ARGUMENTS_BIT_MASK))
                  first_point.X = gi.ptsLocation.x
                  first_point.Y = gi.ptsLocation.y
                  first_point = PointToClient(first_point)
                Case Else
                  second_point.X = gi.ptsLocation.x
                  second_point.Y = gi.ptsLocation.y
                  second_point = PointToClient(second_point)
                  RaiseEvent GestureHappened(Me, New GestureEventArgs With {.Operation = Gestures.Pan, .FirstPoint = first_point, .SecondPoint = second_point})
                  'Invalidate()
                  'MsgBox("zoom")
              End Select
            Case GID_PAN
              Select Case gi.dwFlags
                Case GF_BEGIN
                  first_point.X = gi.ptsLocation.x
                  first_point.Y = gi.ptsLocation.y
                  first_point = PointToClient(first_point)
                Case Else
                  second_point.X = gi.ptsLocation.x
                  second_point.Y = gi.ptsLocation.y
                  second_point = PointToClient(second_point)
                  RaiseEvent GestureHappened(Me, New GestureEventArgs With {.Operation = Gestures.Pan, .FirstPoint = first_point, .SecondPoint = second_point})
                  'Invalidate()
                  'MsgBox("pan")
              End Select
            Case GID_PRESSANDTAP
              'If gi.dwFlags = GF_BEGIN Then
              '  Invalidate()
              'End If
            Case GID_ROTATE
              'Select Case gi.dwFlags
              '  Case GF_BEGIN
              '    iArguments = 0
              '  Case Else
              '    first_point.X = gi.ptsLocation.x
              '    first_point.Y = gi.ptsLocation.y
              '    first_point = PointToClient(first_point)
              '    Invalidate()
              'End Select
          End Select
          Return True
        End Function
      
      
        Public Enum Gestures
          Pan
          Zoom
        End Enum
      
        Public Class GestureEventArgs
          Inherits EventArgs
          Public Property Operation As Gestures
          Public Property FirstPoint As Point
          Public Property SecondPoint As Point
        End Class
      
        Public Event GestureHappened(sender As Object, e As GestureEventArgs)
      

      【讨论】:

      • 不幸的是,pdf 链接已失效。你如何使用上面的代码?我的第一个猜测是将所有这些行放在类定义中。然后呢? [我正在尝试向旧的 WinForms 应用程序添加双指缩放功能。]
      • 没关系。请参阅我在Sheng Jiang's answer 下的评论 - Windows 7 SDK Touch Samples 包含完整的 C# 解决方案,实现捏合缩放和旋转手势。该示例在运行 Windows 10 的 Microsoft Surface Pro (2017) 上按原样工作,所以我有更新旧 WinForms 应用程序所需的东西:)
      猜你喜欢
      • 2011-12-24
      • 1970-01-01
      • 2014-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-22
      • 1970-01-01
      相关资源
      最近更新 更多