Подтвердить что ты не робот

WinForms ComboBox SelectedIndexChanged не срабатывает при наборе нескольких символов, за которыми следуют Alt + Down

Короче

Когда я ввожу символ в ComboBox, нажмите Alt + Down, затем Enter или Tab, событие SelectedIndexChanged не срабатывает, хотя значение SelectedIndex меняется! Почему пожар не срабатывает?

Обновление Такая же ошибка возникает, если вы вводите символ, нажмите Alt + Down, а затем введите Esc. Вы ожидали бы, что Esc отменит изменение. Тем не менее, SelectedIndex действительно изменяется, и событие SelectedIndexChanged не срабатывает.

Что произойдет, если вы просто наберете Alt + Down, используйте клавиши со стрелками, чтобы перейти к записи, а затем введите Esc? Если выбранный индекс будет возвращен к исходному значению?


Не так коротко

У меня есть приложение WinForm с ComboBox. Событие ComboBox SelectedIndexChanged подключается к обработчику событий, который показывает элемент SelectedItem в элементе Label. Коллекция элементов ComboBox имеет три значения: "один", "два" и "три".

  • Когда я выбираю элемент с помощью мыши, событие срабатывает.
  • При прокрутке мыши событие срабатывает.
  • Когда я использую Alt + Down для расширения выпадающего списка и прохождения элементов с помощью Up и Down, событие срабатывает.
  • Но... Когда я набираю первый символ значения, затем нажмите Alt + Down, затем Enter или Tab, значение выбирается и отображается в поле со списком, но событие не срабатывает.

Я также добавил кнопку, которая показывает SelectedIndex. Он показывает, что SelectedIndex изменился. Поэтому, несмотря на изменение SelectedIndex, событие SelectedIndexChanged не срабатывает!

Если я просто набираю допустимое значение, например One, событие также не срабатывает, но в этом случае щелчок по кнопке показывает, что SelectedIndex действительно не изменился. Поэтому в этом случае поведение нормальное.


Воспроизведение, создание формы и добавление ComboBox, метки и кнопки. Поместите следующий код в 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);
        }
    }
}
4b9b3361

Ответ 1

Соответствующим значением свойства DropDown является DropDownList. У этой проблемы нет.

Придумывание обходного пути для вашей конкретной проблемы с помощью стиля DropDown, установленного в DropDown, довольно сложно. Это позволяет пользователю вводить произвольный текст и даже идеальное совпадение с одним из выпадающих элементов, не меняет SelectedIndex. Вам нужно будет выполнить событие Validating и искать совпадение самостоятельно. Событие DropDownClosed было бы полезно для вашего конкретного сценария. Но на самом деле, всегда используйте DropDownList, если вам нужны идеальные соответствия.

Ответ 2

Я пробовал несколько поисковых запросов Google, чтобы найти окончательный ответ на этот вопрос, но не нашел его раньше. Только сейчас я нашел поток, который фактически ссылается на статью базы знаний Microsoft о проблеме. В статье KB948869 описывается проблема.

В статье базы знаний предлагается создать свой собственный combobox и переопределить метод 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 будет запущено.

Итак, оба варианта на самом деле не очень хороший ответ.

Обновление Я даже попробовал переопределить свойство SelectedIndex в MyComboBox, получив вызов OnSelectedIndexChanged(EventArgs.Empty). После ввода символа и нажатия Alt + Down, сеттер выполняется, но он устанавливает значение -1, которое оно уже есть. После нажатия Tab, сеттер не будет выполнен повторно, хотя как-то значение SelectedIndex изменится. Похоже, ComboBox напрямую меняет поле поддержки для SelectedIndex, минуя настройку. Я считаю, что что-то вроде этого, вероятно, также происходит в реальном ComboBox.

Ответ 3

Исправьте меня, если я ошибаюсь. Вот код, который я использовал.

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

Ответ 4

У меня была проблема ESC в combobox в стиле DropDownList. Я немного изменил то, что сработало для меня, чтобы удовлетворить ваши потребности:

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 или ENTER для DropDown -стильного ComboBox или ESC для DropDownList -строчного ComboBox, SelectedIndexChanged -Event запускается, когда DropDown закрывается.
/" > Я никогда не использовал ComboBoxStyle.Simple и не знаю, как он работает или должен работать, но поскольку он, насколько мне известно, никогда не отображает DropDown, это должно быть безопасно даже для этого стиля.

Если вы не хотите получать из ComboBox для создания собственного элемента управления, вы также можете применить аналогичную логику к ComboBox в форме, подписавшись на него PreviewKeyDown и DropDownClosed событиями.

Ответ 5

В результате я получил свой класс из 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);
    }
}