代码之家  ›  专栏  ›  技术社区  ›  Zlatan Omerović

在WinForms中线程安全且快速地向ListView添加20000个项

  •  0
  • Zlatan Omerović  · 技术社区  · 4 年前

    我有一个自定义的WinForms控件,其中包含一个ListView,该ListView必须从CSV文件中读取多达20000个项目。

    我使用CSVHelper在单独的线程中加载文件,然后调用 listView1.Add 在UI线程上添加它们 ListView .

    我目前的实现(如您在代码中看到的)可能很糟糕,因此在将这20000个项目添加到 ListView .

    请注意,我对线程安全和多线程开发相当陌生。

    这是我的自定义控件的整个代码:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using System.Globalization;
    using CsvHelper;
    using System.IO;
    using System.Threading;
    
    namespace MyDevTaskDesktop
    {
        public partial class MyCSVReaderControl : UserControl
        {
            List<Product> loadedProducts;
    
            public MyCSVReaderControl()
            {
                InitializeComponent();
            }
    
            private void BtnBrowse_Click(object sender, EventArgs e)
            {
                CsvFileDialog.ShowDialog();
            }
    
            private void CsvFileDialog_FileOk(object sender, CancelEventArgs e)
            {
                TxtFilename.Text = CsvFileDialog.FileName;
    
                listView1.Items.Clear();
    
                listView1.Visible = false;
                listView1.View = View.Details;
                listView1.AllowColumnReorder = true;
                listView1.GridLines = true;
                listView1.Sorting = SortOrder.Ascending;
                listView1.FullRowSelect = true;
                listView1.AllowColumnReorder = true;
    
                listView1.Columns.Add("#", 50, HorizontalAlignment.Left);
                listView1.Columns.Add("Filename", 100, HorizontalAlignment.Left);
                listView1.Columns.Add("Id", 100, HorizontalAlignment.Left);
                listView1.Columns.Add("Quantity", 50, HorizontalAlignment.Right);
                listView1.Columns.Add("Price", 50, HorizontalAlignment.Right);
    
                LoadCSVThread();
    
                loadedProducts = new List<Product>();
            }
            public void LoadCSV()
            {
                using (var reader = new StreamReader(CsvFileDialog.FileName))
                using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
                {
                    csv.Configuration.HasHeaderRecord = false;
                    csv.Configuration.IgnoreBlankLines = true;
                    csv.Configuration.BadDataFound = null;
                    csv.Configuration.AutoMap<CsvProduct>();
                    csv.Configuration.Delimiter = ";";
    
                    var records = csv.GetRecords<CsvProduct>().ToList();
    
                    int index = 0;
                    string firstFilename = null;
                    Color foreColor;
    
                    foreach (CsvProduct product in records)
                    {
                        index++;
    
                        if (firstFilename == null)
                        {
                            firstFilename = product.Filename;
                        }
    
                        foreColor = (firstFilename == product.Filename) ? Color.Red : Color.Blue;
    
                        loadedProducts.Add(new Product()
                        {
                            Color = foreColor,
                            Index = index,
                            Filename = product.Filename,
                            Id = product.Id,
                            Quantity = product.Quantity,
                            Price = product.Price
                        });
    
                        if (LblProcess.InvokeRequired)
                        {
                            LblProcess.Invoke((MethodInvoker)delegate ()
                            {
                                LblProcess.Text = "Processed " + index + " records...";
                            });
                        }
                    }
    
                    if (LblProcess.InvokeRequired)
                    {
                        LblProcess.Invoke((MethodInvoker)delegate ()
                        {
                            LblProcess.Text = "Finished!";
                            LblProcess.Visible = false;
                        });
                    }
    
                    if (listView1.InvokeRequired)
                    {
                        listView1.Invoke((MethodInvoker)delegate ()
                        {
                            listView1.Visible = true;
    
                            listView1.BeginUpdate();
    
                            foreach (var item in loadedProducts)
                            {
                                ListViewItem listItem = new ListViewItem(new string[]
                                {
                                    item.Index.ToString(),
                                    item.Filename,
                                    item.Id,
                                    item.Quantity.ToString(),
                                    item.Price.ToString()
                                });
    
                                listItem.ForeColor = item.Color;
    
                                listView1.Items.Add(listItem);
                                //listView1.EnsureVisible(listView1.Items.Count - 1);
                            }
    
                            listView1.Sort();
                            listView1.EndUpdate();
                        });
                    }
                }
            }
    
            public void LoadCSVThread()
            {
                Thread worker = new Thread(LoadCSV);
                worker.IsBackground = true;
                worker.SetApartmentState(ApartmentState.STA);
                worker.Start();
            }
    
            private class Product
            {
                public Color Color { get; set; }
                public int Index { get; set; }
                public string Filename { get; set; }
                public string Id { get; set; }
                public int Quantity { get; set; }
                public double Price { get; set; }
            }
    
            private class CsvProduct
            {
                public string Filename { get; set; }
                public string Id { get; set; }
                public int Quantity { get; set; }
                public double Price { get; set; }
            }
        }
    }
    

    我也试过 async/await ,但最终由于我尚不清楚的原因而变得更慢。当我使用这些时,UI中的任何内容在迭代数组时都没有更新。

    我知道我的方法来填补 loadedProducts List 首先迭代该列表以更新视图是不好的,但目前这会产生最快的结果——这是我能做的最好的事情,因为如果我在更新的同时更新UI LblProcess 标签文本,它会变慢,加倍。

    那么,将20000个或更多项目添加到 ListView ?

    0 回复  |  直到 4 年前