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

空扰寡人 提交于 2019-12-09 08:53:10

问题


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?


回答1:


This hotfix will solve this issue.




回答2:


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.




回答3:


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;
    }
  }
}



回答4:


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.




回答5:


I can still reproduce this on Windows 10, so I thought I would add my workaround to the list.

Private Sub LeaveComboBox(s As Object, e As EventArgs) Handles ComboBox1.Leave
    Dim sender as ComboBox = DirectCast(s, ComboBox)

    If sender.DroppedDown Then
        ' Save the correct item
        Dim correctSelection as Object = sender.SelectedItem

        ' The core of the issue seems to be that while searching, Leave is
        ' triggered before DropDownClosed when you hit the TAB.
        ' Instead, trigger that now:
        sender.DroppedDown = False

        ' Restore the correct item.
        sender.SelectedItem = correctSelection
    End If
End Sub



回答6:


I know that this response is quite old, but I was requiring an answer to this Windows 7 bug. I tinkered around for a while in the vein of Ecyrb and came up with the following work-around:

From InitForm() for Application Add this property:

Me.KeyPreview = True

From where the ComboBox is located:

Private mbTab As Boolean 
Private miPrevIndex As Integer = -1
Private Sub DropDownListEx_Validating(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles Me.Validating
   miPrevIndex = Me.SelectedIndex
   MyBase.OnSelectedIndexChanged(e)
End Sub
Private Sub DropDownListEx_PreviewKeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.PreviewKeyDownEventArgs) Handles Me.PreviewKeyDown
   mbTab = e.KeyCode = Windows.Forms.Keys.Tab
End Sub
Protected Overrides Sub OnDropDownClosed(ByVal e As System.EventArgs)
    MyBase.OnDropDownClosed(e)
    If Me.SelectedIndex <> miPrevIndex Then
        If mbTab = True Then
            Me.SelectedIndex = miPrevIndex
        Else
            miPrevIndex = Me.SelectedIndex
        End If
        MyBase.OnSelectedIndexChanged(e)
    End If
End Sub

Now, in my example, I am using a custom control that inherits comboBox. So you will need to wire these up for your own use.




回答7:


Override the OnTextChanged method and just don't pass the message on to the base.

protected override void OnTextChanged(EventArgs e)
{
    //Eat the message so it doesn't select an incorrect value.
}


来源:https://stackoverflow.com/questions/2001361/combobox-selectedvalue-does-not-match-displayed-text-when-dropdownstyle-dropdo

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!