Reputation: 1338
I am creating a customized menu and I want the menu to close when the user clicks anywhere else on the form. I tried using .LostFocus, but that only works if the thing they click on can take the focus. Lots of things don't take focus and therefore the menu stays open. I need a listener on the button that says, "if the mouse was clicked and it wasn't on you, then do something (close)".
Any suggestions?
Thanks.
Upvotes: 0
Views: 8173
Reputation: 11
Just in case its useful to someone at some point... This builds on Daniel's answer and other similar suggestions I've found via a web search. This does 2 things I didn't see in other examples. #1 - only add the click handler to non-input controls, and #2, it recursively adds the click handler for controls that contain other controls. This one goes 4 levels deep.
'Add Click Handler to all non-input controls
Private Sub FrmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each c1 As Control In Me.Controls
If Not c1.CanSelect Then
AddHandler c1.MouseClick, AddressOf ClickHandler
For Each c2 As Control In c1.Controls
If Not c2.CanSelect Then
AddHandler c2.MouseClick, AddressOf ClickHandler
For Each c3 As Control In c2.Controls
If Not c3.CanSelect Then
AddHandler c3.MouseClick, AddressOf ClickHandler
For Each c4 As Control In c3.Controls
If Not c4.CanSelect Then
AddHandler c4.MouseClick, AddressOf ClickHandler
End If
Next
End If
Next
End If
Next
End If
Next
End Sub
'Click Handler
Private Sub ClickHandler(sender As Object, e As MouseEventArgs) Handles Me.MouseClick
MsgBox("Do something!!!")
End Sub
Upvotes: 0
Reputation: 1
The only way to do it is with the hook.
Here is some code. You should still read up on it. And you will need to go to project / properties / Debug and uncheck enable the visual studio hosting process checkbox.
This code will count all your left and right mouse clicks anywhere on the screen while the form is running.
For the Form you want it in:
Public Class Form1
Private WithEvents MouseDetector As MouseDetector
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
MouseDetector = New MouseDetector
End Sub
Private Sub Form1_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
RemoveHandler MouseDetector.MouseLeftButtonClick, AddressOf MouseDetector_MouseLeftButtonClick
RemoveHandler MouseDetector.MouseRightButtonClick, AddressOf MouseDetector_MouseRightButtonClick
MouseDetector.Dispose()
End Sub
Private Sub MouseDetector_MouseLeftButtonClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MouseDetector.MouseLeftButtonClick
If IsNumeric(LabelLeft.Text) Then
LabelLeft.Text = CInt(LabelLeft.Text) + 1
Else
LabelLeft.Text = 1
End If
End Sub
Private Sub MouseDetector_MouseRightButtonClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MouseDetector.MouseRightButtonClick
If IsNumeric(LabelRight.Text) Then
LabelRight.Text = CInt(LabelRight.Text) + 1
Else
LabelRight.Text = 1
End If
End Sub
End Class
Add a class to the project and name it MouseDetector and copy the following code into it.
Imports System.Runtime.InteropServices
Imports System.Reflection
Imports System.Windows.Forms
Public Class MouseDetector
Public Event MouseLeftButtonClick(ByVal sender As Object, ByVal e As MouseEventArgs)
Public Event MouseRightButtonClick(ByVal sender As Object, ByVal e As MouseEventArgs)
Private Delegate Function MouseHookCallback(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
Private MouseHookCallbackDelegate As MouseHookCallback
Private MouseHookID As Integer
Public Sub New()
If MouseHookID = 0 Then
MouseHookCallbackDelegate = AddressOf MouseHookProc
MouseHookID = SetWindowsHookEx(CInt(14), MouseHookCallbackDelegate, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly.GetModules()(0)), 0)
If MouseHookID = 0 Then
'error
End If
End If
End Sub
Public Sub Dispose()
If Not MouseHookID = -1 Then
UnhookWindowsHookEx(MouseHookID)
MouseHookCallbackDelegate = Nothing
End If
MouseHookID = -1
End Sub
Private Enum MouseMessages
WM_LeftButtonDown = 513
WM_LeftButtonUp = 514
WM_LeftDblClick = 515
WM_RightButtonDown = 516
WM_RightButtonUp = 517
WM_RightDblClick = 518
End Enum
<StructLayout(LayoutKind.Sequential)> _
Private Structure Point
Public x As Integer
Public y As Integer
End Structure
<StructLayout(LayoutKind.Sequential)> _
Private Structure MouseHookStruct
Public pt As Point
Public hwnd As Integer
Public wHitTestCode As Integer
Public dwExtraInfo As Integer
End Structure
<DllImport("user32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall)> _
Private Shared Function CallNextHookEx( _
ByVal idHook As Integer, _
ByVal nCode As Integer, _
ByVal wParam As IntPtr, _
ByVal lParam As IntPtr) As Integer
End Function
<DllImport("User32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall, SetLastError:=True)> _
Private Shared Function SetWindowsHookEx _
(ByVal idHook As Integer, ByVal HookProc As MouseHookCallback, _
ByVal hInstance As IntPtr, ByVal wParam As Integer) As Integer
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto, CallingConvention:=CallingConvention.StdCall, SetLastError:=True)> _
Private Shared Function UnhookWindowsHookEx(ByVal idHook As Integer) As Integer
End Function
Private Function MouseHookProc(ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
If nCode < 0 Then
Return CallNextHookEx(MouseHookID, nCode, wParam, lParam)
End If
Dim MouseData As MouseHookStruct = Marshal.PtrToStructure(lParam, GetType(MouseHookStruct))
Select Case wParam
Case MouseMessages.WM_LeftButtonUp
RaiseEvent MouseLeftButtonClick(Nothing, New MouseEventArgs(MouseButtons.Left, 1, MouseData.pt.x, MouseData.pt.y, 0))
Case MouseMessages.WM_RightButtonUp
RaiseEvent MouseRightButtonClick(Nothing, New MouseEventArgs(MouseButtons.Right, 1, MouseData.pt.x, MouseData.pt.y, 0))
End Select
Return CallNextHookEx(MouseHookID, nCode, wParam, lParam)
End Function
End Class
Upvotes: 0
Reputation: 1338
Thanks for the info, but I solved my question by using the code on this page:
http://www.daniweb.com/software-development/vbnet/threads/369609/detecting-a-mouse-click
Apparently using a Hook is the only way to detect ANY mouse click. It even detects clicks outside of my program.
Upvotes: 0
Reputation: 2469
You could create a MouseDown-Eventhandler for every Control in your form
For Each c As Control In Me.Controls
AddHandler c.MouseDown, AddressOf c_MouseDown
Next
Then check if the sender of the event is not your button
Private Sub c_MouseDown(sender As Object, e As MouseEventArgs)
If sender Is Button1 Then
MsgBox("Open costumized menu")
Else
MsgBox("Replace this with 'close something'")
End If
End Sub
Edit: Of course you have to create an eventhandler for the form too
AddHandler Me.MouseDown, AddressOf c_MouseDown
Best to put the handler in the constructor
Upvotes: 1
Reputation: 34846
Setup the MouseClick
event handler in the form constructor, like this:
Public Sub New()
Me.MouseClick += mouseClick
End Sub
Then you can write your event handler code, like this:
Private Sub mouseClick(sender As Object, e As MouseEventArgs)
' Do whatever logic you want here
' For example you can capture which mouse button was clicked, like this:
If e.Button = MouseButtons.Left Then
End If
End Sub
Upvotes: 0