在Windows 7中DropDownStyle = DropDownList时,ComboBox.SelectedValue与显示的文本不匹配

时间:2010-01-04 18:48:05

标签: .net winforms data-binding windows-7 combobox

假设我们在Windows应用程序中有以下代码:

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

这里没有魔法,只有ComboBox绑定到字符串列表。 TextBox显示SelectedValue的{​​{1}}。

当我在ComboBox和标签栏中输入“Bucket”时,我遇到了意外行为。出于某种原因,ComboBox显示“船”,但ComboBox显示“Bucket”。我希望他们都能显示“Bucket”。

如果我将TextBox更改为DropDownStyle,它会按预期运行,但我不希望用户能够键入他们想要的任何内容。他们应该只能输入列表中的项目。

更有趣的是,在输入“Bucket”并跳出标签后,如果我再次输入“Bucket”,它将同时显示“Bucket”。如果我进行第三次尝试,它将返回“船只”以获取{TextBox'的DropDown和“Bucket”。所以它似乎在所有B的循环中运行。

在我最近从XP升级到Windows 7之前,我没有注意到这一点。我不明白这与此有什么关系,但无论如何我都把它丢掉了。

如果这种行为是正确的,任何人都可以告诉我应该做些什么来实现我预期的行为吗?

更新似乎 与Windows 7相关。在Windows XP模式下,所有操作都符合预期。任何运行Windows 7的人都可以尝试我的代码并验证我不是疯了吗?

7 个答案:

答案 0 :(得分:6)

解决方法可能是将DropDownStyle更改为DropDown并添加以下内容:

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

这将允许用户输入任何内容,但除非他们输入有效的项目,否则不会让他们远离控件。

仍然对从XP变为Win 7的行为感到不满。

答案 1 :(得分:2)

hotfix将解决此问题。

答案 2 :(得分:1)

我自己遇到了这个并找到了一些解决方法。最后,我将自己的解决方案作为ComboBox子类推出:

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

答案 3 :(得分:1)

我创建了自己的解决方案,因为我没有想到将修补程序部署到所有用户的计算机是合理的还是可管理的。但是,修补程序问题描述描述了我的问题。请参阅下面的列表,例如:

橡子 苹果



干酪

如果我选择以B开头的项目,例如Bed,则会选择第一次以B开头,这将是Bad。这是因为我只输入前两个字符Be(没有测试输入整个字符串作为我的真实案例,这对用户来说是不合理的。)

我有一个ComboBox,其中包含以下设置:
AutoCompleteMode - SuggestAppend,
AutoCompleteSource - ListItems,
DropDownStyle - DropDownList。

为了解决这个问题,我做了以下工作,因为我注意到在SelectedIndexChanged事件期间选择了我想要的值,但没有选择Leave事件。

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

离开事件似乎解决了按Enter键的问题。 KeyUp(KeyDown没有工作)似乎解决了标签问题。

答案 4 :(得分:1)

我仍然可以在Windows 10上重现这一点,所以我想我会将我的解决方法添加到列表中。

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

答案 5 :(得分:0)

我知道这个回复很老了,但我要求回答这个Windows 7错误。我在Ecyrb的静脉中徘徊了一会儿,并提出了以下解决方法:

来自InitForm()for Application添加此属性:

Me.KeyPreview = True

ComboBox所在的位置:

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

现在,在我的示例中,我使用的是继承comboBox的自定义控件。因此,您需要将它们连接起来供自己使用。

答案 6 :(得分:0)

覆盖OnTextChanged方法,只是不要将消息传递给基础。

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