2012-06-14 212 views
9

在我的C#3.5 Windows窗體應用程序中,我有幾個SplitContainers。每個(碼頭填充)都有一個列表控件。當焦點在這些控件之一,我移動鼠標滾輪,列表中,這是現在的重點,滾動。鼠標滾輪事件與懸停控件一起工作

我的任務是滾動列表,這是當前鼠標懸停,而不是被選中的。在Windows窗體中可能嗎?如果沒有,是否可以使用PInvoke?

+0

看起來他們在Windows 10的標準行爲中「滾動鼠標光標所在的位置」。實際上,在大多數情況下,這是一種煩人的行爲。 – Nyerguds

回答

9

它看起來像你可以使用IMessageFilter和PInvoke來處理這個。在VB的例子可以在Redirect Mouse Wheel Events to Unfocused Windows Forms Controls找到。你應該能夠輕鬆地將其轉換爲C#。

興趣點

的這個類使用給定的任務以下技術:

  • 聽控制的的MouseEnter和鼠標離開事件,以確定當鼠標指針懸停在控制。
  • 實現IMessageFilter以捕獲應用程序中的WM_MOUSEWHEEL消息。
  • PInvoke Windows API調用SendMessage將WM_MOUSEWHEEL消息重定向到控件的句柄。
  • IMessageFilter對象作爲MouseWheelRedirector類的單例實現,並由共享成員Attach,Detach和Active訪問。

使用a VB.NET to C# converter,這是你到底是什麼了:

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Data; 
using System.Diagnostics; 

using System.Windows.Forms; 
using System.Runtime.InteropServices; 

public class MouseWheelRedirector : IMessageFilter 
{ 
    private static MouseWheelRedirector instance = null; 
    private static bool _active = false; 
    public static bool Active 
    { 
     get { return _active; } 
     set 
     { 
      if (_active != value) 
      { 
      _active = value; 
      if (_active) 
      { 
       if (instance == null) 
       { 
        instance = new MouseWheelRedirector(); 
       } 
       Application.AddMessageFilter(instance); 
      } 
      else 
      { 
       if (instance != null) 
       { 
        Application.RemoveMessageFilter(instance); 
       } 
      } 
      } 
     } 
    } 

    public static void Attach(Control control) 
    { 
     if (!_active) 
      Active = true; 
     control.MouseEnter += instance.ControlMouseEnter; 
     control.MouseLeave += instance.ControlMouseLeaveOrDisposed; 
     control.Disposed += instance.ControlMouseLeaveOrDisposed; 
    } 

    public static void Detach(Control control) 
    { 
     if (instance == null) 
      return; 
     control.MouseEnter -= instance.ControlMouseEnter; 
     control.MouseLeave -= instance.ControlMouseLeaveOrDisposed; 
     control.Disposed -= instance.ControlMouseLeaveOrDisposed; 
     if (object.ReferenceEquals(instance.currentControl, control)) 
      instance.currentControl = null; 
    } 

    private MouseWheelRedirector() 
    { 
    } 


    private Control currentControl; 
    private void ControlMouseEnter(object sender, System.EventArgs e) 
    { 
     var control = (Control)sender; 
     if (!control.Focused) 
     { 
      currentControl = control; 
     } 
     else 
     { 
      currentControl = null; 
     } 
    } 

    private void ControlMouseLeaveOrDisposed(object sender, System.EventArgs e) 
    { 
     if (object.ReferenceEquals(currentControl, sender)) 
     { 
      currentControl = null; 
     } 
    } 

    private const int WM_MOUSEWHEEL = 0x20a; 
    public bool PreFilterMessage(ref System.Windows.Forms.Message m) 
    { 
     if (currentControl != null && m.Msg == WM_MOUSEWHEEL) 
     { 
      SendMessage(currentControl.Handle, m.Msg, m.WParam, m.LParam); 
      return true; 
     } 
     else 
     { 
      return false; 
     } 
    } 

