【问题标题】:ComboBox SelectionChangeCommitted event doesn't work with AutoCompleteComboBox SelectionChangeCommitted 事件不适用于 AutoComplete
【发布时间】:2013-01-23 20:21:17
【问题描述】:

这是一个重现我刚刚遇到的问题的简短程序。这是在带有 .NET 4.0 的 MS Windows 7 下编译的,以防万一。

using System;
using System.Drawing;
using System.Windows.Forms;

// Compile with "csc /target:exe /out:comboboxbug.exe /r:System.dll /r:System.Drawing.dll /r:System.Windows.Forms.dll comboboxbug.cs"
// in a Visual Studio command prompt.

static class Program
{
    [STAThread]
    static void Main()
    {
        //Create a label.
        Label oLabel = new Label();
        oLabel.Location = new Point (10, 10);
        oLabel.Size = new Size (100, 15);
        oLabel.Text = "Combo box bug:";
        
        // Create a combo-box.
        ComboBox oComboBox = new ComboBox();
        oComboBox.Location = new Point (10, 50);
        oComboBox.Size = new Size (150, 21);
        oComboBox.Items.AddRange (new object[]
            { "A", "A B", "A C", "A B C", "A C B", "A B C D", "A C B D" });
        oComboBox.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
        oComboBox.AutoCompleteSource = AutoCompleteSource.ListItems;
        oComboBox.SelectionChangeCommitted
            += new EventHandler (comboBox_SelectionChangeCommitted);
        
        // Create a form.
        Form oForm = new Form();
        oForm.Size = new Size (200, 150);
        oForm.Controls.Add (oLabel);
        oForm.Controls.Add (oComboBox);
        
        // Run this form.
        Application.Run (oForm);
    }
    static void comboBox_SelectionChangeCommitted (object sender,
        EventArgs e)
    {
        MessageBox.Show ("SelectionChangeCommitted");
    }
}

单击组合框的文本部分并输入“A”。您将获得自动完成建议的列表。用鼠标单击其中一个选项。 SelectionChangeCommitted 事件不会发生!

选择一个菜单项而不使用自动完成。您将收到一个消息框,显示 SelectionChangeCommitted 事件已发生!

鉴于用户在这两种情况下都更改了选择,不应该在两种情况下都调用SelectionChangeCommitted吗?

使用SelectedIndexChanged 事件不是一个选项,因为对于这个固定示例背后的应用程序,我只希望它在用户进行选择时发生,而不是在以编程方式设置时发生。

编辑 2020 年 10 月 28 日:我发现了另一个没有调用 SelectionChangeCommitted 的案例!甚至不需要为问题发生设置自动完成!单击以打开组合框,按与组合框项目之一的开头匹配的键,然后按 Tab 键离开。组合框项目被选中,但 SelectionChangeCommitted 未被调用!我修改后的答案如下。

【问题讨论】:

  • 我试过你的代码,但一切都很好。
  • this issue 有帮助吗?
  • @spajce:我运行了编写的示例源代码并复制了所描述的行为。 Win 8,VS 2010,.NET 4 客户端配置文件。 Note that the expected behavior is a pop up when selecting a value as part of auto complete.
  • 显然DropDownClosed 也没有被调用,参考我之前的帖子。
  • @Guvante:是的,this issue 有帮助,但对于我在自动完成过程中单击某个项目的情况没有帮助。

标签: c# winforms autocomplete combobox


【解决方案1】:

使用SelectedIndexChanged 事件不是一个选项,因为对于这个固定示例背后的应用程序,我只希望它在用户进行选择时发生,而不是在以编程方式设置时发生。

您还可以通过编写一个包装方法来更改暂时禁用您的事件的选择来完成此操作。

不幸的是,对于更一般的情况(例如您不控制 ComboBox 或如何访问它的情况),SelectionChangeCommitted 未启动的问题我不知道解决方案。

编辑:

我制作了 ComboBox 调用的所有事件的流媒体,并且似乎没有任何其他事件会满足您的需求。我能想到的唯一解决方案是挂钩自动完成触发的事件。困难在于知道这些事件是什么,因为从我的小测试显示,它们似乎不会触发ComboBox

