W jaki sposób mogę łatwo przechwycić zdarzenia "myszy w dół" wszystkich formantów w formularzu, bez ręcznego subskrybowania każdego zdarzenia? (C#) Coś jak funkcja "KeyPreview", ale dla zdarzeń myszy.Subskrybowanie zdarzeń myszy wszystkich formantów w formularzu
Odpowiedz
Rozwiązanie 1
Zapisywanie się każda impreza na każdej kontroli w formie jest z pewnością najbardziej Najprostszym podejście do podjęcia, ponieważ wystarczy użyć kodu podanego przez Ramesh.
Jednak inna technika polega na przesłonięciu domyślnej metody przetwarzania komunikatów systemu Windows ("WndProc") w formancie nadrzędnym - w tym przypadku w formularzu zawierającym wszystkie formanty. Ma to efekt uboczny, którego nie można wykryć, gdy kursor myszy przesunie się nad kontrolkami zawartymi w innym elemencie nadrzędnym.
Na przykład nie można wykryć, kiedy kursor myszy znajduje się nad TextBox
, który jest zawarty wewnątrz TabControl
. Dzieje się tak, ponieważ TabControl
będzie kontynuować przetwarzanie wszystkich zdarzeń myszy.
Rozwiązanie 2
Poniżej rozwiązanie pozwoli przezwyciężyć wszystkie problemy próbując wykryć, które kontrolować kursor myszy znajduje się nad zastosowaniem techniki znanej jako okien haki.
Haczyki zasadniczo umożliwiają uwięzienie zdarzeń myszy i klawiatury nawet przed ich wysłaniem do okna z fokusem.
Oto próbka:
public enum HookType : int
{
WH_JOURNALRECORD = 0,
WH_JOURNALPLAYBACK = 1,
WH_KEYBOARD = 2,
WH_GETMESSAGE = 3,
WH_CALLWNDPROC = 4,
WH_CBT = 5,
WH_SYSMSGFILTER = 6,
WH_MOUSE = 7,
WH_HARDWARE = 8,
WH_DEBUG = 9,
WH_SHELL = 10,
WH_FOREGROUNDIDLE = 11,
WH_CALLWNDPROCRET = 12,
WH_KEYBOARD_LL = 13,
WH_MOUSE_LL = 14
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
}
[StructLayout(LayoutKind.Sequential)]
public struct MouseHookStruct
{
public POINT pt;
public int hwnd;
public int hitTestCode;
public int dwExtraInfo;
}
[DllImport("user32.dll", SetLastError = true)]
static extern int SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hInstance, uint dwThreadId);
[DllImport("user32.dll", SetLastError= true)]
static extern int CallNextHookEx(int hook, int code, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
static extern int GetLastError();
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
private static int hHook;
public Form1()
{
InitializeComponent();
hHook = SetWindowsHookEx(HookType.WH_MOUSE, MouseHookProc, IntPtr.Zero, (uint)GetCurrentThreadId());
if (hHook == 0)
MessageBox.Show("GetLastError: " + GetLastError());
}
private int MouseHookProc(int code, IntPtr wParam, IntPtr lParam)
{
//Marshall the data from the callback.
MouseHookStruct mouseInfo = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
if (code < 0)
{
return CallNextHookEx(hHook, code, wParam, lParam);
}
else
{
//Create a string variable that shows the current mouse coordinates.
String strCaption = "x = " + mouseInfo.pt.X.ToString("d") +
" y = " + mouseInfo.pt.Y.ToString("d");
//You must get the active form because it is a static function.
Form tempForm = Form.ActiveForm;
Control c = Control.FromHandle((IntPtr)mouseInfo.hwnd);
if (c != null)
label1.Text = c.Name;
else
label1.Text = "Control not found";
//Set the caption of the form.
tempForm.Text = strCaption;
return CallNextHookEx(hHook, code, wParam, lParam);
}
}
Inne kontrakty w formularzu nie mogą nasłuchiwać obsługi zdarzeń myszy w formularzu. Ponieważ każdy kontroler ma własne listy obserwatorów zdarzeń myszy.
ale można zapisać się każdy zdarzeń Kontrola zdarzeń myszy do formy myszy
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MDown);
this.label1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MDown);
this.ListBox1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MDown);
ten sposób można mieć jeden obsługi dla wszystkich zdarzeń steruje myszką.
I okazało się, że najlepszym rozwiązaniem dla moich celów.
Utwórz nową klasę pochodną od IMessageFilter
:
public class GlobalMouseHandler : IMessageFilter
{
private const int WM_LBUTTONDOWN = 0x201;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_LBUTTONDOWN)
{
// do something
((YourMainForm)Form.ActiveForm).YourMainForm_Click(null, null);
}
return false;
}
}
Następnie w formularzu głównym dodać do zarejestrowania filtr wiadomość:
GlobalMouseHandler globalClick = new GlobalMouseHandler();
Application.AddMessageFilter(globalClick);
i dodać tę funkcję, aby robić, co trzeba, w twojej formie:
public void YourMainForm_Click(object sender, EventArgs e)
{
// do anything here...
}