【问题标题】:Listview is too slow when loadingListview加载时太慢了
【发布时间】:2023-03-27 06:48:02
【问题描述】:

我正在用 1286 条记录填充列表视图,大约需要 34 秒。我在互联网上搜索并找到了这个

 private void PopulateListViewWithCables(List<Cable> cables)
    {
        listView1.BeginUpdate();
        listView1.Items.Clear();
        System.Diagnostics.Stopwatch myStopWatch = new System.Diagnostics.Stopwatch();
        myStopWatch.Start(); 
        AddItemsToListView(cables);
        myStopWatch.Stop();
        string str;
        str = myStopWatch.Elapsed.Seconds.ToString();
        string s = str;
        listView1.EndUpdate();
    }


    private void AddItemsToListView(List<Cable> cables)
    {
        //Add to an ArrayList first because AddRange is a lot faster
        //than Add when dealing with lots of elements.
        //Also, there is no need to turn off the sorter when adding
        //all the elements at once

        ArrayList listItems = new ArrayList();

        foreach (Cable cable in cables)
        {
            // The if statement can be removed if all items in
            // MyDataClassCollection should be added to the ListView.               
            listItems.Add(CreateListViewItem(GetCableNavigation(cable)));
        }

        // Adds the items to the ListView
        listView1.Items.AddRange(
        (ListViewItem[])listItems.ToArray(typeof(ListViewItem)));
    }

    // Generate a listviewitem by using the myDataItem instance.
    private static ListViewItem CreateListViewItem(Cable cable)
    {
        ListViewItem item = new ListViewItem(
        new string[]
    {
    cable.Id.ToString(),
       cable.Item.ToString(),
       cable.GeneralFormat + cable.TagNo.ToString() + cable.EndString,
       cable.FromBay + cable.FromPanel, 
       cable.ToBay + cable.ToPanel,
       cable.CableProperty.Catalogue.Type,
       cable.CableProperty.Catalogue.CoreSize,
       cable.CableProperty.CableApplication.Application, 
       cable.CableRevision,
       cable.MinRequestCore.ToString(), 
       cable.Route, 
       cable.Distance.ToString(),
       ((CableStatusEnum)cable.Status).ToString(),
       cable.CableProperty.ProjectId.ToString(), 
       cable.CablePropertyId.ToString(),
       cable.TagNo.ToString(), 
       cable.GeneralFormat,
       cable.Length.ToString(), 
       cable.EndString, 
       cable.User.LastName, 
       cable.EditedOn.ToString()                
   });          
        return item;
    }

   private Cable GetCableNavigation(Cable cable)
    {
        CurrentInfo currentInfo = CurrentInfo.RecGetSingle();
        if (cable.CableProperty == null || cable.User == null)
        {
            using (CableServiceClient client = new CableServiceClient())
            {
                SearchElement[] criteria = new SearchElement[] { new SearchElement { Comparison = "=", FieldName = "Id", FieldValue = cable.Id, LogicalOperator = "" } };
                cable = client.GetCables(criteria, null, "CableProperty,CableProperty.Catalogue,CableProperty.CableApplication,User").SingleOrDefault();
                client.Close();
            }
        }
        return cable;
    }

我可以将加载时间减少到29秒,但是对于1286条记录来说仍然太多了,如何减少加载数据的时间?

谢谢

【问题讨论】:

  • 贴出GetCableNavigation的代码
  • 我会使用通用列表 (List&lt;ListViewItem&gt;) 而不是 ArrayList
  • 从哪里获得List&lt;Cable&gt; cables?也许涉及延迟加载和多次往返数据库?
  • @nnmmss 看到您的编辑后,您应该尝试从服务调用中一次获取所有Cables。
  • 您需要致电SingleOrDefault吗? FirstOrDefault 不够吗? SingleOrDefault 有点慢。

标签: c# winforms listview


【解决方案1】:

假设这是System.Windows.Forms.ListView,您应该查看virtualization。使用虚拟化会自动为您执行列表视图项的延迟加载,从而减少内存使用和更好的响应时间。

