Захват событий мыши из каждого компонента на С# 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);
Ответы
Ответ 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 в своей форме.