Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ComboBox.SelectedValue does not match displayed text when DropDownStyle = DropDownList in Windows 7

Let's say we have the following code in a Windows application:

ComboBox comboBox = new ComboBox()
{
    AutoCompleteMode = AutoCompleteMode.SuggestAppend,
    AutoCompleteSource = AutoCompleteSource.ListItems,
    DataSource = new string[] { "", "Ark", "Boat", "Bucket" },
    DropDownStyle = ComboBoxStyle.DropDownList
};
this.Controls.Add(comboBox);

TextBox textBox = new TextBox()
{
    Left = comboBox.Right,
    Top = comboBox.Top,
    ReadOnly = true
};
textBox.DataBindings.Add("Text", comboBox, "SelectedValue");
this.Controls.Add(textBox);

No magic here, just a ComboBox bound to a list of strings. The TextBox displays the SelectedValue of the ComboBox.

I'm getting unexpected behavior when I type "Bucket" into the ComboBox and tab away. For some reason the ComboBox displays "Boat" but the TextBox displays "Bucket". I would expect them both to display "Bucket".

It behaves as expected if I change the DropDownStyle to DropDown, but I don't want users to be able to type anything they want. They should only be able to type items that are in the list.

Even more interesting is that, after typing "Bucket" and tabbing away, if I type "Bucket" again it will display "Bucket" in both. If I make a third attempt, it goes back to "Boat" for the ComboBox and "Bucket" for the `TextBox'. So it seems like it's cycling through all the B's.

I didn't notice this until I upgraded from XP to Windows 7 recently. I don't see how that could have anything to do with this, but I'm throwing it out anyway.

If this behavior is correct, can anyone tell me what I should be doing to achieve my expected behavior?

UPDATE It would seem this is related to Windows 7. Everything behaves as expected in Windows XP Mode. Can anyone else running Windows 7 try my code and verify that I'm not crazy?

like image 537
Ecyrb Avatar asked Jan 04 '10 18:01

Ecyrb


4 Answers

A workaround could be changing the DropDownStyle to DropDown and adding the following:

comboBox.Validating += new CancelEventHandler((o, e) =>
    {
        e.Cancel = (comboBox.DataSource as string[]).Contains(comboBox.Text) == false;
    });

That will allow users to type anything, but it won't let them tab away from the control unless they typed a valid item.

Still, not happy with the behavior changing from XP to Win 7.

like image 159
Ecyrb Avatar answered Nov 15 '22 10:11

Ecyrb


This hotfix will solve this issue.

like image 22
bittusarkar Avatar answered Nov 15 '22 09:11

bittusarkar


I ran into this myself and found a few workarounds. In the end I rolled my own solution as a ComboBox subclass:

//as noted here: https://connect.microsoft.com/VisualStudio/feedback/details/523272/combobox-does-not-display-selectedvalue-to-user-in-windows-7
//Windows 7 has issues with ComboBoxStyle.DropDownList mixed with AutoCompleteMode.Append or AutoCompleteMode.SuggestAppend
//this class seeks to address those problems
public class BetterComboBox : ComboBox
{
  private int _windows7CorrectedSelectedIndex = -1;

  private int? _selectedIndexWhenDroppedDown = null;
  protected override void OnDropDown(EventArgs e)
  {
    _selectedIndexWhenDroppedDown = SelectedIndex;
    base.OnDropDown(e);
  }
  private bool _onDropDownClosedProcessing = false;
  protected override void OnDropDownClosed(EventArgs e)
  {
    if (_selectedIndexWhenDroppedDown != null && _selectedIndexWhenDroppedDown != SelectedIndex)
    {
      try
      {
        _onDropDownClosedProcessing = true;
        OnSelectionChangeCommitted(e);
      }
      finally
      {
        _onDropDownClosedProcessing = false;
      }
    }
    base.OnDropDownClosed(e);
    if (SelectedIndex != _windows7CorrectedSelectedIndex)
    {
      SelectedIndex = _windows7CorrectedSelectedIndex;
      OnSelectionChangeCommitted(e);
    }
  }
  protected override void OnSelectionChangeCommitted(EventArgs e)
  {
    if (!_onDropDownClosedProcessing) _windows7CorrectedSelectedIndex = SelectedIndex;
    _selectedIndexWhenDroppedDown = null;
    base.OnSelectionChangeCommitted(e);
  }
  protected override void OnSelectedIndexChanged(EventArgs e)
  {
    bool alreadyMatched = true;
    if (_windows7CorrectedSelectedIndex != SelectedIndex)
    {
      _windows7CorrectedSelectedIndex = SelectedIndex;
      alreadyMatched = false;
    }
    base.OnSelectedIndexChanged(e);

    //when not dropped down, the SelectionChangeCommitted event does not fire upon non-arrow keystrokes due (I suppose) to AutoComplete behavior
    //this is not acceptable for my needs, and so I have come up with the best way to determine when to raise the event, without causing duplication of the event (alreadyMatched)
    //and without causing the event to fire when programmatic changes cause SelectedIndexChanged to be raised (_processingKeyEventArgs implies user-caused)
    if (!DroppedDown && !alreadyMatched && _processingKeyEventArgs) OnSelectionChangeCommitted(e);
  }
  private bool _processingKeyEventArgs = false;
  protected override bool ProcessKeyEventArgs(ref Message m)
  {
    try
    {
      _processingKeyEventArgs = true;
      return base.ProcessKeyEventArgs(ref m);
    }
    finally
    {
      _processingKeyEventArgs = false;
    }
  }
}
like image 37
Pennidren Avatar answered Nov 15 '22 09:11

Pennidren


I created my own solution because I didn't think deploying a hotfix to all users computers was reasonable or manageable. However the hotfix problem description describes my problem. See list below for example:

Acorn Apple
Bad
Bed
Brick
Cheese

If I am selecting an item starting with B such as Bed, it would select the first time starting with B which would be Bad. This is if I only type the first two characters Be (did not test typing entire string as my real world case this would be unreasonable for a user to do so).

I have a ComboBox with the following settings:
AutoCompleteMode - SuggestAppend,
AutoCompleteSource - ListItems,
DropDownStyle - DropDownList.

To solve the problem I did the following as I noticed that my desired value was selected during the SelectedIndexChanged event but not the Leave event.

    int myDesiredIndexSelection;

    private void myComboBox_SelectedIndexChanged(object sender, EventArgs e)
    {
        myDesiredIndexSelection = myComboBox.SelectedIndex;
    }

    private void myComboBox_Leave(object sender, EventArgs e)
    {
        myComboBox.SelectedIndex = myDesiredIndexSelection;
    }
    private void myForm_KeyUp(object sender, KeyEventArgs e) 
    { 
        if (e.KeyCode == Keys.Tab)
            myComboBox.SelectedIndex = myDesiredIndexSelection;
    }

The Leave event seems to address hitting the enter key. The KeyUp (KeyDown didn't work) seemed to fix the tab issue.

like image 23
Matt Avatar answered Nov 15 '22 10:11

Matt