Search code examples
c#winformscomboboxevent-handlingselectedindexchanged

Prevent bypassing SelectedIndexChanged in ComboBox


I was surprised, that SelectedIndexChanged on a ComboBox with ComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList can be bypassed by changing the shown value to another one.

Here are steps to reproduce the case:

  • Create a Form with a ComboBox with ComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownListand some other control, which can get the focus (e.g. TextBox)
  • Attach an event handler for ComboBox.SelectedIndexChanged and let say do reset the selected index of ComboBox there always to 0 for only first entry could be selected.
  • Fill ComboBox.Items with e.g. integers from 1 to 5.
  • Start the application and open drop down list
  • Click any entry except the first and hold left mouse button down(no LMBUp must be triggered)
  • Press TAB key holding the left mouse button down
  • Clicked value is shown in the ComboBox and no ComboBox.SelectedIndexChanged being triggered.

What would be your offer to prevent this undesirable behavior. Tab key must not be suppressed and ComboBox.SelectedIndexChanged must be triggered on change.

Some code for copy-paste:

public Form1()
{
    InitializeComponent();

    comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
    comboBox1.Items.Add(1);
    comboBox1.Items.Add(2);
    comboBox1.Items.Add(3);
    comboBox1.Items.Add(4);
    comboBox1.Items.Add(5);
    
    comboBox1.SelectedIndexChanged += ComboBox1_SelectedIndexChanged;
}
private void ComboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    comboBox1.SelectedIndex = 0;
}

Solution

  • I have solved it with derived control:

    class ModifiedComboBox : ComboBox
    {
        private object _lastSelectedItem = null;
    
        protected override void OnDropDownClosed(EventArgs e)
        {
            if(SelectedItem != _lastSelectedItem)
            {
                OnSelectedIndexChanged(new EventArgs());
            }
            base.OnDropDownClosed(e);
        }
    
        protected override void OnSelectedIndexChanged(EventArgs e)
        {
            _lastSelectedItem = SelectedItem;
            base.OnSelectedIndexChanged(e);
        }
    }