2009-11-15 60 views
0

我有一個窗體顯示,而不是ShowDialog,但通過將其可見屬性設置爲true。這就像是一個下拉菜單。NET和Windows窗體中鼠標鉤的奇怪行爲

表單安裝鼠標鉤,使用SetWindowsHookEx(WH_MOUSE, ...)

我檢測鼠標是否在下拉菜單之外單擊,如果是,請在我的HookProc方法中返回1,然後關閉下拉菜單。

奇怪的是,如果我在我的下拉菜單中單擊外部,文本框仍然會在我的下拉菜單關閉後收到鼠標單擊,即使它已由我的HookProc方法處理。

它變得陌生......如果我點擊一個標籤或按鈕,他們不會收到鼠標點擊,如預期的,在下拉關閉後!

任何想法是怎麼回事?

ETA 2:

你可以忽略我下面的所有代碼,因爲,在進一步的調查中,我發現,這種行爲是在實現一個下拉型窗體至少一個框架控制展出。

要複製,創建一個窗體並添加屬性網格,按鈕,文本框和標籤。將屬性網格的選定對象設置爲字體。

運行表單並選擇字體名稱。出現一個下拉列表。現在點擊表單的文本框。文本框點擊事件被觸發。但是,按鈕或標籤不會發生同樣的情況。

發生了什麼事?

ETA 1:

下面是How to set a Windows hook in Visual C# .NET一些裸機代碼來說明這是怎麼回事。我用轉換器將代碼轉換回C#,但希望它沒問題。我不確定,但您可能需要用Debug.WriteLine替換Console.WriteLine

創建兩個表單Form1DropDown

(1)VB.NET

Form1,添加一個按鈕,標籤和文本框,和下面的代碼。

Imports System.Runtime.InteropServices 

Public Class Form1 

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 
     Console.WriteLine("Button1_Click") 
     Dim dd As New DropDown 
     dd.Visible = True 
     Do While dd.Visible 
      Application.DoEvents() 
      MsgWaitForMultipleObjectsEx(0, IntPtr.Zero, 250, &HFF, 4) 
     Loop 
    End Sub 

    <DllImport("user32.dll", CharSet:=CharSet.Auto, ExactSpelling:=True)> _ 
    Public Shared Function MsgWaitForMultipleObjectsEx(ByVal nCount As Integer, ByVal pHandles As IntPtr, ByVal dwMilliseconds As Integer, ByVal dwWakeMask As Integer, ByVal dwFlags As Integer) As Integer 
    End Function 

    Private Sub Label1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Label1.Click 
     Console.WriteLine("Label1_Click") 
    End Sub 

    Private Sub TextBox1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.Click 
     Console.WriteLine("TextBox1_Click") 
    End Sub 

End Class 

在DropDown中,放置以下代碼。

Imports System.Runtime.InteropServices 

Public Class DropDown 

    Public Delegate Function HookProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer 

    Private hHook As Integer = 0 
    Public Const WH_MOUSE As Integer = 7 
    Private MouseHookProcedure As HookProc 

    <StructLayout(LayoutKind.Sequential)> _ 
    Public Class POINT 
     Public x As Integer 
     Public y As Integer 
    End Class 

    <StructLayout(LayoutKind.Sequential)> _ 
    Public Class MouseHookStruct 
     Public pt As POINT 
     Public hwnd As Integer 
     Public wHitTestCode As Integer 
     Public dwExtraInfo As Integer 
    End Class 

    <DllImport("user32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _ 
    Public Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal lpfn As HookProc, ByVal hInstance As IntPtr, ByVal threadId As Integer) As Integer 
    End Function 

    <DllImport("user32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _ 
    Public Shared Function UnhookWindowsHookEx(ByVal idHook As Integer) As Boolean 
    End Function 

    <DllImport("user32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _ 
    Public Shared Function CallNextHookEx(ByVal idHook As Integer, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer 
    End Function 

    Protected Overrides Sub OnDeactivate(ByVal e As System.EventArgs) 
     MyBase.OnDeactivate(e) 
     UnhookWindowsHookEx(hHook) 
     hHook = 0 
    End Sub 

    Public Sub New() 
     InitializeComponent() 
     MouseHookProcedure = New HookProc(AddressOf MouseHookProc) 
     hHook = SetWindowsHookEx(WH_MOUSE, MouseHookProcedure, New IntPtr(0), AppDomain.GetCurrentThreadId()) 
    End Sub 

    Public Function MouseHookProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer 
     Dim MyMouseHookStruct As MouseHookStruct = DirectCast(Marshal.PtrToStructure(lParam, GetType(MouseHookStruct)), MouseHookStruct) 
     If nCode < 0 Then 
      Return CallNextHookEx(hHook, nCode, wParam, lParam) 
     Else 
      Select Case CInt(wParam) 
       Case &H21, &HA1, &HA4, &H204, &H207, &HA7, &H201 
        Me.Visible = False 
        Return 1 
      End Select 
      Return CallNextHookEx(hHook, nCode, wParam, lParam) 
     End If 
    End Function 

End Class 

(2)C#

在Form1中添加一個按鈕,標籤和文本框和以下代碼:

using System.Runtime.InteropServices; 

public class Form1 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
     Button1.Click += Button1_Click; 
     Label1.Click += Label1_Click; 
     TextBox1.Click += TextBox1_Click; 
    } 

    private void Button1_Click(System.Object sender, System.EventArgs e) 
    { 
     Console.WriteLine("Button1_Click"); 
     DropDown dd = new DropDown(); 
     dd.Visible = true; 
     while (dd.Visible) { 
      Application.DoEvents(); 
      MsgWaitForMultipleObjectsEx(0, IntPtr.Zero, 250, 0xff, 4); 
     } 
    } 

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] 
    public static extern int MsgWaitForMultipleObjectsEx(int nCount, IntPtr pHandles, int dwMilliseconds, int dwWakeMask, int dwFlags); 

    private void Label1_Click(object sender, System.EventArgs e) 
    { 
     Console.WriteLine("Label1_Click"); 
    } 

    private void TextBox1_Click(object sender, System.EventArgs e) 
    { 
     Console.WriteLine("TextBox1_Click"); 
    } 
} 

