2017-09-05 46 views
6

更新WPF应用程序的触摸(部分)冻结4.7

Microsoft acknowledged the issue

Gepost门微软运算13/10/2017 OM 11:38

谢谢你报告这一点。 我们意识到这个问题,并正在修复.NET的未来版本。 还有一个相关的问题正在服务修复中发布,这将大大降低打击此问题的可能性。这将很快得到服务。

问题

我们WPF应用程序正在使用触摸(无笔)平板电脑使用,我们正在经历的安装.NET框架4.7后的问题。使用应用程序一段时间后可能会出现两种情况:应用程序完全冻结并且必须重新启动,或者禁用了PopupWindow元素中的所有触摸功能。两者之间有相当大的差异,但我相信原因是一样的。

方案1:完全冻结

  • 的应用程序变得完全没有响应,应用程序必须使用任务管理器
  • 触摸,也可以用鼠标
  • 有时以下错误被抛出关闭在应用程序挂起之前:

索引是ou摆脱阵列的界限。

这是堆栈跟踪:

at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add) 
    at System.Windows.Input.StylusWisp.WispLogic.CoalesceAndQueueStylusEvent(RawStylusInputReport inputReport) 
    at System.Windows.Input.StylusWisp.WispLogic.ProcessSystemEvent(PenContext penContext, Int32 tabletDeviceId, Int32 stylusDeviceId, Int32 timestamp, SystemGesture systemGesture, Int32 gestureX, Int32 gestureY, Int32 buttonState, PresentationSource inputSource) 
    at System.Windows.Input.PenContext.FireSystemGesture(Int32 stylusPointerId, Int32 timestamp) 
    at System.Windows.Input.PenThreadWorker.FireEvent(PenContext penContext, Int32 evt, Int32 stylusPointerId, Int32 cPackets, Int32 cbPacket, IntPtr pPackets) 
    at System.Windows.Input.PenThreadWorker.ThreadProc() 
    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
    at System.Threading.ThreadHelper.ThreadStart() 

方案2:部分冻结

  • 主窗口仍然响应(通过鼠标和触摸),但任何 '覆盖' 内容(模态对话框,Window,Popup元素,来自DatePicker,ComboBox,...)不响应轻拍。应用程序必须重新启动才能重新启用触摸。
  • 鼠标仍可用于“叠加”元素。

此问题也在详细解释here。 问题发生后的行为视频可以找到here

附加信息

  • 这两种情况都可以模拟不同类型的片,并且还在Windows模拟器,使用Windows 8.1和Windows 10的混合。
  • 当删除.NET Framework 4.7时,问题得到解决。
  • 通过用多个手指快速点击某些ComboBox元素,可以轻松地再现场景2。几分钟后,弹出窗口不再响应触摸。
  • 情景1很难模拟,并且随机发生。

原因

这个问题似乎有事情做与StylusWisp代码。我想它突然失败,并在那之后变得无法使用。

使用DisableWPFTabletSupportDisableStylusAndTouchSupport禁用触笔支持时,问题消失。但是,任何ScrollViewerPanningMode="Both"不能再被滑动滚动。

解决方案?

A similar issue已报告给Microsoft。由于目前还没有太多支持,修复可能需要一段时间。与此同时,我正在寻找一个针对此问题的解决方案,即不涉及禁用.NET Framework 4.7并保持原始触摸支持完好。有没有人有相同的问题和更好的解决方案?

回答

0

更新: 变通办法解决方法的下面解决方法无法正常工作。
问题是,所有手指轻敲都被解释为鼠标点击。自定义触摸滚动只适用于对鼠标点击无反应的内容。为了使它工作正常,您需要找到一种方法来在执行滚动操作时“吃”鼠标点击事件。

我可能找到了一个解决方法,用于断开的触摸滚动。
处理WM_TOUCH并使用自定义TouchDevice。
幸得卢卡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来说,它似乎工作,还没有测试过其他控件。
它不能解决我部分断开的Stylus支持的问题。在InkCanvas上书写工作并不像以前那么流畅,橡皮擦按钮根本无法使用DisableWPFTabletSupport破解工具。

也有趣的是,同样的方法:github上的WmTouchDevice。

0

安装.NET Framework 4.7.1似乎解决了这个问题。 .NET Framework 4.7。1还包含在自10月份开始推出的Windows 10 Fall Creators更新中。