2011-11-04 94 views
20

我有一個WPF應用程序,可以同時使用鼠標和使用觸摸。我關閉所有窗口「增強功能」只是有觸摸事件:如何避免鼠標移動觸摸

Stylus.IsPressAndHoldEnabled="False" 
Stylus.IsTapFeedbackEnabled="False" 
Stylus.IsTouchFeedbackEnabled="False" 
Stylus.IsFlicksEnabled="False" 

結果是一個點擊行爲像我想除了在兩點:

  • 小「觸摸」光標(小白星號)出現在拖動時單擊的地方。
    完全無用,因爲用戶手指已經在此位置,不需要任何反饋(除了我的元素在可操作的情況下可能會改變顏色)。
  • 移動/點擊結束後,元素保持「懸停」狀態。

兩者都是事實的結果,雖然Windows傳輸正確的觸摸事件,他仍然移動鼠標到最後一個主要觸摸事件。

當我在我的應用程序中使用觸摸時,我不想讓windows移動鼠標。有沒有辦法完全避免這種情況?

注:

  • 處理觸摸事件改變什麼了這一點。
  • 使用SetCursorPos移動鼠標使光標閃爍,並不真正用戶友好。
  • 禁用觸摸面板作爲輸入設備可以完全禁用所有事件(並且我更喜歡應用程序本地解決方案,而不是系統範圍)。
  • 我不在乎如果解決方案涉及COM/PInvoke或在C/C++提供我會翻譯。
  • 如果有必要修補/掛鉤一些windows dll,那麼軟件無論如何都會在專用設備上運行。
  • 我正在研究表面SDK,但我懷疑它會顯示任何解決方案。由於表面是純觸摸設備,因此不存在與鼠標不良交互的風險。
+0

經過深入研究,似乎並不僅僅可以用於我的窗口,而是使用SetWindowsHookEx我可以在全球範圍內完成。我現在可以在我的應用全屏顯示時啓用此功能。當我有一些乾淨的東西時,我會發布解決方案。 –

+0

如果有人(比如說來自微軟)碰巧有更好的解決方案,請隨時發佈。 –

+0

我不知道你是否足夠,但你可以通過檢查是否(e.StylusDevice!= null)來發現是否觸摸了鼠標事件。 – Andreas

回答

10

這是我從現在發現的最佳解決方案。不要猶豫,張貼你自己的,特別是如果它更好。

使用SetWindowsHookEx低水平鼠標事件捕獲(WH_MOUSE_LL),並從觸摸轉換成鼠標的所有事件都被標記爲這樣的事實(該MOUSEEVENTF_FROMTOUCH標誌設置在事件的ExtraInfo,見Microsoft's FAQ)我能全球刪除觸摸面板上的所有鼠標事件。

這不是一個理想的解決方案,但它現在可以在我的應用程序中運行全屏(99%的時間,因爲它是一個專用的硬件設備)。

第二步也只在全屏(我不會提供代碼,因爲它非常簡單)只是將鼠標移動到「安全」的位置,如屏幕的右下方SetCursorPos

如果您需要的代碼是a Gist on Github,我會在本文最後發佈當前版本。要使用它:類

// As long as the instance is alive the conversion won't occur 
var disableTouchMouse = new DisableTouchConversionToMouse(); 

// To let the conversion happen again, Dispose the class. 
disableTouchMouse.Dispose(); 

完整的源代碼:

namespace BlackFox 
{ 
    using System; 
    using System.ComponentModel; 
    using System.Runtime.InteropServices; 
    using System.Security; 

    /// <summary> 
    /// As long as this object exists all mouse events created from a touch event for legacy support will be disabled. 
    /// </summary> 
    class DisableTouchConversionToMouse : IDisposable 
    { 
     static readonly LowLevelMouseProc hookCallback = HookCallback; 
     static IntPtr hookId = IntPtr.Zero; 

     public DisableTouchConversionToMouse() 
     { 
      hookId = SetHook(hookCallback); 
     } 

     static IntPtr SetHook(LowLevelMouseProc proc) 
     { 
      var moduleHandle = UnsafeNativeMethods.GetModuleHandle(null); 

      var setHookResult = UnsafeNativeMethods.SetWindowsHookEx(WH_MOUSE_LL, proc, moduleHandle, 0); 
      if (setHookResult == IntPtr.Zero) 
      { 
       throw new Win32Exception(); 
      } 
      return setHookResult; 
     } 

     delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam); 

     static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) 
     { 
      if (nCode >= 0) 
      { 
       var info = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)); 

       var extraInfo = (uint)info.dwExtraInfo.ToInt32(); 
       if ((extraInfo & MOUSEEVENTF_MASK) == MOUSEEVENTF_FROMTOUCH) 
       { 
        if((extraInfo & 0x80) != 0) 
        { 
         //Touch Input 
         return new IntPtr(1); 
        } 
        else 
        { 
         //Pen Input 
         return new IntPtr(1); 
        } 

       } 
      } 

      return UnsafeNativeMethods.CallNextHookEx(hookId, nCode, wParam, lParam); 
     } 

     bool disposed; 

     public void Dispose() 
     { 
      if (disposed) return; 

      UnsafeNativeMethods.UnhookWindowsHookEx(hookId); 
      disposed = true; 
      GC.SuppressFinalize(this); 
     } 

     ~DisableTouchConversionToMouse() 
     { 
      Dispose(); 
     } 

     #region Interop 

     // ReSharper disable InconsistentNaming 
     // ReSharper disable MemberCanBePrivate.Local 
     // ReSharper disable FieldCanBeMadeReadOnly.Local 

     const uint MOUSEEVENTF_MASK = 0xFFFFFF00; 

     const uint MOUSEEVENTF_FROMTOUCH = 0xFF515700; 
     const int WH_MOUSE_LL = 14; 

     [StructLayout(LayoutKind.Sequential)] 
     struct POINT 
     { 

      public int x; 
      public int y; 
     } 

     [StructLayout(LayoutKind.Sequential)] 
     struct MSLLHOOKSTRUCT 
     { 
      public POINT pt; 
      public uint mouseData; 
      public uint flags; 
      public uint time; 
      public IntPtr dwExtraInfo; 
     } 

     [SuppressUnmanagedCodeSecurity] 
     static class UnsafeNativeMethods 
     { 
      [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
      public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, 
       uint dwThreadId); 

      [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
      [return: MarshalAs(UnmanagedType.Bool)] 
      public static extern bool UnhookWindowsHookEx(IntPtr hhk); 

      [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
      public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, 
       IntPtr wParam, IntPtr lParam); 

      [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
      public static extern IntPtr GetModuleHandle(string lpModuleName); 
     } 

     // ReSharper restore InconsistentNaming 
     // ReSharper restore FieldCanBeMadeReadOnly.Local 
     // ReSharper restore MemberCanBePrivate.Local 

     #endregion 
    } 
} 

