How to detect mouse click outside winform control in C#

Viewed 5212

I have a custom control (custom ComboBox). It`s work well, when I press "arrow button" it deployed, if I press it again it deployed too, but if it are deployed and I press anywhere in my form - it close but then, when I'm trying to open it - I must press "arrow button" two times. So I need to detect this moment, when I click outside of my combobox.

Code to open ComboBox(call in ButtonClick)

private void OpenComboBox()       
{
    if (drop_flag)
    {
        ...

        popup.Show(this);
    }
    else
    {
        drop_flag = true;
    }
}

And Close event

private void popup_Closed(object sender, ToolStripDropDownClosedEventArgs e)
{
    drop_flag = false;
}

So, I want something like this

private ClickedOutsideControl()
{
    dropflag = true;
}
2 Answers

In the form, you can pre-process messages sent to controls by using a message filter. Here is my FAILED attempt to implement the desired functionality:

public partial class frmAutoCloseDropDown : Form, IMessageFilter
{
    int _lastMsg;

    public frmAutoCloseDropDown()
    {
        InitializeComponent();
        Application.AddMessageFilter(this);
    }

    // THIS ATTEMPT DOES NOT WORK!
    public bool PreFilterMessage(ref Message m)
    {
        const int WM_LBUTTONDOWN = 0x0201;

        if (m.Msg!= _lastMsg) {
            _lastMsg = m.Msg;
        }
        if (m.Msg == WM_LBUTTONDOWN) {
            // You would have to do this recursively if the combo-boxes were nested inside other controls.
            foreach (ComboBox cbo in Controls.OfType<ComboBox>()) {
                cbo.DroppedDown = false;
            }
        }
        return false;
    }

    // Note: Dispose is created inside *.Designer.cs and you have to move it manually to *.cs
    protected override void Dispose(bool disposing)
    {
        if (disposing) {
            Application.RemoveMessageFilter(this);
            if (components != null) {
                components.Dispose();
            }
        }
        base.Dispose(disposing);
    }
}

Why does it not work? Probably because the new messages generated by cbo.DroppedDown = false that are sent to the form to close the drop-down are appended to the message queue and are only processed after the left mouse button click has been processed. This means that even when with PreFilterMessage our attempt to close the drop-down comes too late.

A possible solution would be to re-send WM_LBUTTONDOWN to the right ComboBox. You would have to interpret the parameters of the message to get the mouse coordinates and look to which combobox they are pointing to get its HWnd. My attempt to do this also shows that the behavior also depends on the drop-down style. It also has the undesired effect to close the drown-down you just opened. And what happens if you click in the drop-down itself to select an entry?

Probably you could do it, but the code tends to become very complicated. It's not worth the effort.

Related