2010-11-11 11 views
5

¿Cómo habilita WPF para responder al desplazamiento horizontal con la rueda de inclinación del mouse? Por ejemplo, tengo un mini ratón Microsoft Explorer y he tratado de desplazamiento horizontal contenido en su interior un ScrollViewer con¿Cómo desplazarse horizontalmente en WPF con la rueda de inclinación del mouse?

HorizontalScrollBarVisibility="Visible" 

pero el contenido no se desplazará horizontalmente. El desplazamiento vertical, sin embargo, funciona de manera confiable como de costumbre.

Si WPF no admite esta entrada en este momento, ¿hay alguna manera de hacerlo mediante la interoperabilidad con código no administrado?

Gracias!

+0

Esto no sólo funciona? Muy decepcionante. –

+0

No con .NET 3.5 en Windows XP, no en mi máquina. –

Respuesta

5

Llame al método AddHook() en su constructor de ventana para que pueda espiar los mensajes. Busque WM_MOUSEHWHEEL, mensaje 0x20e. Use wParam.ToInt32() >> 16 para obtener la cantidad de movimiento, un múltiplo de 120.

+0

Incluso después de agregar un controlador con AddHook, la ventana aún no puede detectar cuándo realizo la entrada con la rueda de inclinación central. También he confirmado que, si bien Microsoft Word, por ejemplo, puede detectar la entrada de rueda inclinada, al mismo tiempo Microsoft Spy ++ no detectará la entrada en la misma ventana. –

+0

¿El mouse viene con algún tipo de utilidad, cualquier cosa que haya instalado? No sería raro que el fabricante del mouse lo incluyera, agregando soporte de desplazamiento lateral a programas que no lo implementan ellos mismos. Pocos lo hacen. Tal utilidad solo reconocería programas populares, como Word o su navegador. No es tuyo. Debería volver a verlo en la pestaña Procesos de TaskMgr.exe. –

+0

Sí, es Intellitype. Su solución debe ser viable, así que la marcaré como respuesta. –

1

T. Webster publicó un WPF code snippet that adds horizontal mouse scroll support en cualquier ScrollViewer y DependancyObject. Utiliza los mensajes AddHook y de ventana como otros han descrito.

Pude adaptar esto a un comportamiento bastante rápido y conectarlo a un ScrollViewer en XAML.

+0

Gracias por el crédito. –

+0

Doh, ni siquiera me di cuenta de que eres el remitente: P – LongZheng

+0

T. Webster, he encontrado un problema con tu fragmento que consiste en que las ruedas de desplazamiento del mouse/controlador de Logitech y los controladores del panel táctil de Apple hacen que tu código se bloquee con un " Error aritmético " – LongZheng

4

acabo de hacer una clase que añade la PreviewMouseHorizontalWheel y MouseHorizontalWheel eventos inherentes a todas UiElements. Estos eventos incluyen como parámetro un MouseHorizontalWheelEventArgs HorizontalDelta.

Actualización 3

El valor de inclinación se invierte de acuerdo con las normas de WPF, donde hasta es positivo y negativo es negativo, por lo que hizo que se fue positivo y derecho negativo.

Actualización 2

Si el AutoEnableMouseHorizontalWheelSupport se establece en true (como está por defecto) no hay ningún requisito especial para usar esos eventos.

Sólo si se establece en false continuación, tendrá que llamar a cualquiera MouseHorizontalWheelEnabler.EnableMouseHorizontalWheel(X) donde X es el elemento de nivel superior (Ventana, emergente o ContextMenu) o MouseHorizontalWheelEnabler.EnableMouseHorizontalWheelForParentOf(X) con el Elemento para habilitar el soporte para. Puede leer los documentos proporcionados para obtener más información sobre esos métodos.

Tenga en cuenta que todo esto no hace nada en XP, ya que WM_MOUSE-H-WHEEL se agregó en Vista.

MouseHorizontalWheelEnabler.cs

using System; 
using System.Collections.Generic; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Controls.Primitives; 
using System.Windows.Input; 
using System.Windows.Interop; 
using JetBrains.Annotations; 

namespace WpfExtensions 
{ 
    public static class MouseHorizontalWheelEnabler 
    { 
     /// <summary> 
     /// When true it will try to enable Horizontal Wheel support on parent windows/popups/context menus automatically 
     /// so the programmer does not need to call it. 
     /// Defaults to true. 
     /// </summary> 
     public static bool AutoEnableMouseHorizontalWheelSupport = true; 

     private static readonly HashSet<IntPtr> _HookedWindows = new HashSet<IntPtr>(); 

