列表框的实时过滤

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)

Las*_*olt 1

你可以做两件事:

  1. 使用负责过滤的第二个线程使您的 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)
  2. 使您的过滤算法更加高效。您是否使用 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)