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

Posted

技术标签:

【中文标题】在 Windows 7 中 DropDownStyle = DropDownList 时,ComboBox.SelectedValue 与显示的文本不匹配【英文标题】:ComboBox.SelectedValue does not match displayed text when DropDownStyle = DropDownList in Windows 7 【发布时间】:2011-01-01 08:42:29 【问题描述】:

假设我们在 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);

这里没有魔法,只是一个绑定到字符串列表的ComboBoxTextBox 显示ComboBoxSelectedValue

当我在 ComboBox 中键入“Bucket”并离开时,我遇到了意外行为。出于某种原因,ComboBox 显示“Boat”,但TextBox 显示“Bucket”。我希望他们都显示“桶”。

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

更有趣的是,在键入“Bucket”并跳开后,如果我再次键入“Bucket”,它会在两者中显示“Bucket”。如果我进行第三次尝试,它会返回到 ComboBox 的“Boat”和“TextBox”的“Bucket”。所以它似乎在所有 B 中循环。

直到我最近从 XP 升级到 Windows 7 时才注意到这一点。我不明白这与这有什么关系,但我还是把它扔掉了。

如果这种行为是正确的,谁能告诉我应该做些什么来实现我的预期行为?

更新这似乎与 Windows 7 相关。在 Windows XP 模式下,一切都按预期运行。其他运行 Windows 7 的人可以尝试我的代码并验证我没有发疯吗?

【问题讨论】:

我完全采用了您的代码并将其扔到一个新的表单上,并且无法重现该行为。您可能会尝试连接 SelectedIndexChanged 事件并在其中输入一些控制台消息,然后在您键入组合时查看发生了什么。 我添加了comboBox.SelectedIndexChanged += new EventHandler(delegate Console.WriteLine("SelectedIndex = 0", comboBox.SelectedIndex); );,输出显示SelectedIndex 更改为“2”,然后更改为“3”,这与TextBox 显示的内容相匹配。 如果这是一个可重现的错误,您应该将其提交给 Microsoft Connect:connect.microsoft.com/VisualStudio 我希望其他运行 Win7 的人可以先确认一下,但我已经继续并打开了一个错误:connect.microsoft.com/VisualStudio/feedback/…。 我刚刚尝试过,我也可以复制(尽管我没有尝试查看它是否可以在 XP 上复制)。我已对您的 Connect 错误表示赞成并声明我可以复制它。这个问题现在可以结束了。 【参考方案1】:

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

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

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

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

【讨论】:

+1 这是适合我的 vb.net 版本:e.Cancel = Not (comboBox.Items.Contains(comboBox.Text))【参考方案2】:

我知道这个回复已经很老了,但我需要一个关于这个 Windows 7 错误的答案。我在 Ecyrb 的脉络中修补了一段时间,并想出了以下解决方法:

从 InitForm() 为 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

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

【讨论】:

【参考方案3】:

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

protected override void OnTextChanged(EventArgs e)

    //Eat the message so it doesn't select an incorrect value.

【讨论】:

【参考方案4】:

我自己遇到了这个问题,并找到了一些解决方法。最后,我将自己的解决方案作为 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;
    
  

【讨论】:

【参考方案5】:

这个hotfix 将解决这个问题。

【讨论】:

虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。 嗯,这是微软发布的修补程序下载页面的链接。这里没有什么要补充的,我不希望这个链接过期:)【参考方案6】:

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

橡子 苹果 不好 床 砖 奶酪

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

我有一个具有以下设置的 ComboBox:AutoCompleteMode - SuggestAppend、AutoCompleteSource - ListItems、DropDownStyle - 下拉列表。

为了解决这个问题,我注意到在 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;
    

Leave 事件似乎解决了按 Enter 键的问题。 KeyUp(KeyDown 不起作用)似乎解决了选项卡问题。

【讨论】:

【参考方案7】:

我仍然可以在 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

【讨论】:

以上是关于在 Windows 7 中 DropDownStyle = DropDownList 时,ComboBox.SelectedValue 与显示的文本不匹配的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Windows 7 中编译 C 编程? [关闭]

您如何在 Windows 中注册最近使用列表以准备 Windows 7?

markdown 在Windows 7中禁用密码到期

如何在 Safari(Windows 7)中禁用同源策略?

如何使我的程序在 Windows Vista 和 Windows 7 中运行?

在 Windows 8 上的 Creator 中编译将在 Windows 7 上运行的 Qt 5 应用程序?