繁体   English   中英

WinForms ComboBox SelectedIndexChanged在键入少量字符后跟Alt + Down时不触发

[英]WinForms ComboBox SelectedIndexChanged not firing when typing few chars followed by Alt+Down

简而言之

当我在ComboBox中键入一个字符时,按Alt +向下键,然后按Enter键或Tab键,即使SelectedIndex值确实发生更改,也不会触发SelectedIndexChanged事件! 为什么事件没有发生?

更新如果键入字符,则按Alt +向下键,然后键入Esc,则会出现相同的错误。 您可能希望Esc取消更改。 但是,SelectedIndex 更改,并且不会触发SelectedIndexChanged事件。

如果只键入Alt + Down,使用箭头键浏览条目, 然后键入Esc,会发生什么? 是否应将所选索引设置回其原始值?


不是那么短暂

我有一个带有ComboBox的WinForm应用程序。 ComboBox的SelectedIndexChanged事件连接到一个事件处理程序,该事件处理程序显示Label控件中的SelectedItem。 ComboBox的Items集合有三个值:“One”,“Two”和“Three”。

  • 当我用鼠标选择项目时,事件将触发。
  • 滚动鼠标时,事件会触发。
  • 当我使用Alt + Down扩展组合框并使用向上和向下遍历项目时,事件将触发。
  • 但是......当我输入值的第一个字符时, 然后按Alt + Down,然后按Enter或Tab,值将被选中并显示在组合框中,但事件不会触发。

我还添加了一个显示SelectedIndex的按钮。 它显示SelectedIndex 更改。 因此,即使SelectedIndex确实发生了变化,SelectedIndexChanged事件也不会触发!

如果我只输入一个有效值,如 One 则事件也不会触发,但在这种情况下单击按钮会显示SelectedIndex确实没有更改。 所以在这种情况下行为是正常的。


要重现,请创建一个表单并添加一个ComboBox,一个Label和一个Button。 将以下代码放在Form1.cs中:

using System;
using System.Windows.Forms;

namespace ComboBoxSelectedIndexChanged
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            comboBox1.Items.AddRange(new object[] {
                "One",
                "Two",
                "Three"
            });
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            label1.Text = "Selected index: " + comboBox1.SelectedIndex;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Selected item: " + comboBox1.SelectedItem +
                "\nSelected index: " + comboBox1.SelectedIndex);
        }
    }
}

我已经尝试了几次谷歌搜索,以便找到一个明确的答案,但之前没有找到。 刚才我找到了一个线程,实际上是指有关该问题的Microsoft知识库文章。 文章KB948869描述了该问题。

知识库文章建议创建自己的组合框并覆盖ProcessDialogKey方法。

using System.Windows.Forms;

public class MyComboBox : ComboBox
{
    protected override bool ProcessDialogKey(Keys keyData)
    {
        if (keyData == Keys.Tab)
            this.DroppedDown = false;
        return base.ProcessDialogKey(keyData);
    }
}

我试过了,但不幸的是,它似乎没有任何影响。 这有点奇怪。 我希望知识库文章中描述的解决方法是准确的。

我找到了另一种解决方法,即使用DropDownClosed事件代替。

private void comboBox1_DropDownClosed(object sender, EventArgs e)
{
    label1.Text = "DroDownClosed Selected index: " + comboBox1.SelectedIndex;
}

似乎工作,但使用DropDownStyle.DropDown时只。 当您将DropDownStyle设置为DropDownList时,键入一个字符不会触发DropDownClosed(因为在这种情况下没有实际的下拉)。 只有当您实际打开下拉列表并选择一个值时才会触发DropDownClosed事件。

所以,这两个选项都不是一个好的答案。

更新我甚至尝试覆盖MyComboBox中的属性SelectedIndex,让它调用OnSelectedIndexChanged(EventArgs.Empty) 键入字符并按Alt + Down后,执行setter,但它将值设置为-1,它已经是。 按下Tab后,不会再次执行setter,尽管SelectedIndex值以某种方式更改。 看起来ComboBox正在直接更改SelectedIndex的支持字段,绕过设置。 我相信这样的事情也可能发生在真正的ComboBox中。

