DigitalDan3
asked on
Auto collapsing the combox's dropdownlist on mouse leave.
I have added a combobox to a form on the mouse leave event I display messagebox stating "Mouse Left". However, if I expand the list even if empty and move the mouse off the combox the MouseLeave event does not fire until i collapse the dropdown list.
What I would like to do is collapse the combobox dropdown list when the Combobox or the dropdown portion no longer has the mouse over it.
What I would like to do is collapse the combobox dropdown list when the Combobox or the dropdown portion no longer has the mouse over it.
ASKER
Here is what I am trying to avoid. I have a combobox that shows the current forms I have open in a panel control with the active window chosen. If the user drops down the list but decides not to change the selection and then clicks on a toostrip button the dropdown collapses but the button's click event doesnt fire until you press it again.
That's sounds to me like yet another manifestation of the same problem - this time, at the toolstrip end. If that makes sense to you, then I think it would probably be better/easier to tackle it at that end. Would that fill the bill? I can't say, off the top of my head, how to tackle it at that end. But if it would meet your needs, I'll give it some thought (or someone else may already have some code: I do seem to remember a post on here that touched on that issue).
By the way, although I picked up your last post pretty quickly, I may not be about for a few hours now. So if you do post, and there's then a relatively long silence, it's not because I'm ignoring you.
Roger
By the way, although I picked up your last post pretty quickly, I may not be about for a few hours now. So if you do post, and there's then a relatively long silence, it's not because I'm ignoring you.
Roger
ASKER
I just played with regular buttons and it has the same issue of having to double click.
Yes, now I've tried it, I agree we get the same effect. Here's some code that will overcome it. I suggest you try it first in its demo form. One form, two buttons near the top and one combo - called cbo for demo purposes - lower down. This code
Public Class Form1
Private Sub cbo_DropDownClosed(ByVal sender As Object, ByVal e As System.EventArgs) Handles cbo.DropDownClosed
Dim pt As System.Drawing.Point
pt = New Point(Windows.Forms.Cursor .Position. X, Windows.Forms.Cursor.Posit ion.Y)
Dim ctl As Control = FindControlAtLocation(pt)
If Not ctl Is Nothing Then
ctl.Focus()
If ctl.Equals(Button1) Then
Button1PseudoClick()
End If
If ctl.Equals(Button2) Then
Button2PseudoClick()
End If
End If
End Sub
Private Function FindControlAtLocation(ByVa l pt As Point) As Control
Dim result As Control = Nothing
Dim testpt As New Point(Me.PointToClient(pt) )
For Each ctl As Control In Me.Controls
If testpt.Y > ctl.Top Then
If testpt.Y < (ctl.Top + ctl.Height) Then
If testpt.X > ctl.Left Then
If testpt.X < (ctl.Left + ctl.Width) Then
Return ctl
End If
End If
End If
End If
Next
Return result
End Function
Private Sub Button1PseudoClick()
Debug.WriteLine("Button 1 Click")
End Sub
Private Sub Button2PseudoClick()
Debug.WriteLine("Button 2 Click")
End Sub
Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
Button1PseudoClick()
End Sub
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'put some items in combo
cbo.Items.AddRange(New Object() {"a", "b", "c", "d"})
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Button2PseudoClick()
End Sub
End Class
It works OK within the scope of the demo but (a) you'll need to consider whether it (or code based on the same approach) would work in your real-life scenario and (b) it may be possible to streamline it somewhat.
The idea is that we hook into the combo's DropDownClosed event because that is one which - unlike mouse and focus events - does seem to be visible to the application at large and, as you say, the dropdown DOES close. Then we find out where the mouse cursor is, and pass that info to a sub which cycles through the controls to find out if that location is on one of them. Focus and other properties/events are no good for this purpose because they're still stuck, at that point, in the combo.
If it is in one of the controls we fire the appropriate Click code for that. To make that a bit easier, I've moved the code into PseudoClick subs, which can be called directly either from this "special" code or from the "normal" Click code. There are other ways of doing that, but I reckon this is cleaner and clearer.
Finally, you'll see I've used Debug.WriteLine, rather than a MessageBox, to demonstrate that the code is being hit. That is deliberate. In fact, I think MessageBox would be OK as the code is now arranged. But the problem with using that (as your original post did) in this sort of "confused focus" situation is that, as a MessageBox itself takes focus, it can just confuse the focus even further.
Roger
Public Class Form1
Private Sub cbo_DropDownClosed(ByVal sender As Object, ByVal e As System.EventArgs) Handles cbo.DropDownClosed
Dim pt As System.Drawing.Point
pt = New Point(Windows.Forms.Cursor
Dim ctl As Control = FindControlAtLocation(pt)
If Not ctl Is Nothing Then
ctl.Focus()
If ctl.Equals(Button1) Then
Button1PseudoClick()
End If
If ctl.Equals(Button2) Then
Button2PseudoClick()
End If
End If
End Sub
Private Function FindControlAtLocation(ByVa
Dim result As Control = Nothing
Dim testpt As New Point(Me.PointToClient(pt)
For Each ctl As Control In Me.Controls
If testpt.Y > ctl.Top Then
If testpt.Y < (ctl.Top + ctl.Height) Then
If testpt.X > ctl.Left Then
If testpt.X < (ctl.Left + ctl.Width) Then
Return ctl
End If
End If
End If
End If
Next
Return result
End Function
Private Sub Button1PseudoClick()
Debug.WriteLine("Button 1 Click")
End Sub
Private Sub Button2PseudoClick()
Debug.WriteLine("Button 2 Click")
End Sub
Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
Button1PseudoClick()
End Sub
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'put some items in combo
cbo.Items.AddRange(New Object() {"a", "b", "c", "d"})
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Button2PseudoClick()
End Sub
End Class
It works OK within the scope of the demo but (a) you'll need to consider whether it (or code based on the same approach) would work in your real-life scenario and (b) it may be possible to streamline it somewhat.
The idea is that we hook into the combo's DropDownClosed event because that is one which - unlike mouse and focus events - does seem to be visible to the application at large and, as you say, the dropdown DOES close. Then we find out where the mouse cursor is, and pass that info to a sub which cycles through the controls to find out if that location is on one of them. Focus and other properties/events are no good for this purpose because they're still stuck, at that point, in the combo.
If it is in one of the controls we fire the appropriate Click code for that. To make that a bit easier, I've moved the code into PseudoClick subs, which can be called directly either from this "special" code or from the "normal" Click code. There are other ways of doing that, but I reckon this is cleaner and clearer.
Finally, you'll see I've used Debug.WriteLine, rather than a MessageBox, to demonstrate that the code is being hit. That is deliberate. In fact, I think MessageBox would be OK as the code is now arranged. But the problem with using that (as your original post did) in this sort of "confused focus" situation is that, as a MessageBox itself takes focus, it can just confuse the focus even further.
Roger
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
A neat solution I came across is:
Declare at top of class:
Private Declare Sub mouse_event Lib "user32" (ByVal dwFlags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long)
Private Const MOUSEEVENTF_LEFTDOWN = &H2 ' left button down
Private Const MOUSEEVENTF_LEFTUP = &H4 ' left button up
Then in the cmbBox_DropDownClosed use:
Private Sub cmbBox_DropDownClosed(ByVa l sender As Object, ByVal e As System.EventArgs) Handles cmbBox.DropDownClosed
Dim pt As System.Drawing.Point = New Point(Windows.Forms.Cursor .Position. X, Windows.Forms.Cursor.Posit ion.Y)
If Not cmbBox.Bounds.Contains(pt. X, pt.Y) Then
System.Windows.Forms.Curso r.Position = New Point(pt.X, pt.Y)
mouse_event(MOUSEEVENTF_LE FTDOWN Or MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
End If
End Sub
Declare at top of class:
Private Declare Sub mouse_event Lib "user32" (ByVal dwFlags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long)
Private Const MOUSEEVENTF_LEFTDOWN = &H2 ' left button down
Private Const MOUSEEVENTF_LEFTUP = &H4 ' left button up
Then in the cmbBox_DropDownClosed use:
Private Sub cmbBox_DropDownClosed(ByVa
Dim pt As System.Drawing.Point = New Point(Windows.Forms.Cursor
If Not cmbBox.Bounds.Contains(pt.
System.Windows.Forms.Curso
mouse_event(MOUSEEVENTF_LE
End If
End Sub
or to put it more simply:
Private Sub cmbBox_DropDownClosed(ByVa l sender As Object, ByVal e As System.EventArgs) Handles cmbBox.DropDownClosed
If Not cmbBox.Bounds.Contains(Win dows.Forms .Cursor.Po sition.X, Windows.Forms.Cursor.Posit ion.Y) Then
mouse_event(MOUSEEVENTF_LE FTDOWN Or MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
End If
End Sub
Private Sub cmbBox_DropDownClosed(ByVa
If Not cmbBox.Bounds.Contains(Win
mouse_event(MOUSEEVENTF_LE
End If
End Sub
I have deliberately put quotes round the word "problem" above. This is because I am far from personally sure that - given how a combobox normally works - it is a "problem" in this case. When its dropdown portion is showing it means that a selection is IN THE PROCESS of being made. If/when a selection IS made, then the dropdown portion will close anyway. If the user wants to simply to "accept" any selection that is currently highlighted and s/he therefore moves to another control (e.g by clicking on it or tabbing to it) again the dropdown portion will close anyway. I am not sure that, as a user, I would be particularly happy if - if I happened to move the mouse off the dropdown while deciding what selection to make - the dropdown were to disappear.
I am not saying that what you want to do definitely cannot be done: I haven't given that a great deal of thought. I think it would be messy to code, but it may well be possible. But my question at the moment is: are you sure it is something you really want to do? And, if so, why?
Roger