【讨论】:

    【解决方案2】:

    仅供参考,这是我想出的最佳解决方案。显然,这是 ComboBox 子类上的 Leave 事件处理程序。 SelectionChangeCommitted 事件不会在鼠标单击时发生,但至少会在 GUI 交互的正常流程中发生。

    private void this_Leave (object sender, EventArgs e)
    {
        // If this is an autocomplete combo-box, select the
        // item that was found by autocomplete.
        // This seems like something that ComboBox should be
        // doing automatically...I wonder why it doesn't?
        if (this.AutoCompleteMode != AutoCompleteMode.None)
        {
            // Determine which combo-box item matches the text.
            // Since IndexOf() is case-sensitive, do our own
            // search.
            int iIndex = -1;
            string strText = this.Text;
            ComboBox.ObjectCollection lstItems = this.Items;
            int iCount = lstItems.Count;
            for (int i = 0; i < iCount; ++i)
            {
                string strItem = lstItems[i].ToString();
                if (string.Compare (strText, strItem, true) == 0)
                {
                    iIndex = i;
                    break;
                }
            }
    
            // If there's a match, and this isn't already the
            // selected item, make it the selected item.
            //
            // Force a selection-change-committed event, since
            // the autocomplete was driven by the user.
            if (iIndex >= 0
            && this.SelectedIndex != iIndex)
            {
                this.SelectedIndex = iIndex;
                OnSelectionChangeCommitted (EventArgs.Empty);
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      如果有人遇到这个问题,我建议一个适合我的解决方案...

      想一想,要接受Combo-box的建议,一般用户需要用Enter键按下。

      您可以写入 Combo-box 属性的 KeyDown 事件,如下所示:

          private void cboProperty_SelectionChangeCommitted(object sender, EventArgs e)
          {
             //Call here the event of SelectionChangeCommitted
             cboProperty_SelectionChangeCommitted(sender,null);
          }
      

      它将在正确的时间引发 SelectionChangeCommitted。

      【讨论】:

      • 用户也可以开始处理表单的另一部分,而不是完全按下键。另外,您的 cboProperty_SelectionChangeCommitted() 方法是否在调用自身?这不是无限循环吗?
      【解决方案4】:

      这个解决方法对我来说很好,也希望对你有用。当通过在组合框中键入数据来使用自动完成以通过键盘或鼠标选择获取项目时,您需要 _KeyDown 事件。从内部调用 _SelectionChangeCommitted 方法,该方法包含其他操作的代码。见以下代码:

          private void YourComboBox_KeyDown(object sender, KeyEventArgs e)
          {
              //Works also when user select and click on autocomplete list.
              if (e.KeyCode == Keys.Enter && YourComboBox.SelectedItem != null)
                  YourComboBox_SelectionChangeCommitted(sender, e);
          }
      

      【讨论】:

        【解决方案5】:

        对于上面提到的非自动完成案例(即我的 2020 年 10 月 28 日编辑),ComboBox 子类上的这个 Leave 事件处理程序包含新案例和旧案例,只要因为您的SelectionChangeCommitted 事件处理程序是idempotent。与我之前的答案相比,它删除了自动完成的测试,并始终调用OnSelectionChangeCommitted()

        private void this_Leave (object sender, EventArgs e)
        {
            // Determine which combo-box item matches the text.
            // Since IndexOf() is case-sensitive, do our own
            // search.
            int iIndex = -1;
            string strText = this.Text;
            ComboBox.ObjectCollection lstItems = this.Items;
            int iCount = lstItems.Count;
            for (int i = 0; i < iCount; ++i)
            {
                string strItem = lstItems[i].ToString();
                if (string.Compare (strText, strItem, true) == 0)
                {
                    iIndex = i;
                    break;
                }
            }
        
            // If there's a match, and this isn't already the
            // selected item, make it the selected item.
            if (iIndex >= 0
            && this.SelectedIndex != iIndex)
                this.SelectedIndex = iIndex;
        
            // Force a selection-change-committed event, since
            // the autocomplete was driven by the user.
            OnSelectionChangeCommitted (EventArgs.Empty);
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-11-29
          • 1970-01-01
          • 2019-02-16
          • 1970-01-01
          • 2019-08-12
          相关资源
          最近更新 更多