Странное поведение с помощью мыши в .NET и Windows Forms - PullRequest
0 голосов
/ 15 ноября 2009

У меня есть форма, которая отображается не ShowDialog, а путем установки его свойства visible в значение true. Это так, что он ведет себя как выпадающий список.

Форма устанавливает крюк мыши, используя SetWindowsHookEx(WH_MOUSE, ...).

Я определяю, нажата ли мышь за пределами выпадающего списка, и если да, верните 1 в моем методе HookProc и закройте раскрывающийся список.

Странно то, что если я щелкаю за пределами своего раскрывающегося списка в текстовом поле, текстовое поле все равно получает щелчок мыши после закрытия раскрывающегося списка, даже если оно обрабатывается моим HookProc методом.

Становится страннее ... Если я нажму на ярлык или кнопку, они не получат щелчок мыши, как и ожидалось, после того, как раскрывающийся список закроется!

Есть идеи, что происходит?

ETA 2:

Вы можете игнорировать весь мой код ниже, потому что при дальнейшем исследовании я обнаружил, что это поведение проявляется по крайней мере в одном элементе управления каркасом, который реализует форму раскрывающегося типа.

Для репликации создайте форму и добавьте сетку свойств, кнопку, текстовое поле и метку. Установите для выбранного объекта сетки свойств шрифт.

Запустите форму и выберите имя шрифта. Появляется раскрывающийся список. Теперь нажмите на текстовое поле формы. Событие щелчка текстового поля запускается. Однако то же самое не происходит для кнопки или метки.

Что происходит?

ETA 1:

Вот небольшой код из Как установить хук Windows в Visual C # .NET , чтобы продемонстрировать, что происходит Я использовал конвертер для преобразования кода обратно в C #, но, надеюсь, все в порядке. Я не уверен, но вам, возможно, придется заменить Console.WriteLine на Debug.WriteLine.

Создать две формы, Form1 и DropDown.

(1) VB.NET

В Form1 добавьте Button, Label и TextBox и следующий код.

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 добавьте Button, Label и TextBox и следующий код:

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

В DropDown введите следующий код:

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 Ответ

1 голос
/ 15 ноября 2009

Эта проблема вызвана тем, что вы фильтруете сообщение мыши, но не сообщение вверх. Вы можете исправить это так:

  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.

...