Adding Windows 8 touch support to existing WinForms application

13,869

Solution 1

Detecting the device's touch support at run-time is problematic since the user can connect a touch device anytime. If you resize the form after detecting touch device connection and the device's connection is not stable you may end up with a form that keep resizing itself. Better have one steady interface for all inputs (e.g. using ribbon instead of a small menu bar). If you really want to detect touch devices, you can call GetSystemMetrics with SM_DIGITIZER.

Gestures are not supported in Windows Forms (feature frozen in 2005). However form controls can still use touch input since the default touch handler translate touches to mouse clicks. If you want to have your own touch handler, since touch inputs are sent in the form of windows Messages, you can override your form or control's WndProc function to handle the gesture messages. For sample code check Windows Touch Samples in the the Windows 7 SDK.

For writing test code for your touch capability, you can call InjectTouchInput to simulate touch input. A complete sample can be found at Input: Touch injection sample.

Solution 2

You can use TouchToolkit for WinForms from component one. However I think you'll have to rewrite your application to use these components.

enter image description here

Solution 3

Regarding the comment on "Inability to directly test the touch features of the application on my non-touch development machine. Microsoft's simulator only seems to support Metro apps" - I am able to run the simulator, go to the desktop in the simulator and run any app while simulating touch input - that's including WinForms apps.

Since WinForms is just a wrapper over WinAPI's native UI APIs - you can use p/Invoke to use the touch APIs that I think were added around Vista/Windows 7 timeframe. Mainly the WM_TOUCH and WM_GESTURE messages. There are plenty of examples for p/Invoking and using protected override void WndProc(ref Message m) which are the main things you'd need to handle touch. Other than that - touch inputs are by default automatically promoted to mouse events when not handled as touch, so a lot of things just work.

Solution 4

Touch should more or less "just work", but of course, the buttons will need to be larger etc. Also see here for more complex gestures than just touch.

Solution 5

Adding gesture support to winforms - solved here:

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)
Share:
13,869
Balmus
Author by

Balmus

Hi, I'm Dave, a freelance software developer. I code in a variety of languages, and currently concentrate on C# and web technologies. I love learning new things, and I hope I can help others to do the same. In my spare time I like movies and reading fiction. I do get out of the house occasionally, though, and enjoy a walk in the local countryside when I need to clear my head.

Updated on July 23, 2022

Comments

  • Balmus
    Balmus almost 2 years

    I have an existing Windows Forms desktop application targeting .NET Framework 4 and would like to add Windows 8 touch support to it.

    Currently the program works fine in Windows 8, and I can potentially just resize some of the elements to make it more user-friendly on touch devices. However, adding gestures such as pinch-to-zoom on datagrids, and swipe support for other elements would go a long way to making the application more modern in a touch-only environment.

    I'm investing in Visual Studio 2012, which will let me target .NET 4.5 and the new Windows 8 features, but does anyone know of any resources which would help me with updating my application? I'm specifically concerned about the following:

    • Inability to directly test the touch features of the application on my non-touch development machine. Microsoft's simulator only seems to support Metro apps. I've heard that tablet apps such as Splashtop can help (I have an Android tablet), but haven't seen anything concrete for this particular scenario
    • Whether gestures are even supported on WinForms applications. Am I going to have to upgrade the entire UI to WPF to get this working? (If I did go this route, I believe I could also target Windows 7, as multi-touch is supported on WPF 4)
    • Detecting the device's touch support at runtime and scaling/changing the UI appropriately, similar to the Touch mode setting on Microsoft's Windows RT Office apps. I don't want to fork the project just to add the new features
    • Automated testing of touch interactions

    This isn't an exhaustive list by any means, but I'd really appreciate any advice from those who may have approached a similar upgrade in the past.

    • N_A
      N_A over 11 years
      If you're targeting WinRT you're going to have to do a complete rewrite since WinRT has no WinForms equivalent.
    • Balmus
      Balmus over 11 years
      @mydogisbox - I'm not targeting WinRT, just interested in upgrading an existing WinForms desktop application to support touch. I should have made that clearer in my question. Thanks for the input.
    • ToolmakerSteve
      ToolmakerSteve almost 7 years
      [For anyone who needs to update an old WinForms app to take full advantage of Windows 10 touchscreen], see my comment under Sheng Jiang's answer for current link to Windows 7 SDK to obtain the gesture sample (pinch-zoom and rotate). Verified that the C# solution works on Microsoft Surface Pro (2017) running Windows 10 Pro.
  • SSS
    SSS almost 10 years
    Awarded bounty as this was the best answer. Thanks!
  • FastAl
    FastAl over 8 years
    How to add touch to winforms has since been answered buy LIEW HON CHIN in his thesis: portal.fke.utm.my/libraryfke/files/1387_LIEWHONCHIN2011.pdf, see my answer below.
  • Balmus
    Balmus over 8 years
    @FastAl - Thanks, but the thesis author uses the same Windows APIs which are referred to above. I think the MSDN page linked above provides better background and examples.
  • FastAl
    FastAl over 8 years
    @Daver, thanks, but can anybody provide a link to the examples? Note 5+ user comments at the bottom of the pages you link to complaining about a lack of complete examples! I really wanted that page to provide a complete downloadable .sln file for the C# example - soooo much is just missing and I know Microsoft probably had to have one- there are even screenshots! I just hope I missed it. (note-download ?id=6450 mentioned by one user comment is dead). Lacking that, Liew's complete.Net code was nice (C# users can convert at telerik.com).
  • ToolmakerSteve
    ToolmakerSteve almost 7 years
    @FastAl - link no longer useable, get "portal.fke.utm.my refused to connect."
  • ToolmakerSteve
    ToolmakerSteve almost 7 years
    TouchToolkit link now results in "404 Not Found".
  • ToolmakerSteve
    ToolmakerSteve almost 7 years
    Unfortunately, the pdf link is dead. How do you use the code above? My first guess is to put all those lines inside a class definition. Then what? [I'm trying to add pinch-zoom to an old WinForms app.]
  • ToolmakerSteve
    ToolmakerSteve almost 7 years
    [For anyone who needs to add touch support to an old WinForms app, and run it in Windows 10]: Windows Touch Samples are in Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1. C# source including .sln installed to C:\Program Files\Microsoft SDKs\Windows\v7.0\Samples\Touch\MTGestures\CS . Sample works in Win 10 - tested just now in Visual Studio 2010 on Microsoft Surface Pro (2017) running Windows 10 Pro.
  • ToolmakerSteve
    ToolmakerSteve almost 7 years
    MORE: The sample shows two-touchpoint pinch-zoom and rotate working in a WinForms form window. Which of those gestures is active is determined by how you move your fingers at the beginning of the gesture. If you touch with two fingers and move them apart or together, then you are in pinch-zoom gesture. If you touch with two fingers and rotate one around the other, then you are in rotate gesture.
  • ToolmakerSteve
    ToolmakerSteve almost 7 years
    Never mind. See my comment under Sheng Jiang's answer - Windows 7 SDK Touch Samples includes a complete C# solution, implementing pinch-zoom and rotate gestures. That sample works as-is on Microsoft Surface Pro (2017) running Windows 10, so I have what I need to update an old WinForms app :)