更新:
以下解决方法的解决方法效果不佳。
问题是所有手指点击都被解释为鼠标点击。自定义触摸滚动仅适用于对鼠标单击没有反应的内容。为了使其正常工作,您需要找到一种方法来在执行滚动操作时“吃掉”鼠标单击事件。
我可能找到了解决触摸滚动损坏的方法。
处理 WM_TOUCH 并使用自定义 TouchDevice。
归功于 Luca Cornazzani:Enable multitouch on WPF controls
我使用的另一个来源(用于 TOUCHINPUT 定义):WPF and multi-touch
在应用启动时调用众所周知的 DisableWPFTabletSupport 函数。
MainWindow.xaml:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TouchScrollTest"
mc:Ignorable="d"
Title="MainWindow" Height="395.603" Width="525">
<Grid>
<StackPanel>
<ComboBox x:Name="comboBox1" FontSize="16" Width="150">
</ComboBox>
<ScrollViewer Height="300" Width="300" PanningMode="Both" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<TextBlock x:Name="textBlock1">
</TextBlock>
</ScrollViewer>
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.vb:
Class MainWindow
Private _devices As New Dictionary(Of Integer, TouchDeviceEmulator)()
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
For i As Integer = 1 To 19
Dim myComboBoxItem As ComboBoxItem = New ComboBoxItem
myComboBoxItem.Content = "ComboBoxItem " & i.ToString()
comboBox1.Items.Add(myComboBoxItem)
Next
For i As Integer = 65 To 90
Dim c As Char = ChrW(i)
For j As Integer = 1 To 10
textBlock1.Text += " ".PadLeft(10, c)
Next
textBlock1.Text += vbCrLf
Next
End Sub
Protected Overrides Sub OnSourceInitialized(e As EventArgs)
MyBase.OnSourceInitialized(e)
Dim source As Interop.HwndSource = TryCast(PresentationSource.FromVisual(Me), Interop.HwndSource)
source.AddHook(New Interop.HwndSourceHook(AddressOf WndProc))
Dim presentation = DirectCast(PresentationSource.FromDependencyObject(Me), Interop.HwndSource)
If presentation Is Nothing Then
Throw New Exception("Unable to find the parent element host.")
End If
RegisterTouchWindow(presentation.Handle, TouchWindowFlag.WantPalm)
End Sub
Private Function WndProc(hwnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr, ByRef handled As Boolean) As IntPtr
' Handle messages...
If msg = WM_TOUCH Then
handled = HandleTouch(wParam, lParam)
Return New IntPtr(1)
End If
Return IntPtr.Zero
End Function
Private Function HandleTouch(wParam As IntPtr, lParam As IntPtr) As Boolean
Dim handled As Boolean = False
Dim inputCount = wParam.ToInt32() And &HFFFF
Dim inputs = New TOUCHINPUT(inputCount - 1) {}
If GetTouchInputInfo(lParam, inputCount, inputs, Runtime.InteropServices.Marshal.SizeOf(inputs(0))) Then
For i As Integer = 0 To inputCount - 1
Dim input As TOUCHINPUT = inputs(i)
'TOUCHINFO point coordinates and contact size is in 1/100 of a pixel; convert it to pixels.
'Also convert screen to client coordinates.
Dim position As Point = PointFromScreen(New System.Windows.Point((input.x * 0.01), (input.y * 0.01)))
Dim device As TouchDeviceEmulator = Nothing
If Not _devices.TryGetValue(input.dwID, device) Then
device = New TouchDeviceEmulator(input.dwID)
_devices.Add(input.dwID, device)
End If
device.Position = position
If (input.dwFlags And TOUCHEVENTF_DOWN) > 0 Then
device.SetActiveSource(PresentationSource.FromVisual(Me))
device.Activate()
device.ReportDown()
ElseIf device.IsActive AndAlso (input.dwFlags And TOUCHEVENTF_UP) > 0 Then
device.ReportUp()
device.Deactivate()
_devices.Remove(input.dwID)
ElseIf device.IsActive AndAlso (input.dwFlags And TOUCHEVENTF_MOVE) > 0 Then
device.ReportMove()
End If
Next
CloseTouchInputHandle(lParam)
handled = True
End If
Return handled
End Function
Private Class TouchDeviceEmulator
Inherits TouchDevice
Public Position As System.Windows.Point
Public Sub New(deviceId As Integer)
MyBase.New(deviceId)
End Sub
Public Overrides Function GetTouchPoint(relativeTo As IInputElement) As TouchPoint
Dim pt As System.Windows.Point = Position
If relativeTo IsNot Nothing Then
pt = ActiveSource.RootVisual.TransformToDescendant(DirectCast(relativeTo, Visual)).Transform(Position)
End If
Dim rect = New Rect(pt, New Size(1.0, 1.0))
Return New TouchPoint(Me, pt, rect, TouchAction.Move)
End Function
Public Overrides Function GetIntermediateTouchPoints(relativeTo As IInputElement) As TouchPointCollection
Throw New NotImplementedException()
End Function
Public Overloads Sub SetActiveSource(activeSource As PresentationSource)
MyBase.SetActiveSource(activeSource)
End Sub
Public Overloads Sub Activate()
MyBase.Activate()
End Sub
Public Overloads Sub ReportUp()
MyBase.ReportUp()
End Sub
Public Overloads Sub ReportDown()
MyBase.ReportDown()
End Sub
Public Overloads Sub ReportMove()
MyBase.ReportMove()
End Sub
Public Overloads Sub Deactivate()
MyBase.Deactivate()
End Sub
End Class
Private Const WM_TOUCH As Integer = &H240
Private Enum TouchWindowFlag As UInteger
FineTouch = &H1
WantPalm = &H2
End Enum
' Touch event flags ((TOUCHINPUT.dwFlags) [winuser.h]
Private Const TOUCHEVENTF_MOVE As Integer = &H1
Private Const TOUCHEVENTF_DOWN As Integer = &H2
Private Const TOUCHEVENTF_UP As Integer = &H4
Private Const TOUCHEVENTF_INRANGE As Integer = &H8
Private Const TOUCHEVENTF_PRIMARY As Integer = &H10
Private Const TOUCHEVENTF_NOCOALESCE As Integer = &H20
Private Const TOUCHEVENTF_PEN As Integer = &H40
Private Const TOUCHEVENTF_PALM As Integer = &H80
<Runtime.InteropServices.StructLayout(Runtime.InteropServices.LayoutKind.Sequential)>
Private Structure TOUCHINPUT
Public x As Int32
Public y As Int32
Public hSource As IntPtr
Public dwID As Int32
Public dwFlags As Int32
Public dwMask As Int32
Public dwTime As Int32
Public dwExtraInfo As IntPtr
Public cxContact As Int32
Public cyContact As Int32
End Structure
<Runtime.InteropServices.DllImport("user32")>
Private Shared Function RegisterTouchWindow(hWnd As System.IntPtr, flags As TouchWindowFlag) As Boolean
End Function
<Runtime.InteropServices.DllImport("user32")>
Private Shared Function GetTouchInputInfo(hTouchInput As IntPtr, cInputs As Int32, <Runtime.InteropServices.[In], Runtime.InteropServices.Out> pInputs As TOUCHINPUT(), cbSize As Int32) As <Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.Bool)> [Boolean]
End Function
<Runtime.InteropServices.DllImport("user32")>
Private Shared Sub CloseTouchInputHandle(lParam As System.IntPtr)
End Sub
End Class
此示例中的大多数代码与 Cornazzani 的 C# 代码相同。
至少对于 ScrollViewer 它似乎可以工作,还没有测试过其他控件。
它不能解决我的手写笔支持部分损坏的问题。在 InkCanvas 上书写不像以前那么顺畅,并且橡皮擦按钮在 DisableWPFTabletSupport hack 中根本不起作用。
同样有趣的方法:github 上的 WmTouchDevice。