I'm making a custom ComboBox, and I'm struggling to make it's dropdown (a Panel) close when the user clicks outside. As everyone probably knows, using LostFocus
event is not enough because when the user clicks on eg.: a scroolbar or the form itself, the control doesn't lose focus. I tried using IMessageFilter
, but I don't think I understood how it works. I also tried using the Capture
property, forcing the dropdown to capture the mouse, but that also forces the mouse click to happen on the dropdown (Panel) itself, meaning that if the user had clicked on an item on the dropdown, the click won't work.
Let me clarify:
I'm making a custom control, a UserControl, which is compiled to a .DLL, and can be dragged'n dropped from the Toolbox onto forms, just like any other Winforms control. The control is a Combobox. I want to make the Combobox's dropdown (which is a panel, created at runtime) close when the user clicks outside of it, just like a normal ComboBox. And what is the problem? Detecting clicks outside of the panel and then closing it.
-> Using LostFocus
event: Not enough. If the user clicks on a blank space on the form, LostFocus
is not fired. I know I can place a LostFocus
event on my form and set the ActiveContrl
to Nothing
, but that would require that users (devs) of my custom ComboBox place the same code on every form they use the control.
-> Using Capture
property: Has given the best results so far. There are still some problems though. When Capture
is set to True
, the mouse is indeed captured by the control which has that property set to True. Therefore, no mouse activity is sensed by any other control on the app, but only the control which has the mouse captured. It'll only release the mouse when the user performs a click. This leads to some problems:
- If the user clicks on an item (a
Button
) on the dropdown, and the dropdown (aPanel
) has itsCapture
property set to true, the click will be "ignored", as it'll be handled by the dropdown, and not by theButton
on which the user actually wanted to click. - If the user clicks on the dropdown's scroolbar, the click will also be "ignored", just like explained above.
- When user moves the mouse above the
Button
s inside the dropdown (the ComboBox's items), they are not highlighted, because noMouseEnter
is fired for them, as the mouse is Captured by the dropdown.
There is a way to solve the first issue: You can find on which button is the mouse pointing using Control.MousePosition
, and then force a click on it.
I haven't been able to find a way to solve the 2nd and 3rd issue though. One possible solution I thought of was setting the dropdown's Capture
property to False when the mouse enters on it. So, this is how it would work:
- User clicks on the Combobox. Dropdown opens, captures the mouse;
- User then moves the mouse inside Combobox's dropdown. Capture is set to false, thus making it possible for the user to click on any button inside the dropdown or on the dropdown's scroolbar, and so forth. Buttons inside the dropdown are also properly highlighted as user moves the mouse above them;
- User moves the mouse outside Combobox's dropdown, Capture is again set to true, and now any click the user perform outside of it will be "ignored", and the dropdown will be closed. Just like a normal Combobox.
But when I tried to do this, another issue arrived: when a control has its Capture
property set to True, it'll constantly fire MouseEnter
events. MouseEnter
event doesn't use the real mouse pointer location. Even if the mouse pointer is actually outside a Control, that Control will think the mouse is actually inside of it if its Capture
property is set to True.