我想要做的是搜索一个ComboBox,一个单词或一个单词的一部分,如下所示:
例如,我在组合框中有这些条目:
abc
Abc
Dabc
adbdcd
当我搜索“abc
”时,它应该向我展示列表中的每一个,除了“
adbdcd"
我从mysql数据库中填充我的组合框,所以我的项目位于“Collection
”。
我启用了自动完成功能(模式:SuggestAppend,来源:ListItems )
这是我现在正在使用的代码:
private void comboBox1_KeyPress(object sender,KeyPressEventArgs e) { comboKeyPressed(); }
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
”也会显示。当我在组合框中点击退格键时,这段代码会随机崩溃我的程序。
答案 0 :(得分:0)
这是崩溃的根本原因:
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();
然而,你想要实现的目标仍然不清楚。 如果combox的文本为空,那么除了清除并重新添加相同的项目到组合框之外,什么都不会发生。
我的理解是,您在尝试启用时会尝试复制完成行为。它也可以引发异常( 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;
}
答案 1 :(得分:0)
所以,你想要全文搜索。
您的数据来自哪里?它是什么类型的数据?有什么边缘案例?
对于数据库,我喜欢使用 Sphinx 进行全文索引。
对于内存数据,高效的全文搜索算法包括后缀数组,后缀树和Patricia尝试。实施它们可能是一项有趣(且耗时)的挑战。或者您可以在线找到合适的实施方案。您可以找到这些算法的准系统实现,以及更全面的全文搜索实现,例如 Lucene 。
为了让您了解它们的工作原理,想象一下您存储的每个单词的每个可能后缀,例如: needle
:
将所有这些后缀放入有序数据结构中,例如排序数组或列表(对于静态数据)或B树或SortedDictionary(如果定期添加数据)。插入needle
后,它将包含:
现在我们可以使用二分搜索或更好的方式搜索单词的任何部分(例如edl
),看看我们是否有点击。
要提取更多信息,而不仅仅是我们是否有点击,我们可以将数据添加到,例如,SortedDictionary的值。 (我们将后缀用作键。)有趣的数据可能是整个单词,也可能是遇到单词的原始文本和位置。
答案 2 :(得分:0)
如果要搜索的条目数总是很低 - 可能不会超过几百 - 那么一个简单的实现就足够了。
// Select all words that contain our query string
var matchingWords = wordList.Where(word => word.Contains(query));
这是天真的线性实现,对于大数据来说会变得很慢。但是对于小数据来说,它非常简单。只需将新子集提供给组合框即可。
您可能希望使用其可选的第二个参数为Contains()
调用添加不区分大小写。