Search code examples
c#.netwinformscomboboxdropdown

How to auto open a ComboBox when focused?


I have a Form which contains several ComboBoxes.

I want one ComboBox of them to open the elements list when it gets the focus, both from keyboard and mouse.

The DroppedDown property of the ComboBox class manages the visibility of the elements list.

The event that most fits my needs is Enter, so the code I wrote is:

private void comboBox1_Enter(object sender, EventArgs e)
{
    this.comboBox1.DroppedDown = true;
}

It works, but when directly clicking on the icon located on the right part of the ComboBox which does NOT have the focus, the elements list opens up and the suddenly disappears after its opening.

I've tried many ways to fix this weird behavior, checking the Focused property or using other events like DropDown or MouseClick, without getting any acceptable result.

animation of the problem


Solution

  • A simple way (which doesn't force you to override a ComboBox derived Control's WndProc) is to simulate a HitTest, testing whether the MouseDown occurred on the ComboBox button area; then, set DroppedDown = true; only if it didn't.

    Thus, when the Mouse is clicked on the Button, you won't cause a double effect, moving the Focus in an unexpected way (for the Control).

    GetComboBoxInfo() is used to retrieve the correct bounds of the ComboBox Button, whether the current layout is (LTR or RTL).

    private void comboBox1_Enter(object sender, EventArgs e)
    {
        var combo = sender as ComboBox;
        if (!combo.DroppedDown) {
            var buttonRect = GetComboBoxButtonInternal(combo.Handle);
            if (!buttonRect.Contains(combo.PointToClient(Cursor.Position))) {
                combo.DroppedDown = true;
                Cursor = Cursors.Default;
            }
        }
    }
    

    Declarations for the GetComboBoxInfo() function:

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    internal static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);
    
    [StructLayout(LayoutKind.Sequential)]
    internal struct COMBOBOXINFO {
        public int cbSize;
        public Rectangle rcItem;
        public Rectangle rcButton;
        public int buttonState;
        public IntPtr hwndCombo;
        public IntPtr hwndEdit;
        public IntPtr hwndList;
    }
    
    internal static Rectangle GetComboBoxButtonInternal(IntPtr cboHandle) {
        var cbInfo = new COMBOBOXINFO();
        cbInfo.cbSize = Marshal.SizeOf<COMBOBOXINFO>();
        GetComboBoxInfo(cboHandle, ref cbInfo);
        return cbInfo.rcButton;
    }