【问题标题】:WPF C# 4.5 ListBox Update Very SlowWPF C# 4.5 ListBox 更新非常慢
【发布时间】:2013-11-06 02:31:15
【问题描述】:

我刚开始使用 Redis,并且正在将它用于我的个人项目之一。 Redis DB 包含大约 10k 个对象

public Class FileList
{
    public string FileName { get; set;} 
    public string FolderName { get; set;}
}

我最初在加载时在 ListBox ResultsView 中显示此列表。我有一个TextBox,我可以在其中输入字符,在TextBoxChanged_Event 上,我正在调用一个函数,该函数将在RedisDB 中查询包含我在TextBox 中键入的字符的所有FileList 对象并将其存储在一个ResultsList<FileList>,它将在foreach中循环,然后添加到ListBox

开始显示结果至少需要一秒钟,而且速度不是很快。

现在,如果我对 MasterList<FileList> 执行相同的查询,那么它会快一点,但仍然不够快。

昨天,我尝试使用 RavenDb,它费力地花费了很长时间来完成相同的任务。

是我在forach 中将Items 添加到ListBox 中,这需要花费很多时间,还是有什么可以像ItemSource 那样加快速度的方法,我试过但是给了我ItemList should be empty before binding的错误

我确实尝试了几乎类似问题的大多数答案,但没有一个对我有帮助。

代码

ResultsView.Items.Clear();
var redisClient = new RedisClient("localhost");
using (var client = redisClient.As<FileList>())
{
    var foldersFromRedis = client.GetAll().Where(fileList => fileList.FileName.Contains(this.Search.Text.ToLower()));
    foreach (FileList fileList in foldersFromRedis)
    {
        var listViewItem = new ListViewItem { Content = fileList.FileName , Tag = fileList.FolderName  };
        this.ResultsView.Items.Add(listViewItem);
    }
}
//this.ResultsView.ItemsSource = ResultsFileList;
<ListBox Height="374" ScrollViewer.VerticalScrollBarVisibility="Disabled" HorizontalAlignment="Left" Margin="10,0,0,0" Name="ResultsView" VerticalAlignment="Bottom" Width="405" BorderThickness="0" SelectionChanged="MovieNameSelectionChanged" FontFamily="Nobile" FontSize="13" Background="#A6FCFCFC" Foreground="Black" FontStretch="Normal">
  <GridView>
        <GridViewColumn Header="FileName" DisplayMemberBinding="{Binding FileName}"/>
        </GridView>
</ListBox>

更新 1:

添加 ViewCollectionSource 如下

 private void ApplyViewCollectionSource()
    {
        _viewSource.Filter += ViewSourceFilter;
        _viewSource.Source = _fileList = (List<FileList>)PopulateFileListEnglishWithReturn();
        ResultsView.ItemsSource = _viewSource.View;
        _timer.Interval = new TimeSpan(0, 0, 0, 0, 200);
        _timer.Tick += (o, e) =>
        {
            _timer.Stop();
            _viewSource.View.Refresh();
        };
        Search.TextChanged += (o, e) => _timer.Start();
    }

在构造函数中被调用。过滤器如下:

private void ViewSourceFilter(object sender, FilterEventArgs e)
{

    var src = e.Item as FileList;
    e.Accepted = src != null;
    if (string.IsNullOrEmpty(Search.Text)) return;
    var regex = new Regex(Search.Text, RegexOptions.IgnoreCase);
    e.Accepted = regex.IsMatch(src.FileName);
}

这非常适合我的要求,但仅在文本框中的第三个字符之后。输入第一个字符后,ListBox 需要 2 秒才能更新,第二个字符需要 1 秒。在此之后,几乎是瞬间。主列表_fileList大约有5000项。

有什么方法可以提高前 2 个字符搜索的速度?

【问题讨论】:

  • 您是如何使用绑定获取ItemList should be empty before binding 的? AlbumName 是如何适应这一切的?
  • 1) 你是否重新加载 TextChanged 中的所有数据?如果是这样考虑缓存几秒。 2) 将this.Search.Text.ToLower()的值缓存到局部变量中并在Where()中使用
  • @Noctis 是错字,已更正。我只是设置了 ItemSource={Binding} 并在代码隐藏中设置了 ItemSource。
  • ListBox 中移除GridView。这是完全错误的。

标签: c# wpf listbox redis .net-4.5


【解决方案1】:

不要在每次要过滤时重新加载数据。 WPF 具有适当的机制来过滤现有数据,而无需往返服务器。考虑使用CollectionView 为您进行过滤。集合视图允许您更改呈现给用户的内容,而无需触及数据本身。

您可以在这里找到一个工作示例:http://social.msdn.microsoft.com/Forums/vstudio/en-US/0d2b882d-cf56-4385-9b76-dd280c4c35b1/filter-a-readonlycollection-for-binding-a-listview?forum=wpf#213dda80-5d29-47fa-b832-ea7e098590d7

【讨论】:

  • 我同意 CollectionView,但有趣的是,我们曾经在工作中遇到过问题,我实际上对在 10K 的集合中搜索字符串与 VS 之间的差异进行了基准测试。使用集合视图过滤器......搜索简单的方式更快......
【解决方案2】:

首先,我个人不喜欢空绑定...我会给 Window / UserControl 一个名字

<Window ...
    Name="your_name_here">

然后在绑定中,绑定到后面代码上的一个属性(假设它叫做property_for_binding),你会像这样绑定它:

<Listbox ...
   ItemSource={Binding ElementName=your_name_here , Path=property_for_binding} />

话虽如此,我确实更喜欢 MVVM 方法,但没关系,回到正题... 您是否尝试在您的功能上放置一些秒表?您可以在调用 DB 之前、调用 DB 之后(排除这是您的 DB 错误)、然后在排序之前/之后打印到控制台,依此类推。这样,您就可以看到最慢的因素是什么。

正如 cmets 所说,您可能不应该为文本框中的每个更改查询数据库(假设您在文本更改时执行此操作,而不是在完成输入后通过按钮),因为通常您的数据库会给出相同的结果页面内的所有搜索(除非它从其他地方被大量修改,在这种情况下,我猜你别无选择)。

因此,将结果缓存一次,搜索该变量,然后用结果填充您将绑定到的属性。

编辑:
您总是可以在用户输入 3 个字符后运行搜索,从而解决问题(假设有 10K 个项目,一个字母无论如何都无关紧要,他会继续搜索)。

其他选项是仅在上次击键后经过一段时间后进行搜索。

最后但并非最不重要的一点是,这里有一个链接可能也有帮助(解决您将项目添加到列表的第一个循环):Why is AddRange faster than using a foreach loop?

【讨论】:

    【解决方案3】:

    这些问题在我发现的 ListBox 中很常见,您是否检查过数据虚拟化没有被禁用并且 VirtualizationMode 设置为“Recycling”?

    我发现有帮助的另一件事是使用 DispatchTimer 在输入最后一个字符几秒钟后触发搜索。大多数用户不介意最后稍等片刻,但他们不希望 GUI 在打字时感觉迟钝。

    【讨论】:

      猜你喜欢
      • 2017-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-07
      • 2018-07-04
      • 2021-03-27
      • 2011-06-11
      相关资源
      最近更新 更多