Làm cách nào để tăng tốc độ thêm các mục vào ListView?


83

tôi đang thêm một vài nghìn (ví dụ: 53,709) mục vào WinForms ListView.

Nỗ lực 1 :13,870 ms

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

Điều này chạy rất tệ. Cách khắc phục rõ ràng đầu tiên là gọi điện BeginUpdate/EndUpdate.

Nỗ lực 2 :3,106 ms

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

Điều này tốt hơn, nhưng vẫn là một thứ tự cường độ quá chậm. Hãy tách việc tạo ListViewItems khỏi việc thêm ListViewItems, để chúng tôi tìm ra thủ phạm thực sự:

Nỗ lực 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()

Điểm nghẽn thực sự là thêm các mặt hàng. Chúng ta hãy cố gắng chuyển đổi nó sang AddRangechứ không phải là mộtforeach

Nỗ lực 4: 2,182 ms

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

Tốt hơn một chút. Hãy chắc chắn rằng nút thắt cổ chai không nằm trongToArray()

Nỗ lực 5: 2,132 ms

ListViewItem[] arr = items.ToArray();

stopwatch.Start();

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

stopwatch.Stop();

Hạn chế dường như là thêm các mục vào chế độ xem danh sách. Có thể quá tải khác AddRange, nơi chúng tôi thêm một ListView.ListViewItemCollectionthay vì một mảng

Nỗ lực 6: 2,141 ms

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

Điều đó không tốt hơn.

Bây giờ là lúc để kéo dài:

  • Bước 1 - đảm bảo không có cột nào được đặt thành "chiều rộng tự động" :

    nhập mô tả hình ảnh ở đây

    Kiểm tra

  • Bước 2 - đảm bảo rằng ListView không cố gắng sắp xếp các mục mỗi khi tôi thêm một mục:

    nhập mô tả hình ảnh ở đây

    Kiểm tra

  • Bước 3 - Hỏi stackoverflow:

    nhập mô tả hình ảnh ở đây

    Kiểm tra

Lưu ý: Rõ ràng ListView này không ở chế độ ảo; vì bạn không / không thể "thêm" các mục vào chế độ xem danh sách ảo (bạn đặt VirtualListSize). May mắn thay câu hỏi của tôi không phải về chế độ xem danh sách ở chế độ ảo.

Có điều gì tôi còn thiếu có thể giải thích cho việc thêm các mục vào listview quá chậm không?


Bonus Chatter

tôi biết lớp ListView của Windows có thể làm tốt hơn, vì tôi có thể viết mã thực hiện nó trong 394 ms:

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

mà khi so sánh với mã C # tương đương 1,349 ms:

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

là một thứ tự của độ lớn nhanh hơn.

Tôi đang thiếu thuộc tính nào của trình bao bọc WinForms ListView?


2
Lưu ý bên: Nếu sử dụng Hộp kiểm, bạn nên đặt trạng thái đã chọn trước khi thêm vào ListView. Khởi tạo các trạng thái đã kiểm tra blog.msdn.com/b/hippietim/archive/2006/03/20/556007.aspx
Tim Schmelter

3
Tôi phải hỏi: tại sao bạn lại thêm nhiều mục như vậy?
OO

4
Ian câu hỏi tuyệt vời. Bạn đã xem blog về chủ đề này chưa? virtualdub.org/blog/pivot/entry.php?id=273
Chris Shain

2
1,349 ms, không thể. Tôi thử với 53709 mục, mất vài phút. Tại sao lại sử dụng chế độ xem danh sách với rất nhiều mục? , không thực sự sử dụng được. Bạn có thể sử dụng ListBox hoặc ComboBox để tăng tốc độ nhưng là một con số điên rồ
Xilmiki

4
Tại sao không sử dụng Chế độ xem danh sách ảo? Được rồi, bạn cần lập trình cách truy xuất dữ liệu mục và bạn có thể phải duy trì những thứ khác như sắp xếp, lọc, v.v. nhưng bạn sẽ lấp đầy chế độ xem danh sách ngay lập tức cho dù có bao nhiêu mục.
Casperah

Câu trả lời:


22

Tôi đã xem mã nguồn cho chế độ xem danh sách và tôi nhận thấy một số điều có thể khiến hiệu suất chậm lại theo hệ số 4 hoặc như vậy mà bạn đang thấy:

trong ListView.cs, ListViewItemsCollection.AddRangecác cuộc gọi ListViewNativeItemCollection.AddRange, đó là nơi tôi bắt đầu kiểm tra

ListViewNativeItemCollection.AddRange(từ dòng: 18120) có hai lần đi qua toàn bộ tập hợp các giá trị, một để thu thập tất cả các mục đã kiểm tra, một để 'khôi phục' chúng sau khi InsertItemsđược gọi (cả hai đều được bảo vệ bởi kiểm tra owner.IsHandleCreated, chủ sở hữu ListView) sau đó gọi BeginUpdate.

ListView.InsertItems(từ dòng: 12952), lần gọi đầu tiên, có một đường đi ngang khác của toàn bộ danh sách, sau đó ArrayList.AddRange được gọi (có thể là một đường chuyền khác ở đó) rồi một đường chuyền khác sau đó. Dẫn tới

ListView.InsertItems(từ dòng: 12952), cuộc gọi thứ hai (qua EndUpdate) một cuộc gọi khác đi qua nơi chúng được thêm vào a HashTablevà a Debug.Assert(!listItemsTable.ContainsKey(ItemId))sẽ làm chậm nó hơn nữa trong chế độ gỡ lỗi. Nếu xử lý không được tạo, nó sẽ thêm các mục vào một ArrayList, listItemsArraynhưng if (IsHandleCreated), sau đó nó gọi

