问题
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.
回答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.
回答2:
You can use TouchToolkit for WinForms from component one. However I think you'll have to rewrite your application to use these components.
回答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.
回答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.
回答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)
来源:https://stackoverflow.com/questions/13309218/adding-windows-8-touch-support-to-existing-winforms-application