Pertanyaan Bagaimana cara mempercepat penambahan item ke ListView?


Saya menambahkan beberapa ribu (misalnya 53.709) item ke WinForms ListView.

Coba 1: 13,870 ms

foreach (Object o in list)
{
   ListViewItem item = new ListViewItem();
   RefreshListViewItem(item, o);
   listView.Items.Add(item);
}

Ini berjalan sangat buruk. Perbaikan pertama yang jelas adalah menelepon BeginUpdate/EndUpdate.

Mencoba 2: 3,106 ms

listView.BeginUpdate();
foreach (Object o in list)
{
   ListViewItem item = new ListViewItem();
   RefreshListViewItem(item, o);
   listView.Items.Add(item);
}
listView.EndUpdate();

Ini lebih baik, tetapi masih urutan besarnya terlalu lambat. Mari kita pisahkan pembuatan ListViewItems dari penambahan ListViewItems, jadi kami menemukan pelaku sebenarnya:

Percobaan 3: 2,631 ms

var items = new List<ListViewItem>();
foreach (Object o in list)
{
   ListViewItem item = new ListViewItem();
   RefreshListViewItem(item, o);
   items.Add(item);
}

stopwatch.Start();

listView.BeginUpdate();
    foreach (ListViewItem item in items)
        listView.Items.Add(item));
listView.EndUpdate();

stopwatch.Stop()

Hambatan sebenarnya adalah menambahkan item. Mari kita coba mengubahnya menjadi AddRange bukannya a foreach

Percobaan 4:  2,182 ms

listView.BeginUpdate();
listView.Items.AddRange(items.ToArray());
listView.EndUpdate();

Sedikit lebih baik. Mari kita pastikan bahwa bottleneck tidak ada di dalam ToArray()

Mencoba 5:  2,132 ms

ListViewItem[] arr = items.ToArray();

stopwatch.Start();

listView.BeginUpdate();
listView.Items.AddRange(arr);
listView.EndUpdate();

stopwatch.Stop();

Keterbatasan tampaknya menambahkan item ke listview. Mungkin kelebihan lainnya AddRange, di mana kami menambahkan ListView.ListViewItemCollection daripada sebuah array

Usaha 6:  2,141 ms

listView.BeginUpdate();
ListView.ListViewItemCollection lvic = new ListView.ListViewItemCollection(listView);
lvic.AddRange(arr);
listView.EndUpdate();

Itu tidak lebih baik.

Sekarang saatnya melakukan peregangan:

  • Langkah 1 - pastikan tidak ada kolom yang diatur "lebar otomatis":

    enter image description here

    Memeriksa

  • Langkah 2 - pastikan ListView tidak mencoba mengurutkan item setiap kali saya menambahkan satu:

    enter image description here

    Memeriksa

  • Langkah 3 - Tanyakan stackoverflow:

    enter image description here

    Memeriksa

catatan: Tentunya ListView ini tidak dalam mode virtual; karena Anda tidak / tidak bisa "menambahkan" item ke tampilan daftar virtual (Anda mengatur VirtualListSize). Untungnya pertanyaan saya bukan tentang tampilan daftar dalam mode virtual.

Adakah sesuatu yang saya lewatkan yang mungkin menyebabkan penambahan item ke dalam listview menjadi sangat lambat?


Obrolan Bonus

Saya tahu kelas Windows ListView dapat melakukan lebih baik, karena saya dapat menulis kode yang melakukannya 394 ms:

ListView1.Items.BeginUpdate;
for i := 1 to 53709 do
   ListView1.Items.Add();
ListView1.Items.EndUpdate;

yang bila dibandingkan dengan kode C # yang setara 1,349 ms:

listView.BeginUpdate();
for (int i = 1; i <= 53709; i++)
   listView.Items.Add(new ListViewItem());
listView.EndUpdate();

adalah urutan besarnya lebih cepat.

Properti apa dari wrapper WinForms ListView yang saya lewatkan?


75
2018-01-25 18:41


asal


Jawaban:


Saya melihat kode sumber untuk tampilan daftar dan saya melihat beberapa hal yang dapat memperlambat kinerja dengan faktor 4 atau lebih yang Anda lihat:

di ListView.cs, ListViewItemsCollection.AddRange panggilan ListViewNativeItemCollection.AddRange, dimana saya memulai audit saya

ListViewNativeItemCollection.AddRange (Dari baris: 18120) memiliki dua melewati seluruh koleksi nilai, satu untuk mengumpulkan semua item yang dicentang yang lain untuk 'memulihkan' mereka setelah InsertItems disebut (mereka berdua dijaga oleh pemeriksaan terhadap owner.IsHandleCreated, pemilik menjadi ListView) lalu menelepon BeginUpdate.