ListView.InsertItemsNative(từ dòng: 3848) cuối cùng chuyển qua danh sách nơi nó thực sự được thêm vào chế độ xem danh sách gốc. a Debug.Assert(this.Items.Contains(li)cũng sẽ làm chậm hiệu suất trong chế độ gỡ lỗi.

Vì vậy, có RẤT NHIỀU lần đi qua toàn bộ danh sách các mục trong điều khiển .net trước khi nó thực sự chèn các mục vào chế độ xem danh sách gốc. Một số đường chuyền được bảo vệ bằng các kiểm tra đối với Tay cầm đang được tạo, vì vậy nếu bạn có thể thêm các mục trước khi tay cầm được tạo, điều đó có thể giúp bạn tiết kiệm một chút thời gian. Các OnHandleCreatedphương pháp lấy listItemsArrayvà gọi InsertItemsNativetrực tiếp mà không cần tất cả các fuss thêm.

Bạn có thể tự đọc ListViewmã trong nguồn tham khảo và xem qua, có thể tôi đã bỏ sót điều gì đó.

Trong số tháng 3 năm 2006 của Tạp chí MSDN có một bài báo được gọi là Winning Forms: Practical Tips for Boosting The Performance of Windows Forms Apps.

Bài viết này chứa các mẹo để cải thiện hiệu suất của ListView, trong số những thứ khác. Nó dường như chỉ ra rằng việc thêm các mục trước khi xử lý được tạo sẽ nhanh hơn, nhưng bạn sẽ phải trả giá khi điều khiển được hiển thị. Có lẽ việc áp dụng các tối ưu hóa hiển thị được đề cập trong các nhận xét và thêm các mục trước khi tay cầm được tạo sẽ mang lại hiệu quả tốt nhất cho cả hai thế giới.

Chỉnh sửa: Đã kiểm tra giả thuyết này theo nhiều cách khác nhau và mặc dù việc thêm các mục trước khi tạo trình xử lý là cực nhanh, nhưng khi tạo xử lý sẽ chậm hơn theo cấp số nhân. Tôi đã cố gắng đánh lừa nó để tạo ra tay cầm, sau đó bằng cách nào đó khiến nó gọi InsertItemsNative mà không cần vượt qua tất cả các lần vượt qua, nhưng than ôi tôi đã bị cản trở. Điều duy nhất tôi có thể nghĩ là có thể, là tạo Win32 ListView của bạn trong một dự án c ++, nhồi nhét nó với các mục và sử dụng hooking để nắm bắt thông báo CreateWindow do ListView gửi khi tạo xử lý của nó và chuyển lại một tham chiếu đến win32 ListView thay vì một cửa sổ mới .. nhưng ai biết được điều gì ảnh hưởng đến điều đó sẽ có ... một chuyên gia Win32 sẽ cần phải lên tiếng về ý tưởng điên rồ đó :)


10

Tôi đã sử dụng mã này:

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();

Tôi cũng đã đặt thành GenerateMemberfalse cho mỗi cột.

Liên kết đến trình sắp xếp chế độ xem danh sách tùy chỉnh: http://www.codeproject.com/Articles/5332/ListView-Column-Sorter


4
Có, việc có một bộ phân loại đang hoạt động trong khi thêm các mục là *** cực kỳ chậm. Nhưng trong trường hợp này, tôi không có bộ phân loại. Nhưng đây là bước đầu tiên hữu ích cho những người có thể không nhận ra rằng chế độ xem danh sách .NET gọi sắp xếp mỗi khi một mục được thêm vào - thay vì khi kết thúc.
Ian Boyd

0

Tôi có cùng một vấn đề. Sau đó, tôi thấy nó sorterlàm cho nó quá chậm. Đặt bộ sắp xếp thành null

this.listViewAbnormalList.ListViewItemSorter = null;

sau đó khi nhấp vào sắp xếp, trên ListView_ColumnClickphương thức, hãy làm cho nó

 lv.ListViewItemSorter = new ListViewColumnSorter()

Cuối cùng, sau khi nó được sắp xếp, hãy đặt lại sortergiá trị rỗng

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

Slav2 đã gợi ý rằng . Mà tôi cũng đã đề xuất trong câu hỏi ban đầu của tôi.
Ian Boyd

Vâng, nó cũng giống như câu trả lời ở trên. :)
Batur

-1

Hộp ListView Thêm

Đây là mã đơn giản mà tôi có thể xây dựng để thêm các Mục vào một hộp danh sách bao gồm các cột. Cột đầu tiên là mặt hàng trong khi cột thứ hai là giá. Đoạn mã dưới đây in Mục Cinnamon trong cột đầu tiên và 0,50 trong cột thứ hai.

// How to add ItemName and Item Price
listItems.Items.Add("Cinnamon").SubItems.Add("0.50");

Không cần khởi tạo.


Đó là một cách dễ dàng để thêm một mục. Nhưng không phải là một cách nhanh chóng để thêm 75.000 mặt hàng.
Ian Boyd

Tôi đồng ý. Tôi sẽ đăng một triển khai để thêm nhiều kết quả với việc khởi tạo lớp ListView đó.
Demetre Phipps

-2

Tạo tất cả các ListViewItems của bạn ĐẦU TIÊN , sau đó thêm tất cả chúng vào ListView cùng một lúc.

Ví dụ:

    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 );
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.