編輯:從Troubleshooting ApplicationsSystem Events and Mouse Messages附加信息的評論部分,從觸摸筆歧義。

+0

我們必須找到應用範圍廣泛的解決方案......大多數觸摸應用程序沒有專用的硬件設備。 此外,「右下角」絕對不是一個安全的地方,因爲在Windows 7 +中,它隱藏了懸停時的所有窗口。而在Windows 8中,各個角落都有其含義。嘗試從顯示器集合中查找Right(Left)屬性的Max(Min)值,並添加更多像素來隱藏光標(不要忘記根據API將像素轉換爲DIP) –

+0

我還必須硬重啓我的這個建議後的平板電腦(完全不瞭解這裏的'全球'是什麼意思)。因爲我一直無法關閉我的應用程序與這個黑客... –

+0

嗯,我沒有找到任何解決方案本地的應用程序。正如我所說的解決方案是全球性的,我的應用程序是全屏,並運行在Win7上(所以右下角是一個安全的位置,因爲我決定在我的應用程序中,並沒有把任何東西) –

0

我曾經爲應用程序使用過的東西,是簡單地設置一個自定義光標,其圖像只是一個空白的.CUR文件。至於第二個問題,我確實建議將光標移到另一個位置,但後來我看到你做了同樣的事情。如果您的應用程序不能全屏運行,您可以將該安全位置定義爲應用程序窗口的右下角。

+0

那麼,空白光標的問題是,我不希望我的應用程序無法使用鼠標,它應該適用於鼠標,因爲用戶可以自由地將其連接到設備。另外Windows 7在觸摸時創建「白色星形」光標,並且它不可配置(它總是顯示並忽略任何定義的應用程序) –

+0

我明白你的意思。有一個[頁面](http://windows.microsoft.com/en-US/windows-vista/Turn-the-touch-pointer-on-or-off)這表明它可能會關閉它。我不知道的是它是禁用還是隱藏。如果只是隱藏起來,我想這個選項可以通過編程方式訪問(最壞的情況,基於找出它在註冊表中的位置)。我在想的是你可以根據你的應用程序窗口是否在前臺來啓用/禁用它。這有道理嗎? – Vlad

+0

其實我試過這個選項,它所做的並不是控制小白星指針,而是啓用/禁用一個更可怕的「光標」,鼠標圖片顯示在您的點擊的左側,您可以點擊它的按鈕模擬點擊。它默認關閉,看它有多糟糕,它應該根本不存在:-D –

-1

剛剛有同樣的問題,我發現here,可以使用PreviewXXX事件來確定事件是由觸摸還是鼠標輸入啓動。看看下面的代碼:

private void UIElement_OnPreviewMouseMove(object sender, MouseEventArgs e) 
{ 
    if (e.StylusDevice != null) 
    { 
     AddInfoItem("Stylus or Touch recognized"); 
     e.Handled = true; 
     return; 
    } 

    AddInfoItem("No Stylus or Touch recognized"); 
} 

您可以檢查MouseEventArgsStylusDevice屬性來確定觸摸事件曾參與。如果它不爲空,則可以將e.Handled設置爲true以防止引發與PreviewXXX事件相對應的事件。

希望有所幫助。一個演示項目可以從here下載(Dropbox鏈接)。

+0

我根本不想壓制它們,我希望他們能夠像平常一樣行事,除了他們沒有顯示醜陋的白色星星,並且沒有讓控制權在懸停狀態中出現。 –

+0

對不起,我誤解了你的問題,我應該刪除我的答案嗎? – feO2x