我有一個窗體顯示,而不是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
。
創建兩個表單Form1
和DropDown
。
(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);
}
}
}
嗨,謝謝你。我從來沒有聽說過IMessageFilter,它似乎照顧着我。我嘗試用IMessageFilter替換現有的代碼,但遇到了問題。爲了忽略與我的下拉式無關的事件,我在我的下拉列表中激活和取消激活事件中使用了Hook和UnHook。但是,在PreFilterMessage之前會激活停用。使用PreFilterMessage,我可以確定被點擊的控件以及它屬於哪種形式。如何確定該表單是否在我的下拉菜單後創建? – Jules 2009-11-15 17:41:02
我可能有辦法做到這一點。首先,我會在顯示我的下拉列表之前列出所有打開的表單,在PreFiltureMessage中,如果點擊的控件屬於已保存集合中的表單,則我處理點擊並關閉下拉菜單。否則,我忽略。 – Jules 2009-11-15 18:36:59
忽略我上面的動作。我決定堅持使用我的鉤子而不是IMessageFilter,並且添加了鼠標事件過濾器已經解決了我的問題。再次感謝。 – Jules 2009-11-16 14:13:09