【讨论】:

  • VirtualMode 的优点 - OP 可以在需要时加载有线导航。当然取决于查询速度
  • @lazyberezovsky 确实,这取决于查询速度,但 OP 说大约 1200 个项目大约需要 30 秒,即每秒 40 个项目或每个项目 25 毫秒。这比人类的感知速度要快。
  • 1200 items 并不算多,可以考虑使用virtualization,尽管使用它会更好。但原来的原因造成相当大的延迟应该修复另一种方式,而不是使用virtualization
【解决方案2】:

我认为您的代码大部分时间都在创建服务客户端并从中查询数据。我建议您将数据加载(您可以在后台线程中进行)和数据显示操作分开。还可以考虑重用单个服务客户端实例。

类似:

private void AddItemsToListView(List<Cable> cables)
{
    var items = GetCableNaviagations(cables)
                   .Select(CreateListViewItem)                       
                   .ToArray();

    listView1.Items.AddRange(items);
}

// consider to do data retrieving in background thread
private IEnumerable<Cable> GetCableNaviagations(IEnumerable<Cable> cables)
{
   var arg = "CableProperty,CableProperty.Catalogue,CableProperty.CableApplication,User";

   using (CableServiceClient client = new CableServiceClient())
   {
        foreach(var cable in cables)
        {
           var criteria = new SearchElement[] { 
              new SearchElement { 
                   Comparison = "=", 
                   FieldName = "Id", 
                   FieldValue = cable.Id, 
                   LogicalOperator = ""      
              } };

           yield return client.GetCables(criteria, null, arg).SingleOrDefault();
        }

        client.Close();
    }
}

【讨论】:

  • 并且可能一次获得所有Cables 以减少往返和连接开销。
  • @Ahmed 为什么你认为它是从数据库中检索数据。假设我有一个包含 1286 个项目的列表,我想从该列表中填充列表视图,我在不到一秒的时间内从数据库中获取数据
  • 我认为@Ahmed 说的是一次获取所有有线电视信息,您似乎从数据库中获取了所有有线电视信息(在此代码之前),但随后您访问 CableServiceClient 进行了 1286 次.如果您可以在第一个查询中带上来自服务客户端的所有信息,它可能会快得多。
  • @lazyberezovsky 我以前没做过。如何在后台线程中做到这一点?可以举个例子吗?
  • @nnmmss 您可以使用BackgroundWorker 组件在后台线程中准备数据。但是试试这段代码,让我知道它是否足够快
【解决方案3】:

它不会改变数据检索的时间,但有助于列表视图的“加载”阶段。创建一个返回“ListViewItem[]”的方法并使用 Yield 返回:

private void PopulateListViewWithCables(List<Cable> cables)
{
    listView1.BeginUpdate();
    listView1.Items.Clear();
    System.Diagnostics.Stopwatch myStopWatch = new System.Diagnostics.Stopwatch();
    myStopWatch.Start();         
    foreach (listitem i in AddItemsToListView(cables))
    {
            cables.Add(i);
    }
    AddItemsToListView(cables);
    myStopWatch.Stop();
    string str;
    str = myStopWatch.Elapsed.Seconds.ToString();
    string s = str;
    listView1.EndUpdate();
}


private IEnumerable<ListViewItem> AddItemsToListView(List<Cable> cables)
{
    foreach (Cable cable in cables)
    {        
        yield return(CreateListViewItem(GetCableNavigation(cable)));
    }

}

我没有检查它是否编译就写了它。

【讨论】:

  • 对不起,我听不懂,你能告诉我一点点吗
  • 是的,我的示例基于收益回报机制。 [link]msdn.microsoft.com/en-us/library/vstudio/9k7k7cf0.aspx[/link] 因此,在 foreach 循环中,您不必等待所有 ListItems[] 都被填充以将其放在 ListView 上,但同时创建项目,就像在一个单独的线程中一样,每个项目都是一一创建并返回。因此 ListView 开始快速填充。
猜你喜欢
  • 2019-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-03
  • 1970-01-01
  • 2013-08-18
  • 1970-01-01
  • 2021-05-30
相关资源
最近更新 更多