Событие колеса мыши для работы с зависающим управлением

В моем приложении С# 3.5 Windows Forms у меня есть несколько SplitContainers. В каждом из них есть контроль над списком (заполнение док-станции). Когда фокус находится на одном из этих элементов управления, и я перемещаю колесо мыши, список, который теперь сфокусирован, прокручивается.

Моя задача - прокрутить список, который в настоящее время зависает от мыши, а не тот, который выбран. Возможно ли это в Windows Forms? Если нет, возможно ли это с помощью PInvoke?

Ответ 1

Похоже, вы можете использовать IMessageFilter и PInvoke для обработки этого. Пример в VB можно найти в Переадресация событий колесика мыши в нефокусированные элементы управления Windows Forms. Вы должны легко преобразовать это в С#.

Достопримечательности

Этот класс использует следующие методы для данной задачи:

  • Прослушайте события управления MouseEnter и MouseLeave, чтобы определить, когда указатель мыши находится над элементом управления.
  • Внесите IMessageFilter, чтобы поймать сообщения WM_MOUSEWHEEL в приложении.
  • PInvoke вызов API Windows SendMessage перенаправляет сообщение WM_MOUSEWHEEL в дескриптор элемента управления.
  • Объект IMessageFilter реализуется как одноэлемент класса MouseWheelRedirector и получает доступ к общим членам Attach, Detach и Active.

Используя конвертер VB.NET в С#, вот что получилось:

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);
 }

Ответ 2

У меня был аналогичный вопрос и я нашел эту тему... поэтому отправляю мой запоздалый ответ другим, которые могут найти эту тему. В моем случае я просто хочу, чтобы события колесика мыши проходили независимо от того, какой элемент управления находится под курсором... точно так же, как щелчок правой кнопкой мыши (это было бы странно, если бы щелчок правой кнопкой мыши переходил к фокусу, а не к элементу управления под курсором... Я утверждаю, что то же самое верно для колеса мыши, за исключением того, что мы привыкли к нему).

В любом случае, ответ очень прост. Просто добавьте 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; 
           } 
}

Ответ 4

Это ответ Брайана Кеннеди завершен комментарием Хэнк Шульц:

Сначала вы должны сделать класс, реализующий 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 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);
    }
}