2013-04-22 56 views
3

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

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

我怎樣才能得到WM_KEYDOWN & WM_MOUSEUP爲用戶控件和所有的孩子?

更新

ProcessKeyPreview工程檢測鍵

回答

3

對於鍵盤方面,您可以嘗試覆蓋UserControl內的ProcessCmdKey事件。

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

對於鼠標,我想你的用戶控件實現IMessageFilter接口的PreFilterMessage方法可以是頭(雖然我不知道方向,如果是可以具體到一個用戶控件)。

編輯

我有一個快速瀏覽一下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。如我所說,這不是最直接的方式,但如果標準事件處理程序不適合您(我假設您已嘗試訂閱標準的鼠標點擊事件),它將捕獲點擊次數。

0

如果趕上他們無論哪個應用程序具有焦點是沒有問題的,你總是可以使用Gobal Keyboard/Mouse hook。在這裏詳細解釋有點太複雜,但你總是可以參考my question,我在那裏做了類似的事情。

1

我會捕獲以下事件:

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而且任何點擊/改變事件子控件。讓所有事件處理函數調用相同的通用函數,以保存最後一個操作的時間戳。

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

(不要忘了以後取消這些控制,如果你控制了動態獲取添加和刪除,處理訂閱/退訂得體。)

+0

根據你的控制是多麼複雜,你可能需要將其作爲遞歸方法。 – 2013-04-22 15:17:07

相關問題