     /// <summary> 
     /// Enable Horizontal Wheel support for all the controls inside the window. 
     /// This method does not need to be called if AutoEnableMouseHorizontalWheelSupport is true. 
     /// This does not include popups or context menus. 
     /// If it was already enabled it will do nothing. 
     /// </summary> 
     /// <param name="window">Window to enable support for.</param> 
     public static void EnableMouseHorizontalWheelSupport([NotNull] Window window) { 
      if (window == null) { 
       throw new ArgumentNullException(nameof(window)); 
      } 

      if (window.IsLoaded) { 
       // handle should be available at this level 
       IntPtr handle = new WindowInteropHelper(window).Handle; 
       EnableMouseHorizontalWheelSupport(handle); 
      } 
      else { 
       window.Loaded += (sender, args) => { 
        IntPtr handle = new WindowInteropHelper(window).Handle; 
        EnableMouseHorizontalWheelSupport(handle); 
       }; 
      } 
     } 

     /// <summary> 
     /// Enable Horizontal Wheel support for all the controls inside the popup. 
     /// This method does not need to be called if AutoEnableMouseHorizontalWheelSupport is true. 
     /// This does not include sub-popups or context menus. 
     /// If it was already enabled it will do nothing. 
     /// </summary> 
     /// <param name="popup">Popup to enable support for.</param> 
     public static void EnableMouseHorizontalWheelSupport([NotNull] Popup popup) { 
      if (popup == null) { 
       throw new ArgumentNullException(nameof(popup)); 
      } 

      if (popup.IsOpen) { 
       // handle should be available at this level 
       // ReSharper disable once PossibleInvalidOperationException 
       EnableMouseHorizontalWheelSupport(GetObjectParentHandle(popup.Child).Value); 
      } 

      // also hook for IsOpened since a new window is created each time 
      popup.Opened += (sender, args) => { 
       // ReSharper disable once PossibleInvalidOperationException 
       EnableMouseHorizontalWheelSupport(GetObjectParentHandle(popup.Child).Value); 
      }; 
     } 

     /// <summary> 
     /// Enable Horizontal Wheel support for all the controls inside the context menu. 
     /// This method does not need to be called if AutoEnableMouseHorizontalWheelSupport is true. 
     /// This does not include popups or sub-context menus. 
     /// If it was already enabled it will do nothing. 
     /// </summary> 
     /// <param name="contextMenu">Context menu to enable support for.</param> 
     public static void EnableMouseHorizontalWheelSupport([NotNull] ContextMenu contextMenu) { 
      if (contextMenu == null) { 
       throw new ArgumentNullException(nameof(contextMenu)); 
      } 

      if (contextMenu.IsOpen) { 
       // handle should be available at this level 
       // ReSharper disable once PossibleInvalidOperationException 
       EnableMouseHorizontalWheelSupport(GetObjectParentHandle(contextMenu).Value); 
      } 

      // also hook for IsOpened since a new window is created each time 
      contextMenu.Opened += (sender, args) => { 
       // ReSharper disable once PossibleInvalidOperationException 
       EnableMouseHorizontalWheelSupport(GetObjectParentHandle(contextMenu).Value); 
      }; 
     } 

     private static IntPtr? GetObjectParentHandle([NotNull] DependencyObject depObj) { 
      if (depObj == null) { 
       throw new ArgumentNullException(nameof(depObj)); 
      } 

      var presentationSource = PresentationSource.FromDependencyObject(depObj) as HwndSource; 
      return presentationSource?.Handle; 
     } 

     /// <summary> 
     /// Enable Horizontal Wheel support for all the controls inside the HWND. 
     /// This method does not need to be called if AutoEnableMouseHorizontalWheelSupport is true. 
     /// This does not include popups or sub-context menus. 
     /// If it was already enabled it will do nothing. 
     /// </summary> 
     /// <param name="handle">HWND handle to enable support for.</param> 
     /// <returns>True if it was enabled or already enabled, false if it couldn't be enabled.</returns> 
     public static bool EnableMouseHorizontalWheelSupport(IntPtr handle) { 
      if (_HookedWindows.Contains(handle)) { 
       return true; 
      } 

      _HookedWindows.Add(handle); 
      HwndSource source = HwndSource.FromHwnd(handle); 
      if (source == null) { 
       return false; 
      } 

      source.AddHook(WndProcHook); 
      return true; 
     } 

     /// <summary> 
     /// Disable Horizontal Wheel support for all the controls inside the HWND. 
     /// This method does not need to be called in most cases. 
     /// This does not include popups or sub-context menus. 
     /// If it was already disabled it will do nothing. 
     /// </summary> 
     /// <param name="handle">HWND handle to disable support for.</param> 
     /// <returns>True if it was disabled or already disabled, false if it couldn't be disabled.</returns> 
     public static bool DisableMouseHorizontalWheelSupport(IntPtr handle) { 
      if (!_HookedWindows.Contains(handle)) { 
       return true; 
      } 

      HwndSource source = HwndSource.FromHwnd(handle); 
      if (source == null) { 
       return false; 
      } 

      source.RemoveHook(WndProcHook); 
      _HookedWindows.Remove(handle); 
      return true; 
     } 

