Rya*_*yan 5 c# listbox filter winforms
我希望能够过滤包含1000个字符串的列表框,每个字符串长度为50-4000个字符,因为用户在文本框中输入没有延迟.
我目前正在使用一个计时器,TextChanged
在300毫秒内没有触发文本框事件后更新列表框.然而,这是非常生涩,ui有时会暂时冻结.
实现与此类似的功能的常规方法是什么?
编辑:我正在使用winforms和.net2.
谢谢
这是我目前使用的代码的精简版本:
string separatedSearchString = this.filterTextBox.Text;
List<string> searchStrings = new List<string>(separatedSearchString.Split(new char[] { ';' },
StringSplitOptions.RemoveEmptyEntries));
//this is a member variable which is cleared when new data is loaded into the listbox
if (this.unfilteredItems.Count == 0)
{
foreach (IMessage line in this.logMessagesListBox.Items)
{
this.unfilteredItems.Add(line);
}
}
StringComparison comp = this.IsCaseInsensitive
? StringComparison.OrdinalIgnoreCase
: StringComparison.Ordinal;
List<IMessage> resultingFilteredItems = new List<IMessage>();
foreach (IMessage line in this.unfilteredItems)
{
string message = line.ToString();
if(searchStrings.TrueForAll(delegate(string item) { return message.IndexOf(item, comp) >= 0; }))
{
resultingFilteredItems.Add(line);
}
}
this.logMessagesListBox.BeginUpdate();
this.logMessagesListBox.Items.Clear();
this.logMessagesListBox.Items.AddRange(resultingFilteredItems.ToArray());
this.logMessagesListBox.EndUpdate();
Run Code Online (Sandbox Code Playgroud)
你可以做两件事:
使用负责过滤的第二个线程使您的 UI 响应更快。一项真正伟大的新技术是反应式扩展 (Rx),它将完全满足您的需求。
我可以举个例子。我猜你用的是WinForms?你的一部分代码会有所帮助。
http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx
这是一个小预告:
Observable.Context = SynchronizationContext.Current;
var textchanged = Observable.FromEvent<EventArgs>(textBox1, "TextChanged");
textchanged.Throttle(300).Subscribe(ea =>
{
//Here 300 milisec. is gone without TextChanged fired. Do the filtering
});
Run Code Online (Sandbox Code Playgroud)使您的过滤算法更加高效。您是否使用 StartWith 或 Contains 之类的内容进行过滤?
您可以使用后缀树或列表项的所有前缀之类的东西并进行查找。但准确地描述你需要什么,我会找到一些简单但足够高效的东西。如果您想在 ListBox 中显示 100.000 个项目,但如果您只获取 - 比如说 100 个 - 它会很快(取消注释 .Take(100) 行),那么 UI 会非常重。如果搜索在另一个线程中完成,也可以做得更好一些。用 Rx 应该很容易,但我还没有尝试过。
更新
尝试这样的事情。它在这里可以很好地处理 100.000 个大约 10 个字符长的元素。它使用反应式扩展(前面的链接)。
此外,该算法很简单,如果您愿意的话可以做得更快。
private void Form1_Load(object sender, EventArgs e)
{
Observable.Context = SynchronizationContext.Current;
var textchanged = Observable.FromEvent<EventArgs>(textBox1, "TextChanged");
//You can change 300 to something lower to make it more responsive
textchanged.Throttle(300).Subscribe(filter);
}
private void filter(IEvent<EventArgs> e)
{
var searchStrings = textBox1.Text.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
//my randStrings is your unfiltered messages
StringComparison comp = StringComparison.CurrentCulture; //Do what you want here
var resultList = from line in randStrings
where searchStrings.All(item => line.IndexOf(item, comp) >= 0)
select line;
//A lot faster but only gives you first 100 finds then uncomment:
//resultList = resultList.Take(100);
listBox1.BeginUpdate();
listBox1.Items.Clear();
listBox1.Items.AddRange(resultList.ToArray());
listBox1.EndUpdate();
}
Run Code Online (Sandbox Code Playgroud)