    [DllImport("user32.dll", SetLastError = false)] 
    private static extern IntPtr SendMessage(
     IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); 
} 
+0

這似乎不適用於使用右邊緣或雙面滾動等方法的大多數常用觸摸板。看起來他們的驅動程序做了一些黑魔法,將滾動消息直接發送到控件的滾動條,而不是像發送「WM_MOUSEWHEEL」一樣發送到控件。任何想法如何解決這個問題? – Nyerguds

+0

嗯。看起來像NumericUpDown不想聽這個。 – Nyerguds

5

我有類似的問題,發現這個線程...所以我發佈遲來的答案爲別人誰可能會發現這個線程。在我的情況下,我只想讓鼠標滾輪事件進入光標下的任何控件......就像右鍵單擊一樣(如果右鍵單擊到焦點控件而不是光標下的控件,將會令人困惑......我認爲這同樣適用於鼠標滾輪正確的,除了我們已經習慣了它)。

無論如何,答案是非常容易的。只是PreFilterMessage添加到您的應用程序,並把它重定向鼠標滾輪事件鼠標下的控制:

public bool PreFilterMessage(ref Message m) 
    { 
     switch (m.Msg) 
     { 
      case WM_MOUSEWHEEL: // 0x020A 
      case WM_MOUSEHWHEEL: // 0x020E 
       IntPtr hControlUnderMouse = WindowFromPoint(new Point((int)m.LParam)); 
       if (hControlUnderMouse == m.HWnd) 
        return false; // already headed for the right control 
       else 
       { 
        // redirect the message to the control under the mouse 
        SendMessage(hControlUnderMouse, m.Msg, m.WParam, m.LParam); 
        return true; 
       } 
      default: 
       return false; 
      } 
} 
+0

這裏有一點遺漏。您需要DllImport WindowFromPoint()和SendMessage: '[DllImport(「user32.dll」)] static extern IntPtr WindowFromPoint(Point p); [DllImport(「user32.dll」,CharSet = CharSet.Auto)] static extern IntPtr SendMessage(IntPtr hWnd,UInt32 Msg,IntPtr wParam,IntPtr lParam);' 另外,PreFilterMessage()來自IMessageFilter,並且該實現需要傳遞給ApplicationAddMessageFilter()。 一旦完成,我的應用程序中的所有面板都可以在鼠標下滾動。但是,雙擊不再突出顯示文字。奇。 –

2

這是布賴恩·肯尼迪的回答與漢克·舒爾茨評論完成:

第一你應該做一個類實現IMessageFilter

public class MessageFilter : IMessageFilter 
{ 
    private const int WM_MOUSEWHEEL = 0x020A; 
    private const int WM_MOUSEHWHEEL = 0x020E; 

    [DllImport("user32.dll")] 
    static extern IntPtr WindowFromPoint(Point p); 
    [DllImport("user32.dll", CharSet = CharSet.Auto)] 
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); 

    public bool PreFilterMessage(ref Message m) 
    { 
     switch (m.Msg) 
     { 
      case WM_MOUSEWHEEL: 
      case WM_MOUSEHWHEEL: 
       IntPtr hControlUnderMouse = WindowFromPoint(new Point((int)m.LParam)); 
       if (hControlUnderMouse == m.HWnd) 
       { 
        //Do nothing because it's already headed for the right control 
        return false; 
       } 
       else 
       { 
        //Send the scroll message to the control under the mouse 
        uint u = Convert.ToUInt32(m.Msg); 
        SendMessage(hControlUnderMouse, u, m.WParam, m.LParam); 
        return true; 
       } 
      default: 
       return false; 
     } 
    } 
} 

實例:

public partial class MyForm : Form 
{ 
    MessageFilter mf = null; 

    private void MyForm_Load(object sender, EventArgs e) 
    { 
     mf= new MessageFilter(); 
     Application.AddMessageFilter(mf); 
    } 

    private void MyForm_FormClosing(object sender, FormClosingEventArgs e) 
    { 
     Application.RemoveMessageFilter(mf); 
    } 
}