     /// <summary> 
     /// Disable Horizontal Wheel support for all the controls inside the window. 
     /// This method does not need to be called in most cases. 
     /// This does not include popups or sub-context menus. 
     /// If it was already disabled it will do nothing. 
     /// </summary> 
     /// <param name="window">Window to disable support for.</param> 
     /// <returns>True if it was disabled or already disabled, false if it couldn't be disabled.</returns> 
     public static bool DisableMouseHorizontalWheelSupport([NotNull] Window window) { 
      if (window == null) { 
       throw new ArgumentNullException(nameof(window)); 
      } 

      IntPtr handle = new WindowInteropHelper(window).Handle; 
      return DisableMouseHorizontalWheelSupport(handle); 
     } 

     /// <summary> 
     /// Disable Horizontal Wheel support for all the controls inside the popup. 
     /// This method does not need to be called in most cases. 
     /// This does not include popups or sub-context menus. 
     /// If it was already disabled it will do nothing. 
     /// </summary> 
     /// <param name="popup">Popup to disable support for.</param> 
     /// <returns>True if it was disabled or already disabled, false if it couldn't be disabled.</returns> 
     public static bool DisableMouseHorizontalWheelSupport([NotNull] Popup popup) { 
      if (popup == null) { 
       throw new ArgumentNullException(nameof(popup)); 
      } 

      IntPtr? handle = GetObjectParentHandle(popup.Child); 
      if (handle == null) { 
       return false; 
      } 

      return DisableMouseHorizontalWheelSupport(handle.Value); 
     } 

     /// <summary> 
     /// Disable Horizontal Wheel support for all the controls inside the context menu. 
     /// This method does not need to be called in most cases. 
     /// This does not include popups or sub-context menus. 
     /// If it was already disabled it will do nothing. 
     /// </summary> 
     /// <param name="contextMenu">Context menu to disable support for.</param> 
     /// <returns>True if it was disabled or already disabled, false if it couldn't be disabled.</returns> 
     public static bool DisableMouseHorizontalWheelSupport([NotNull] ContextMenu contextMenu) { 
      if (contextMenu == null) { 
       throw new ArgumentNullException(nameof(contextMenu)); 
      } 

      IntPtr? handle = GetObjectParentHandle(contextMenu); 
      if (handle == null) { 
       return false; 
      } 

      return DisableMouseHorizontalWheelSupport(handle.Value); 
     } 


     /// <summary> 
     /// Enable Horizontal Wheel support for all that control and all controls hosted by the same window/popup/context menu. 
     /// This method does not need to be called if AutoEnableMouseHorizontalWheelSupport is true. 
     /// If it was already enabled it will do nothing. 
     /// </summary> 
     /// <param name="uiElement">UI Element to enable support for.</param> 
     public static void EnableMouseHorizontalWheelSupportForParentOf(UIElement uiElement) { 
      // try to add it right now 
      if (uiElement is Window) { 
       EnableMouseHorizontalWheelSupport((Window)uiElement); 
      } 
      else if (uiElement is Popup) { 
       EnableMouseHorizontalWheelSupport((Popup)uiElement); 
      } 
      else if (uiElement is ContextMenu) { 
       EnableMouseHorizontalWheelSupport((ContextMenu)uiElement); 
      } 
      else { 
       IntPtr? parentHandle = GetObjectParentHandle(uiElement); 
       if (parentHandle != null) { 
        EnableMouseHorizontalWheelSupport(parentHandle.Value); 
       } 

       // and in the rare case the parent window ever changes... 
       PresentationSource.AddSourceChangedHandler(uiElement, PresenationSourceChangedHandler); 
      } 
     } 

     private static void PresenationSourceChangedHandler(object sender, SourceChangedEventArgs sourceChangedEventArgs) { 
      var src = sourceChangedEventArgs.NewSource as HwndSource; 
      if (src != null) { 
       EnableMouseHorizontalWheelSupport(src.Handle); 
      } 
     } 