在下拉菜單中,放置以下代碼:

using System.Runtime.InteropServices; 

public class DropDown 
{ 
    public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam); 

    private int hHook = 0; 
    public const int WH_MOUSE = 7; 
    private HookProc MouseHookProcedure; 

    [StructLayout(LayoutKind.Sequential)] 
    public class POINT 
    { 
     public int x; 
     public int y; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    public class MouseHookStruct 
    { 
     public POINT pt; 
     public int hwnd; 
     public int wHitTestCode; 
     public int dwExtraInfo; 
    } 

    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 
    public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId); 

    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 
    public static extern bool UnhookWindowsHookEx(int idHook); 

    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 
    public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam); 

    protected override void OnDeactivate(System.EventArgs e) 
    { 
     base.OnDeactivate(e); 
     UnhookWindowsHookEx(hHook); 
     hHook = 0; 
    } 

    public DropDown() 
    { 
     InitializeComponent(); 
     MouseHookProcedure = new HookProc(MouseHookProc); 
     hHook = SetWindowsHookEx(WH_MOUSE, MouseHookProcedure, new IntPtr(0), AppDomain.GetCurrentThreadId()); 
    } 

    public int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam) 
    { 
     MouseHookStruct MyMouseHookStruct = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct)); 
     if (nCode < 0) { 
      return CallNextHookEx(hHook, nCode, wParam, lParam); 
     } 
     else { 
      switch ((int)wParam) { 
       case 0x21: 
       case 0xa1: 
       case 0xa4: 
       case 0x204: 
       case 0x207: 
       case 0xa7: 
       case 0x201: 
        this.Visible = false; 
        return 1; 
      } 
      return CallNextHookEx(hHook, nCode, wParam, lParam); 
     } 
    } 
} 

回答

1

此問題是由於您過濾鼠標按下消息而不是鼠標上移消息而引起的。您可以像這樣修復:

Select Case CInt(wParam) 
    Case &HA1, &HA4, &HA7, &H201, &H204, &H207 
     Me.Capture = True 
    Case &HA2, &hA5, &HA8, &H202, &H205, &H208 
     Me.Visible = False 
    End Select 

請考慮實現IMessageFilter。

+0

嗨,謝謝你。我從來沒有聽說過IMessageFilter,它似乎照顧着我。我嘗試用IMessageFilter替換現有的代碼,但遇到了問題。爲了忽略與我的下拉式無關的事件,我在我的下拉列表中激活和取消激活事件中使用了Hook和UnHook。但是,在PreFilterMessage之前會激活停用。使用PreFilterMessage,我可以確定被點擊的控件以及它屬於哪種形式。如何確定該表單是否在我的下拉菜單後創建? – Jules 2009-11-15 17:41:02

+0

我可能有辦法做到這一點。首先,我會在顯示我的下拉列表之前列出所有打開的表單,在PreFiltureMessage中,如果點擊的控件屬於已保存集合中的表單,則我處理點擊並關閉下拉菜單。否則,我忽略。 – Jules 2009-11-15 18:36:59

+0

忽略我上面的動作。我決定堅持使用我的鉤子而不是IMessageFilter,並且添加了鼠標事件過濾器已經解決了我的問題。再次感謝。 – Jules 2009-11-16 14:13:09