views:

117

answers:

2

I have a form, that acts like a drop-down, that I display non-modal. I attach a mouse hook to the form to determine when the mouse is clicked out of it, so that I know to close it - by setting Visible = False.

Because I want the HookProc to handle the last click, I can't dispose the Hook or my Dropdown until I'm sure that my event handler has returned to the HookProc.

It's a bit hard to explain, but I hope the code below makes it a little clearer:-

//Loop to display the dropdown.
Dim dd as New DropDown    
dd.Visible = True
Do While dd.Visible
    Application.DoEvents()
    NativeMethods.MsgWaitForMultipleObjectsEx(0, IntPtr.Zero, 250, &HFF, 4)
Loop
// I want to dispose dd now, but how can I be sure that e.Handled (See below) 
// has been returned to HookProc?

//A handler within dropdown to determine what to do with the mouse click.
Private Sub DropDown_MouseHookClick(ByVal sender As Object, ByVal e As MouseClickEventArgs)
    If IWantToCloseTheDropDown Then
        e.Handled = True
        MyHook.UnHook
        Me.Visible = False
    End If
    // All done, e.Handled is returned to HookProc.
    // But which happens first? Will e.Handled arrive at HookProc first, or will
    // the form display loop, above, notice that Visible is now False?
End Sub

//The main part of the hooking class.
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
        Dim handle As Integer = MyMouseHookStruct.hwnd
        Dim c As Control = Control.FromHandle(New IntPtr(handle))
        If MouseUpOrDown Then
            Dim e As MouseHookClickEventArgs
            OnMouseClick(e)
            If e.Handled Then
                Return 1
            EndIf 
        End If
        Return CallNextHookEx(hHook, nCode, wParam, lParam)
    End If
End Function
A: 

Why not just handle the focus events? Form_LostFocus will tell you when they focus on another control/form. At that point you can hide your form.

A mouse hook seems like overkill for detecting if your form has focus or not.

Jacob Ewald
I use a mouse hook because there are two ways the dropdown can be left. One is by clicking out of it (in which case I want it to close). Another is if the property grid, which it houses and which I have limited control over, opens another form - ie a TypeEditor. In this case I don't want to close my dropdown.
Jules
You may be able to handle the PropertyValueChanged event on your PropertyGrid and re-open the drop down when that particular value changes (the one with the TypeEditor). If the event fires then the dialog was closed and you can open the drop down again.
Jacob Ewald
A: 

You are bypassing .NET mechanisms to handle windows events

You should not need anything native to deal with WinForms. Also the whole code is garbage collected so you do SHOULD not have to worry about deleting hooks

You might want to look at the form.deactivate event

Eric