2010-11-11 6 views
5

Comment activer WPF pour répondre au défilement horizontal à l'aide de la roulette d'inclinaison de la souris? Par exemple, j'ai une mini souris Microsoft Explorer et ont essayé le contenu défilement horizontal contenu dans un ScrollViewer avecComment faire défiler horizontalement dans WPF en utilisant la molette de la souris?

HorizontalScrollBarVisibility="Visible" 

mais le contenu ne défile pas horizontalement. Le défilement vertical, cependant, fonctionne de manière fiable comme d'habitude.

Si une telle entrée n'est pas directement supportée par WPF pour l'instant, est-il possible de le faire en utilisant interop avec du code non managé?

Merci!

+0

Cela ne fonctionne pas seulement? Très décevant. –

+0

Pas avec .NET 3.5 sur Windows XP, pas sur ma machine. –

Répondre

5

Appelez la méthode AddHook() dans votre constructeur Window pour pouvoir espionner les messages. Recherchez WM_MOUSEHWHEEL, message 0x20e. Utilisez wParam.ToInt32() >> 16 pour obtenir la quantité de mouvement, un multiple de 120.

+0

Même après avoir ajouté un gestionnaire avec AddHook, la fenêtre ne peut toujours pas détecter lorsque j'effectue une entrée avec la molette d'inclinaison centrale. J'ai également confirmé que, tandis que Microsoft Word par exemple, peut détecter l'entrée d'inclinaison de roue, cela en même temps Microsoft Spy ++ ne détectera pas l'entrée sur la même fenêtre. –

+0

La souris est-elle livrée avec une sorte d'utilitaire, tout ce que vous avez installé? Il ne serait pas rare que le fabricant de la souris inclue ceci, ajoutant un support de défilement latéral aux programmes qui ne l'implémentent pas eux-mêmes. Peu de faire. Un tel utilitaire ne reconnaitrait que les programmes populaires, comme Word ou votre navigateur. Pas le vôtre. Vous devriez le voir dans l'onglet Processus TaskMgr.exe. –

+0

Oui, c'est Intellitype. Votre solution devrait être réalisable, donc je vais la marquer comme réponse. –

1

T. Webster a publié un WPF code snippet that adds horizontal mouse scroll support dans n'importe quel ScrollViewer et DependancyObject. Il utilise les messages AddHook et window comme d'autres l'ont décrit.

J'ai été capable d'adapter cela assez rapidement à un comportement et de l'attacher à un ScrollViewer en XAML.

+0

Merci pour le crédit. –

+0

Doh, je n'ai même pas remarqué que vous êtes l'émetteur: P – LongZheng

+0

T. Webster, J'ai trouvé un problème avec votre extrait qui est que les roulettes de souris/pilote Logitech et les pilotes de trackpad Apple provoquent le crash de votre code Erreur arithmétique " – LongZheng

4

Je viens de faire une classe qui ajoute la PreviewMouseHorizontalWheel et MouseHorizontalWheel événements attachés à tous UIElements. Ces événements incluent en paramètre un objet HorizontalHorizontalWheelEventArgs HorizontalDelta.

Update 3

La valeur de l'inclinaison a été inversée selon les normes WPF, où jusqu'à est positif et vers le bas est négatif, ainsi fait à gauche et à droite positif négatif.

Mise à jour 2

Si le AutoEnableMouseHorizontalWheelSupport est définie sur true (comme il est par défaut) il n'y a pas d'exigence particulière d'utiliser ces événements.

seulement si elle est définie sur false alors vous devez appeler soit MouseHorizontalWheelEnabler.EnableMouseHorizontalWheel(X) où X est l'élément de niveau supérieur (Fenêtre, Popup ou ContextMenu) ou MouseHorizontalWheelEnabler.EnableMouseHorizontalWheelForParentOf(X) avec l'élément à activer le support. Vous pouvez lire les documents fournis pour plus d'informations sur ces méthodes.

Notez que tout cela ne fait rien sur XP, puisque WM_MOUSE-H-WHEEL a été ajouté sur 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 ce qui concerne VisualTreeHelpers.FindAncestor, il est défini comme suit:

/// <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); 
} 
Questions connexes