这里适当的DropDown属性值是DropDownList。 它没有这个问题。

针对DropDown样式设置为DropDown的特定问题的解决方法非常困难。 它允许用户键入任意文本,甚至与其中一个下拉项完美匹配也不会更改SelectedIndex。 您必须实现Validating事件并自己查找匹配项。 DropDownClosed事件对您的特定场景有用。 但实际上,如果你想完美匹配,总是使用DropDownList。

我在DropDownList风格的组合框中遇到了ESC问题。 我略微修改了适合我的工作:

public class MyComboBox : System.Windows.Forms.ComboBox
{
  private bool _sendSic;

  protected override void OnPreviewKeyDown(System.Windows.Forms.PreviewKeyDownEventArgs e)
  {
    base.OnPreviewKeyDown(e);

    if (DroppedDown)
    {
      switch(e.KeyCode)
      {
        case System.Windows.Forms.Keys.Escape:
          _sendSic = true;
          break;
        case System.Windows.Forms.Keys.Tab:
        case System.Windows.Forms.Keys.Enter:
          if(DropDownStyle == System.Windows.Forms.ComboBoxStyle.DropDown)
            _sendSic = true;
          break;
      }
    }
  }

  protected override void OnDropDownClosed(System.EventArgs e)
  {
    base.OnDropDownClosed(e);

    if(_sendSic)
    {
      _sendSic = false;
      OnSelectedIndexChanged(System.EventArgs.Empty);
    }
  }
}

这样做是听下拉打开时进来的击键。 如果是ESC,TAB输入一个DropDown式的组合框或ESCDropDownList风格的组合框,一个SelectedIndexChanged当下拉关闭-活动被触发。
我从来没有使用过ComboBoxStyle.Simple ,也不知道它是如何工作的,但是因为据我所知,它永远不会显示DropDown,即使对于那种风格也应该是安全的。

如果您不想从ComboBox派生来构建自己的控件,您还可以通过订阅它的PreviewKeyDownDropDownClosed事件将类似的逻辑应用于表单上的ComboBox。

如我错了请纠正我。 这是我用过的代码。

comboBox1.Items.AddRange(new object[] {
                "One",
                "Two",
                "Three"
});

comboBox1.SelectedIndexChanged+=(sa,ea)=>
 {
   label1.Text = "Selected index: " + comboBox1.SelectedIndex;
 };
comboBox1.TextChanged+= (sa, ea) =>
 {
 comboBox1.SelectedIndex = comboBox1.FindStringExact(comboBox1.Text);

 //OR
 //comboBox1.SelectedIndex = comboBox1.Items.IndexOf(comboBox1.Text);
  comboBox1.SelectionStart  = comboBox1.Text.Length;
};

我最终从ComboBox派生出我自己的类:

public class EditableComboBox : ComboBox
{
    protected int backupIndex;
    protected string backupText;

    protected override void OnDropDown(EventArgs e)
    {
        backupIndex = this.SelectedIndex;
        if (backupIndex == -1) backupText = this.Text;
        else backupText = null;
        base.OnDropDown(e);
    }

    protected override void OnSelectionChangeCommitted(EventArgs e)
    {
        backupIndex = -2;
        base.OnSelectionChangeCommitted(e);
    }

    protected override void OnSelectionIndexChanged(EventArgs e)
    {
        backupIndex = -2;
        base.OnSelectionIndexChanged(e);
    }

    protected override void OnDropDownClosed(EventArgs e)
    {
        if (backupIndex > -2 && this.SelectedIndex != backupIndex)
        {
            if (backupIndex > -1)
            {
                this.SelectedIndex = backupIndex;
            }
            else
            {
                string oldText = backupText;
                this.SelectedIndex = -1;
                this.Text = oldText;
                this.SelectAll();
            }
        }
        base.OnDropDownClosed(e);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM