【问题标题】:Search anywhere in list, not just the first letter在列表中的任意位置搜索,而不仅仅是第一个字母
【发布时间】:2016-03-31 22:49:15
【问题描述】:

我想做的是在 ComboBox 中搜索一个单词,或者像这样的单词的一部分:

例如,我的组合框中有这些条目:

abc
Abc
Dabc
adbdcd

当我搜索“abc”时,它应该会显示列表中的每个人,除了“

adbdcd"

我从 mysql 数据库中填充我的组合框,所以我的项目位于“Collection”中。

我启用了自动完成功能(模式:SuggestAppend,来源:ListItems

这是代码,我现在正在使用:

private void comboBox1_KeyPress(object sender, KeyPressEventArgs e) { 组合键按下(); }

    private void comboBox1_TextChanged(object sender, EventArgs e)
    {
        if (comboBox1.Text.Length == 0) comboKeyPressed();
    }

    private void comboKeyPressed()
    {
        comboBox1.DroppedDown = true;

        object[] originalList = (object[])comboBox1.Tag;
        if (originalList == null)
        {
            // backup original list
            originalList = new object[comboBox1.Items.Count];
            comboBox1.Items.CopyTo(originalList, 0);
            comboBox1.Tag = originalList;
        }

        // prepare list of matching items
        string s = comboBox1.Text.ToLower();
        IEnumerable<object> newList = originalList;
        if (s.Length > 0)
        {
            newList = originalList.Where(item => item.ToString().ToLower().Contains(s));
        }

        // clear list (loop through it, otherwise the cursor would move to the beginning of the textbox...)
        while (comboBox1.Items.Count > 0)
        {
            comboBox1.Items.RemoveAt(0);
        }

        // re-set list
        comboBox1.Items.AddRange(newList.ToArray());
    }

这段代码的问题是,如果我在示例列表中搜索“abc”,“adbdcd”也会出现。当我在组合框中按退格键时,此代码会随机使我的程序崩溃。

【问题讨论】:

  • 在 text_Changed 事件中这个检查有什么用? comboBox1.Text.Length == 0 或者是 !=0 ?

标签: c# combobox autocomplete


【解决方案1】:

如果要搜索的条目数量总是很少——可能不超过几百个——那么一个简单的实现就足够了。

// Select all words that contain our query string
var matchingWords = wordList.Where(word => word.Contains(query));

这是一种幼稚的线性实现,对于大数据将变得非常缓慢。但是,对于小数据,这非常容易。只需将新子集输入到您的组合框即可。

您可能希望使用其可选的第二个参数为 Contains() 调用添加不区分大小写的功能。

【讨论】:

    【解决方案2】:

    所以,你想要全文搜索。

    您的数据来自哪里?它是什么样的数据?什么是边缘情况?

    对于数据库,我喜欢使用 Sphinx 进行全文索引。

    对于内存数据,高效的全文搜索算法包括 Suffix Arrays、Suffix Trees 和 Patricia Tries。实施它们可能是一个有趣(且耗时)的挑战。或者您可能会在网上找到合适的实现。您可以找到这些算法的准系统实现,以及全文搜索的更完善的实现,例如 Lucene

    为了让您大致了解它们的工作原理,想象一下您存储的每个单词的每个可能的后缀,例如needle:

    • edle
    • 闲置
    • e

    将所有这些后缀放入有序的数据结构中,例如有序数组或列表(用于静态数据)或 B 树或SortedDictionary(如果定期添加数据)。插入needle后,会包含:

    • 闲置
    • e
    • edle

    现在我们可以使用二分搜索或更好的方法搜索单词的任何部分(例如edl),看看我们是否有命中。

    要提取更多信息,而不仅仅是是否我们有命中,我们可以将数据添加到,例如,SortedDictionary 的。 (我们使用后缀作为 keys。)有趣的数据可能是整个单词,或者可能是原始文本和遇到该单词的位置。

    【讨论】:

      【解决方案3】:

      这是崩溃的根本原因:

          while (comboBox1.Items.Count > 0)
          {
              // this is raising exception if you try to remove the last item
              // Check the doc of RemoveAt
              comboBox1.Items.RemoveAt(0);
          }
      

      改用它:

      comboBox1.Items.Clear();
      

      但是,您要实现的目标仍不清楚。 如果组合框的文本为空,那么除了清除组合框并将相同的项目重新添加到组合框之外,什么都不会发生。

      我的理解是,您在启用它的同时尝试复制完成行为。它也可能引发异常 (AccessViolationException),因为您尝试修改 Items 集合,而框架尝试执行相同操作。

      如果您对默认的自动完成行为不满意,请将其禁用并尝试在 comboKeyPressed 方法中完全实现它。 这意味着每当文本被修改时调用它。

      修改您的代码以使其工作禁用自动完成):

          private void comboBox1_TextChanged(object sender, EventArgs e)
          {
              comboKeyPressed();
          }
      
          private void comboKeyPressed()
          {
              if (comboBox1.Text == lastFilter)
              {
                  return;
              }
      
              object[] originalList = (object[]) comboBox1.Tag;
              if (originalList == null)
              {
                  // backup original list
                  originalList = new object[comboBox1.Items.Count];
                  comboBox1.Items.CopyTo(originalList, 0);
                  comboBox1.Tag = originalList;
              }
      
              // prepare list of matching items
              string s = comboBox1.Text.ToLower();
              IEnumerable<object> newList = originalList;
              if (s.Length > 0)
              {
                  newList = originalList.Where(item => item.ToString().ToLower().Contains(s));
              }            
              // clear list (loop through it, otherwise the cursor would move to the beginning of the textbox...)
              comboBox1.Items.Clear();
              // re-set list
              comboBox1.Items.AddRange(newList.ToArray());
      
              comboBox1.Select(Text.Length -1, 0);
      
              lastFilter = comboBox1.Text;
              comboBox1.DroppedDown = true;
          }
      

      【讨论】:

        猜你喜欢
        • 2015-11-29
        • 2015-01-24
        • 1970-01-01
        • 2022-11-15
        • 2014-02-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多