簡體   English   中英

捕獲用戶控件中的所有鼠標點擊和按鍵

[英]Capture ALL mouse clicks and key presses in a user control

我想捕獲特定用戶控件中的所有按鍵和鼠標點擊。 我需要這樣做才能創建空閑檢測,以便在用戶有一段時間沒有在用戶控件中執行任何操作時我可以解鎖實體。

我的第一次嘗試是使用PreProcessMessage來檢測 Windows 消息,但它似乎沒有被調用? 我的第二個是使用 DefWndProc,但它不是為那些消息調用的。

如何為用戶控件及其所有子控件獲取WM_KEYDOWNWM_MOUSEUP

更新

ProcessKeyPreview用於檢測密鑰

對於鍵盤方面,您可以嘗試覆蓋UserControlProcessCmdKey事件。

UserControl.cs

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    //a key has been pressed within your user control
    //invoke event or some other action
    //...

    return base.ProcessCmdKey(ref msg, keyData);
}

對於鼠標,我想您的 UserControl 實現IMessageFilter接口的PreFilterMessage方法可能是前進的方向(盡管我不確定這是否特定於用戶控件)。

編輯

我快速瀏覽了IMessageFilter ,我認為我有一些可能IMessageFilter東西,盡管它看起來有點令人費解。

創建一個實現IMessageFilterMessageHandler

class MessageHandler : IMessageFilter
{
     private int WM_LBUTTONUP = 0x0202; //left mouse up
     private int WM_MBUTTONUP = 0x0208; //middle mouse up
     private int WM_RBUTTONUP = 0x0205; //right mouse up

     //stores the handles of the children controls (and actual user control)
     List<IntPtr> windowHandles = new List<IntPtr>();
     public MessageHandler(List<IntPtr> wnds)
     {
         windowHandles = wnds;
     }


     public bool PreFilterMessage(ref Message m)
     {
         //make sure that any click is within the user control's window or 
         //its child controls before considering it a click (required because IMessageFilter works application-wide)
         if (windowHandles.Contains(m.HWnd) && (m.Msg == WM_LBUTTONUP || m.Msg == WM_MBUTTONUP || m.Msg == WM_RBUTTONUP))
         {
              Debug.WriteLine("Clicked");                
         }
         return false;
     }
 }

在您的用戶控件(或新類)中,您可以使用 P/Invoke 來獲取給定父級的子窗口。 來自pinvoke.net

public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);

[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);

private static List<IntPtr> GetChildWindows(IntPtr parent, bool addParent = true)
{
    List<IntPtr> result = new List<IntPtr>();
    GCHandle listHandle = GCHandle.Alloc(result);
    try
    {
        EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
        EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
    }
    finally
    {
        if (listHandle.IsAllocated)
           listHandle.Free();
    }


    if (addParent)
      result.Add(parent);

    return result;
}


private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
    GCHandle gch = GCHandle.FromIntPtr(pointer);
    List<IntPtr> list = gch.Target as List<IntPtr>;
    if (list == null)
    {
        throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
    }
    list.Add(handle);
    //  You can modify this to check to see if you want to cancel the operation, then return a null here
    return true;
}

GetChildWindows將返回我們需要的窗口。 我稍微修改了它並添加了一個可選的addParent bool,它將用戶控件本身添加到列表中。

在您的UserControl.cs構造函數中,我們可以注冊MessageFilter並傳遞句柄列表(或者您可以通過不同的形式添加它,只要您知道用戶控件的句柄)。

public MyControl()
{
    InitializeComponent();
    Application.AddMessageFilter(new MessageHandler(GetChildWindows(this.Handle)));
}

您現在應該能夠檢測到用戶控件及其任何子窗口中的三個鼠標按鈕向上事件。 不要忘記在您的應用程序關閉或UserControl關閉/處置時調用Application.RemoveMessageFilter

正如我所說,這不是最直接的方法,但如果標准事件處理程序不適合您,它會捕獲點擊(我假設您已經嘗試訂閱標准鼠標點擊事件)。

我會捕捉以下事件:

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.click.aspx http://msdn.microsoft.com/en-us/library/system.windows.forms.control .textchanged.aspx以及子控件的任何單擊/更改事件。 讓所有事件處理程序調用相同的公共函數來保存最后一個操作的時間戳。

Click += ClickHandler;
TextChanged += ChangedHandler;

foreach(var control in Controls)
{
    control.Click += ClickHandler;
    control.TextChanged += ChangedHandler;
}

(不要忘記稍后取消訂閱這些控件,如果您有控件動態添加和刪除,請適當處理訂閱/取消訂閱。)

如果無論哪個應用程序具有焦點都可以捕獲它們不是問題,那么您始終可以使用Gobal Keyboard/Mouse hook 在這里詳細解釋有點太復雜了,但你可以隨時參考我的問題,我做了類似的事情。

對於鍵盤,如前所述,請使用:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    //invoke event or some other action
    return base.ProcessCmdKey(ref msg, keyData);
}

這應該解決鼠標事件,為您需要的每個現有鼠標事件創建新事件:

public new event MouseEventHandler MouseClick
{
    add
    {
        base.MouseClick += value;
        foreach (Control control in Controls)
        {
            control.MouseClick += value;
        }
    }
    remove
    {
        base.MouseClick -= value;
        foreach (Control control in Controls)
        {
            control.MouseClick -= value;
        }
    }
}

public new event MouseEventHandler MouseDoubleClick
{
    add
    {
        base.MouseDoubleClick += value;
        foreach (Control control in Controls)
        {
            control.MouseDoubleClick += value;
        }
    }
    remove
    {
        base.MouseDoubleClick -= value;
        foreach (Control control in Controls)
        {
            control.MouseDoubleClick -= value;
        }
    }
}

public new event EventHandler DoubleClick
{
    add
    {
        base.DoubleClick += value;
        foreach (Control control in Controls)
        {
            control.DoubleClick += value;
        }
    }
    remove
    {
        base.DoubleClick -= value;
        foreach (Control control in Controls)
        {
            control.DoubleClick -= value;
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM