Захват событий мыши из каждого компонента на С# WInForm - программирование

Захват событий мыши из каждого компонента на С# WInForm

У меня проблема с MouseEvents в моем приложении WinForm С#. Я хочу получить ВСЕ щелчки мышью в моем приложении, но я не хочу помещать слушатель в каждый дочерний компонент, а также не использовать ловушку мыши Windows.

На Flash я мог бы поставить слушателя на сцену, чтобы получить все MouseEvents в фильме. Есть ли такая вещь на С#? Глобальный MouseListener?


Редактировать: я создаю этот класс из IMessageFilter и используется Application.AddMessageFilter.

public class GlobalMouseHandler : IMessageFilter{

    private const int WM_LBUTTONDOWN = 0x201;

    public bool PreFilterMessage(ref Message m){
        if (m.Msg == WM_LBUTTONDOWN) {
            // Do stuffs
        }
        return false;
    }
}

И поместите этот код в элементы управления, которые должны прослушивать глобальные щелчки:

GlobalMouseHandler globalClick = new GlobalMouseHandler();
Application.AddMessageFilter(globalClick);
4b9b3361

Ответ 1

Один простой способ сделать это - добавить фильтр цикла сообщения, вызвав Application.AddMessageFilter и написать класс, реализующий интерфейс IMessageFilter.

Через IMessageFilter.PreFilterMessage, ваш класс получает сообщения о входах, которые проходят через цикл сообщений приложения. PreFilterMessage также принимает решение о передаче этих сообщений конкретному элементу управления, которому они предназначены.

Одна из сложностей, которую вводит этот подход, связана с сообщениями Windows через структуру Message, переданную вашему методу PreFilterMessage. Это означает ссылку на документацию Win32 на WM\_LBUTTONDOWN, WM\_MOUSEMOVE, WM\_LBUTTONUP и т.д. Вместо обычных событий MouseDown, MouseMove и MouseUp.

Ответ 2

Пример класса

class CaptureEvents : IMessageFilter
{
    #region IMessageFilter Members
    public delegate void Callback(int message);
    public event Callback MessageReceived;

    IntPtr ownerWindow;
    Hashtable interestedMessages = null;
    CaptureEvents(IntPtr handle, int[] messages)
    {
        ownerWindow = handle;
        for(int c = 0; c < messages.Length ; c++)
        {
            interestedMessages[messages[c]] = 0;
        }
    }
    public bool PreFilterMessage(ref Message m)
    {
        if (m.HWnd == ownerWindow && interestedMessages.ContainsKey(m.Msg))
        {
            MessageReceived(m.Msg);
        }
        return true;
    }

    #endregion
}

Ответ 3

Если вы не хотите обрабатывать сообщения, переопределяя Form.PreProcessMessage или Form.WndProc, тогда вы можете подклассировать Form для привязки обработчика событий ко всем событиям MouseClick из различных элементов управления в форме.
EDIT: забыл переписать через дочерние элементы управления элементами формы.

    public class MousePreviewForm : Form
    {
      protected override void OnClosed(EventArgs e)
      {
         UnhookControl(this as Control);
         base.OnClosed(e);
      }

      protected override void OnLoad(EventArgs e)
      {
         base.OnLoad(e);

         HookControl(this as Control);
      }

      private void HookControl(Control controlToHook)
      {
         controlToHook.MouseClick += AllControlsMouseClick;
         foreach (Control ctl in controlToHook.Controls)
         {
            HookControl(ctl);
         }      
      }

      private void UnhookControl(Control controlToUnhook)
      {
         controlToUnhook.MouseClick -= AllControlsMouseClick;
         foreach (Control ctl in controlToUnhook.Controls)
         {
            UnhookControl(ctl);
         }
      }

      void AllControlsMouseClick(object sender, MouseEventArgs e)
      {
         //do clever stuff here...
         throw new NotImplementedException();
      }
   }

Затем ваши формы должны были бы выводиться из MousePreviewForm не System.Windows.Forms.Form.

Ответ 4

Взгляните на эту статью. Он рекурсивно подхватывает все контрольные события и транслирует их. Вы также можете переопределить WndProc в своей форме.