     private static void HandleMouseHorizontalWheel(IntPtr wParam) { 
      int tilt = -Win32.HiWord(wParam); 
      if (tilt == 0) { 
       return; 
      } 

      IInputElement element = Mouse.DirectlyOver; 
      if (element == null) { 
       return; 
      } 

      if (!(element is UIElement)) { 
       element = VisualTreeHelpers.FindAncestor<UIElement>(element as DependencyObject); 
      } 
      if (element == null) { 
       return; 
      } 

      var ev = new MouseHorizontalWheelEventArgs(Mouse.PrimaryDevice, Environment.TickCount, tilt) { 
       RoutedEvent = PreviewMouseHorizontalWheelEvent 
       //Source = handledWindow 
      }; 

      // first raise preview 
      element.RaiseEvent(ev); 
      if (ev.Handled) { 
       return; 
      } 

      // then bubble it 
      ev.RoutedEvent = MouseHorizontalWheelEvent; 
      element.RaiseEvent(ev); 
     } 

     private static IntPtr WndProcHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { 
      // transform horizontal mouse wheel messages 
      switch (msg) { 
       case Win32.WM_MOUSEHWHEEL: 
        HandleMouseHorizontalWheel(wParam); 
        break; 
      } 
      return IntPtr.Zero; 
     } 

     private static class Win32 
     { 
      // ReSharper disable InconsistentNaming 
      public const int WM_MOUSEHWHEEL = 0x020E; 
      // ReSharper restore InconsistentNaming 

      public static int GetIntUnchecked(IntPtr value) { 
       return IntPtr.Size == 8 ? unchecked((int)value.ToInt64()) : value.ToInt32(); 
      } 

      public static int HiWord(IntPtr ptr) { 
       return unchecked((short)((uint)GetIntUnchecked(ptr) >> 16)); 
      } 
     } 

     #region MouseWheelHorizontal Event 

     public static readonly RoutedEvent MouseHorizontalWheelEvent = 
      EventManager.RegisterRoutedEvent("MouseHorizontalWheel", RoutingStrategy.Bubble, typeof(RoutedEventHandler), 
      typeof(MouseHorizontalWheelEnabler)); 

     public static void AddMouseHorizontalWheelHandler(DependencyObject d, RoutedEventHandler handler) { 
      var uie = d as UIElement; 
      if (uie != null) { 
       uie.AddHandler(MouseHorizontalWheelEvent, handler); 

       if (AutoEnableMouseHorizontalWheelSupport) { 
        EnableMouseHorizontalWheelSupportForParentOf(uie); 
       } 
      } 
     } 

     public static void RemoveMouseHorizontalWheelHandler(DependencyObject d, RoutedEventHandler handler) { 
      var uie = d as UIElement; 
      uie?.RemoveHandler(MouseHorizontalWheelEvent, handler); 
     } 

     #endregion 

     #region PreviewMouseWheelHorizontal Event 

     public static readonly RoutedEvent PreviewMouseHorizontalWheelEvent = 
      EventManager.RegisterRoutedEvent("PreviewMouseHorizontalWheel", RoutingStrategy.Tunnel, typeof(RoutedEventHandler), 
      typeof(MouseHorizontalWheelEnabler)); 

     public static void AddPreviewMouseHorizontalWheelHandler(DependencyObject d, RoutedEventHandler handler) { 
      var uie = d as UIElement; 
      if (uie != null) { 
       uie.AddHandler(PreviewMouseHorizontalWheelEvent, handler); 

       if (AutoEnableMouseHorizontalWheelSupport) { 
        EnableMouseHorizontalWheelSupportForParentOf(uie); 
       } 
      } 
     } 

     public static void RemovePreviewMouseHorizontalWheelHandler(DependencyObject d, RoutedEventHandler handler) { 
      var uie = d as UIElement; 
      uie?.RemoveHandler(PreviewMouseHorizontalWheelEvent, handler); 
     } 

     #endregion 
    } 
} 

MouseHorizontalWheelEventArgs.cs

using System.Windows.Input; 

namespace WpfExtensions 
{ 
    public class MouseHorizontalWheelEventArgs : MouseEventArgs 
    { 
     public int HorizontalDelta { get; } 

     public MouseHorizontalWheelEventArgs(MouseDevice mouse, int timestamp, int horizontalDelta) 
      : base(mouse, timestamp) { 
      HorizontalDelta = horizontalDelta; 
     } 
    } 
} 

En cuanto a VisualTreeHelpers.FindAncestor, que se define como sigue:

/// <summary> 
/// Returns the first ancestor of specified type 
/// </summary> 
public static T FindAncestor<T>(DependencyObject current) where T : DependencyObject { 
    current = GetVisualOrLogicalParent(current); 

    while (current != null) { 
    if (current is T) { 
     return (T)current; 
    } 
    current = GetVisualOrLogicalParent(current); 
    } 

    return null; 
} 

private static DependencyObject GetVisualOrLogicalParent(DependencyObject obj) { 
    if (obj is Visual || obj is Visual3D) { 
    return VisualTreeHelper.GetParent(obj); 
    } 
    return LogicalTreeHelper.GetParent(obj); 
} 
Cuestiones relacionadas