ListView.InsertItems (Dari baris: 12952), panggilan pertama, memiliki traverse lain dari seluruh daftar, kemudian ArrayList.AddRange dipanggil (mungkin yang lain lewat di sana) kemudian lulus lagi setelah itu. Memimpin ke

ListView.InsertItems (dari baris: 12952), panggilan kedua (via EndUpdate) melewati lain di mana mereka ditambahkan ke HashTable, dan a Debug.Assert(!listItemsTable.ContainsKey(ItemId)) akan memperlambatnya lebih lanjut dalam mode debug. Jika pegangan tidak dibuat, itu akan menambah item ke ArrayList, listItemsArray tapi if (IsHandleCreated), maka panggilan itu

ListView.InsertItemsNative (Dari baris: 3848) lulus akhir melalui daftar di mana sebenarnya ditambahkan ke daftar nama asli. Sebuah Debug.Assert(this.Items.Contains(li) juga akan memperlambat kinerja dalam mode debug.

Jadi ada BANYAK ekstra melewati seluruh daftar item di. Bersih kontrol sebelum pernah benar-benar memasukkan item ke daftar nama asli. Beberapa lintasan dijaga dengan pemeriksaan terhadap Gagang yang dibuat, jadi jika Anda dapat menambahkan item sebelum pegangan dibuat, mungkin menghemat waktu Anda. Itu OnHandleCreatedmetode mengambil listItemsArray dan panggilan InsertItemsNative langsung tanpa semua keributan ekstra.

Anda bisa membaca ListView kode di sumber referensi diri Anda dan lihatlah, mungkin saya melewatkan sesuatu.

Dalam edisi Maret 2006 Majalah MSDN ada sebuah artikel yang disebut Winning Forms: Practical Tips for Boosting The Performance of Windows Forms Apps.

Artikel ini berisi tips untuk meningkatkan kinerja ListViews, di antara hal-hal lainnya. Sepertinya mengindikasikan bahwa lebih cepat untuk menambahkan item sebelum pegangan dibuat, tetapi Anda akan membayar harga ketika kontrol diberikan. Mungkin menerapkan pengoptimalan render yang disebutkan di komentar dan menambahkan item sebelum pegangan dibuat akan mendapatkan yang terbaik dari kedua dunia.

Edit: Uji hipotesis ini dalam berbagai cara, dan sambil menambahkan item sebelum membuat pegangan suuuper cepat, itu secara eksponensial lebih lambat ketika pergi untuk membuat pegangan. Saya bermain dengan mencoba mengelabui untuk membuat pegangan, kemudian entah bagaimana mendapatkannya untuk menelepon InsertItemsNative tanpa melalui semua laluan tambahan, tetapi sayangnya saya telah digagalkan. Satu-satunya hal yang dapat saya pikirkan mungkin adalah, untuk membuat Win32 ListView Anda dalam proyek c ++, mengisinya dengan item, dan menggunakan hooking untuk menangkap pesan CreateWindow yang dikirim oleh ListView saat membuat pegangannya dan mengembalikan referensi ke win32 ListView bukan jendela baru .. tapi siapa yang tahu apa yang mempengaruhi sisi akan ada ... seorang guru Win32 perlu berbicara tentang ide gila itu :)


21
2018-01-26 08:27



Saya menggunakan kode ini:

ResultsListView.BeginUpdate();
ResultsListView.ListViewItemSorter = null;
ResultsListView.Items.Clear();

//here we add items to listview

//adding item sorter back
ResultsListView.ListViewItemSorter = lvwColumnSorter;


ResultsListView.Sort();
ResultsListView.EndUpdate();

Saya telah menetapkan juga GenerateMember salah untuk setiap kolom.

Tautan ke penyortir tampilan daftar kustom: http://www.codeproject.com/Articles/5332/ListView-Column-Sorter


7
2017-07-07 22:16



Saya memiliki masalah yang sama. Kemudian saya menemukannya sorter membuatnya sangat lambat. Jadikan sorter sebagai null

this.listViewAbnormalList.ListViewItemSorter = null;

lalu ketika klik sorter, hidupkan ListView_ColumnClick metode, membuatnya

 lv.ListViewItemSorter = new ListViewColumnSorter()

Akhirnya, setelah disortir, buatlah sorter batal lagi

 ((System.Windows.Forms.ListView)sender).Sort();
 lv.ListViewItemSorter = null;

0
2018-05-18 06:26



Buat semua Anda ListViewItems  PERTAMA, lalu tambahkan mereka ke ListView semua sekaligus.

Sebagai contoh:

    var theListView = new ListView();
    var items = new ListViewItem[ 53709 ];

    for ( int i = 0 ; i < items.Length; ++i )
    {
        items[ i ] = new ListViewItem( i.ToString() );
    }

    theListView.Items.AddRange( items );

-2
2018-01-27 22:30