2010-11-11 14 views
5

Come si abilita WPF a rispondere allo scorrimento orizzontale utilizzando la rotella del mouse? Per esempio, ho un mini mouse Microsoft Explorer e ho provato in orizzontale scrolling contenuti presenti all'interno di uno ScrollViewer conCome scorrere orizzontalmente in WPF usando la rotella di inclinazione del mouse?

HorizontalScrollBarVisibility="Visible" 

ma il contenuto non lo scorrere orizzontalmente. Lo scorrimento verticale, tuttavia, funziona in modo affidabile come al solito.

Se tale input non è supportato direttamente da WPF in questo momento, esiste un modo per farlo utilizzando l'interoperabilità con codice non gestito?

Grazie!

+0

Questo non solo di lavoro? Molto deludente. –

+0

Non con .NET 3.5 su Windows XP, non sul mio computer. –

risposta

5

Chiama il metodo AddHook() nel costruttore di finestre in modo da poter spiare i messaggi. Cerca WM_MOUSEHWHEEL, messaggio 0x20e. Usa wParam.ToInt32() >> 16 per ottenere l'importo del movimento, un multiplo di 120.

+0

Anche dopo aver aggiunto un gestore con AddHook, la finestra non riesce a rilevare quando eseguo l'input con la rotella centrale. Ho anche confermato che, mentre Microsoft Word, ad esempio, è in grado di rilevare input tilt wheel, allo stesso tempo Microsoft Spy ++ non rileverà l'input sulla stessa finestra. –

+0

Il mouse è arrivato con qualche tipo di utilità, qualsiasi cosa è stata installata? Non sarebbe raro che il produttore del mouse includesse questo, aggiungendo il supporto di scorrimento laterale ai programmi che non lo implementano da soli. Pochi. Tale utility riconoscerebbe solo i programmi più diffusi, come Word o il tuo browser. Non tuo. Dovresti vederlo di nuovo nella scheda Processi TaskMgr.exe. –

+0

Sì, è Intellitype. La tua soluzione dovrebbe essere praticabile, quindi la contrassegnerò come risposta. –

1

T. Webster ha inviato uno WPF code snippet that adds horizontal mouse scroll support a qualsiasi ScrollViewer e DependancyObject. Utilizza i messaggi AddHook e finestra come altri hanno descritto.

Sono stato in grado di adattarlo a un comportamento abbastanza rapidamente e collegarlo a un ScrollViewer in XAML.

+0

Grazie per il merito. –

+0

Doh, non ho nemmeno notato che sei il mittente: P – LongZheng

+0

T. Webster, ho trovato un problema con il tuo snippet che è che scrollwheels su Logitech mouse/driver e driver del trackpad Apple causano il crash del tuo codice con un " Errore aritmetico " – LongZheng

4

Ho appena fatto una classe che aggiunge la PreviewMouseHorizontalWheel e MouseHorizontalWheel eventi collegati a tutti i UIElements. Questi eventi includono come parametro a MouseHorizontalWheelEventArgs HorizontalDelta.

Aggiornamento 3

Il valore di inclinazione è stato invertito secondo gli standard WPF, dove fino è positivo e in basso è negativo, quindi ha reso sinistra positivo e diritto negativo.

Update 2

Se il AutoEnableMouseHorizontalWheelSupport è impostato su true (come lo è di default) non c'è alcun requisito speciale per utilizzare quegli eventi.

Solo se è impostato su false allora si avrà bisogno di chiamare sia MouseHorizontalWheelEnabler.EnableMouseHorizontalWheel(X) dove X è l'elemento di primo livello (Finestra, popup o ContextMenu) o MouseHorizontalWheelEnabler.EnableMouseHorizontalWheelForParentOf(X) con l'Elemento per abilitare il supporto per. Puoi leggere i documenti forniti per maggiori informazioni su questi metodi.

Nota che tutto questo non fa nulla su XP, poiché WM_MOUSE-H-WHEEL è stato aggiunto su 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; 
     } 
    } 
} 

Per quanto riguarda VisualTreeHelpers.FindAncestor, esso è definito come segue:

/// <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); 
} 
Problemi correlati