2016-07-28 82 views
2

我已經繼承了一個針對Net 3.5的WPF應用程序,而且我必須將它安裝在Surface Pro 4(I5)中。該應用程序掛在不同的點上,我觀察到動畫有時從不會觸發完成的事件(也許它們在某個時間點結束,但不是在持續時間屬性中表示的時間)。我試過Disable the RealTimeStylus for WPF Applications,但經過多次試用後,我注意到儘管DisableWPFTabletSupport方法已經執行並完成(我在DisableWPFTabletSupport方法中添加了日誌代碼,並且在Surface Pro 4中刪除了四個設備),可能WPF平板電腦我的應用程序仍然支持支持,因爲應用程序會不時掛起,並繼續捕獲屏幕接觸。如何禁用Surface 4 Pro中的WPF Tablet支持?

因此,我發現能夠成功運行面向Surface 4 Pro中的Net 3.5的WPF應用程序的唯一方法是使用Windows設備管理器來禁用人機界面中的所有觸摸屏相關設備。

任何人都知道我可以禁用Surface 4 Pro中的WPF Tablet支持嗎?

注意。儘管在disable and enable the touchscreen driver上說了什麼,但僅禁用「符合HID的觸摸屏設備」是不夠的:直到「英特爾(R)精確觸摸設備」未被禁用,觸摸屏仍保持激活狀態,並且大多數WPF應用程序都會失敗。

回答

2

我有同樣的問題,並能夠找到使用反射的解決辦法。

此問題是由WPF在發送窗口消息WM_TABLET_ADDED,WM_TABLET_REMOVED或WM_DEVICECHANGED(see .net referencesource)時更新其內部平板設備處理引起的。由於這些消息可能會或可能不會生成,具體取決於所使用的硬件,原始的DisableWPFTabletSupport方法可能已經足夠或者沒有。

我的解決辦法,除了原有的代碼來處理和隱藏WPF這三個窗口消息:

class DisableWPFTouchAndStylus 
{ 

private static void DisableWPFTabletSupport() 
{ 
    // Get a collection of the tablet devices for this window. 
    var devices = Tablet.TabletDevices; 

    if (devices.Count > 0) 
    { 
     // Get the Type of InputManager. 
     var inputManagerType = typeof(InputManager); 

     // Call the StylusLogic method on the InputManager.Current instance. 
     var stylusLogic = inputManagerType.InvokeMember("StylusLogic", 
        BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic, 
        null, InputManager.Current, null); 

     if (stylusLogic != null) 
     { 
      // Get the type of the stylusLogic returned from the call to StylusLogic. 
      var stylusLogicType = stylusLogic.GetType(); 

      // Loop until there are no more devices to remove. 
      while (devices.Count > 0) 
      { 
       // Remove the first tablet device in the devices collection. 
       stylusLogicType.InvokeMember("OnTabletRemoved", 
         BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic, 
         null, stylusLogic, new object[] { (uint)0 }); 
      } 
     } 
    } 

    // END OF ORIGINAL CODE 

    // hook into internal class SystemResources to keep it from updating the TabletDevices on system events 

    object hwndWrapper = GetSystemResourcesHwnd(); 
    if (hwndWrapper != null) 
    { 
     // invoke hwndWrapper.AddHook(.. our method ..) 
     var internalHwndWrapperType = hwndWrapper.GetType(); 

     // if the delegate is already set, we have already added the hook. 
     if (_handleAndHideMessageDelegate == null) 
     { 
      // create the internal delegate that will hook into the window messages 
      // need to hold a reference to that one, because internally the delegate is stored through a WeakReference object 

      var internalHwndWrapperHookDelegate = internalHwndWrapperType.Assembly.GetType("MS.Win32.HwndWrapperHook"); 
      var handleAndHideMessagesHandle = typeof(DisableWPFTouchAndStylus).GetMethod(nameof(HandleAndHideMessages), BindingFlags.Static | BindingFlags.NonPublic); 
      _handleAndHideMessageDelegate = Delegate.CreateDelegate(internalHwndWrapperHookDelegate, handleAndHideMessagesHandle); 


      // add a delegate that handles WM_TABLET_ADD 
      internalHwndWrapperType.InvokeMember("AddHook", 
       BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, 
       null, hwndWrapper, new object[] { _handleAndHideMessageDelegate }); 
     } 
    } 
} 

private static Delegate _handleAndHideMessageDelegate = null; 

private static object GetSystemResourcesHwnd() 
{ 
    var internalSystemResourcesType = typeof(Application).Assembly.GetType("System.Windows.SystemResources"); 

    // get HwndWrapper from internal property SystemRessources.Hwnd; 
    var hwndWrapper = internalSystemResourcesType.InvokeMember("Hwnd", 
       BindingFlags.GetProperty | BindingFlags.Static | BindingFlags.NonPublic, 
       null, null, null); 
    return hwndWrapper; 
} 

private static IntPtr HandleAndHideMessages(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 
{ 
    if (msg == (int)WindowMessage.WM_TABLET_ADDED || 
     msg == (int)WindowMessage.WM_TABLET_DELETED || 
     msg == (int)WindowMessage.WM_DEVICECHANGE) 
    { 
     handled = true; 
    } 
    return IntPtr.Zero; 
} 

enum WindowMessage : int 
{ 
    WM_TABLET_DEFBASE = 0x02C0, 
    WM_TABLET_ADDED = WM_TABLET_DEFBASE + 8, 
    WM_TABLET_DELETED = WM_TABLET_DEFBASE + 9, 
    WM_DEVICECHANGE = 0x0219 
} 

} 

在此實現的一些注意事項和限制:

WPF不註冊這些消息在應用程序MainWindow上,但是通過爲每個應用程序實例創建的名爲「SystemResources ...」的隱藏窗口。因此,在MainWindow上處理這些消息(這很容易)在這裏沒有幫助。

我的解決方案也使用了相當多的反射並調用內部類和內部屬性。它適用於.net 4.6.2,並沒有在早期版本上測試過。此外,在深入研究.net源代碼時,我還看到了其他兩種可能的更新平板電腦處理的途徑,這些方案在本解決方案中未得到處理:TabletCollection和HwndStylusInputProvider的構造函數。

+0

你在Surface 4 pro中測試過嗎?感謝您分享您的解決方案 – SERWare

+0

@SERWare:是的,它適用於SP4。我沒有親自測試過它,但它適用於我們客戶的SP4。我還不知道,如果它也適用於Surface Book,但我有信心。 – tseifried

+0

我幾乎是一樣的情況。我在客戶的SP4上檢測到錯誤,我必須等到他們返回來測試您的解決方案。再次感謝。 – SERWare