ListView에 항목을 빠르게 추가하는 방법은 무엇입니까?


84

WinForms ListView에 수천 (예 : 53,709) 항목을 추가하고 있습니다.

시도 1 :13,870 ms

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

이것은 매우 나쁘게 실행됩니다. 분명한 첫 번째 해결 방법은 BeginUpdate/EndUpdate.

시도 2 :3,106 ms

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

이것은 더 좋지만 여전히 너무 느립니다. ListViewItems 생성과 ListViewItem 추가를 분리하여 실제 원인을 찾아 보겠습니다.

시도 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()

실제 병목 현상은 항목을 추가하는 것입니다. AddRange대신 a 로 변환 해 봅시다 .foreach

시도 4 : 2,182 ms

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

조금 더 나은. 병목 현상이 없는지 확인합시다.ToArray()

시도 5 : 2,132 ms

ListViewItem[] arr = items.ToArray();

stopwatch.Start();

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

stopwatch.Stop();

제한은 목록보기에 항목을 추가하는 것 같습니다. 의 다른 오버로드 AddRange일 수 있습니다. 여기서 ListView.ListViewItemCollection배열 이 아닌를 추가합니다.

시도 6 : 2,141 ms

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

글쎄요.

이제 스트레칭 할 시간입니다.

  • 1 단계 - "자동 너비"로 설정된 열이 없는지 확인합니다 .

    여기에 이미지 설명 입력

    검사

  • 2 단계 -ListView가 항목을 추가 할 때마다 항목을 정렬하지 않는지 확인합니다.

    여기에 이미지 설명 입력

    검사

  • 3 단계 -stackoverflow에 질문 :

    여기에 이미지 설명 입력

    검사

참고 : 분명히이 ListView는 가상 모드가 아닙니다. 가상 목록보기에 항목을 "추가"할 수 없거나 할 수 없기 때문입니다 (를 설정 VirtualListSize). 다행히도 내 질문은 가상 모드의 목록보기에 관한 것이 아닙니다.

목록보기에 항목을 추가하는 것이 너무 느려서 누락 된 것이 있습니까?


보너스 채터

Windows ListView 클래스가 더 잘할 수 있다는 것을 알고 있습니다.에서 코드를 작성할 수 있기 때문입니다 394 ms.

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

동등한 C # 코드와 비교할 때 1,349 ms:

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

훨씬 더 빠릅니다.

WinForms ListView 래퍼의 어떤 속성이 누락 되었습니까?


2
참고 : 체크 박스를 사용하는 경우 ListView에 추가하기 전에 체크 된 상태를 설정해야합니다. 체크 상태 초기화 blogs.msdn.com/b/hippietim/archive/2006/03/20/556007.aspx
Tim Schmelter

3
질문해야합니다. 왜 그렇게 많은 항목을 추가하고 있습니까?
OO

4
좋은 질문 이안. 주제에 대한이 블로그를 보셨습니까? virtualdub.org/blog/pivot/entry.php?id=273
Chris Shain

2
1,349 ms, 불가능합니다. 53709 개의 아이템으로 시도하는데 몇 분이 걸립니다. 항목이 너무 많은 목록보기를 사용하는 이유는 무엇입니까? , 실제로 사용할 수 없습니다. 속도를 높이기 위해 listBox 또는 comboBox를 사용할 수 있지만 미친 숫자입니다
Xilmiki

4
가상 목록보기를 사용하지 않는 이유는 무엇입니까? 좋아, 항목 데이터를 검색하는 방법을 프로그래밍해야하고 정렬, 필터링 등과 같은 다른 항목을 유지해야 할 수도 있지만 항목 수에 관계없이 목록보기를 즉시 채울 것입니다.
Casperah

답변:


22

목록보기의 소스 코드를 살펴본 결과 성능이 4 배 정도 느려질 수있는 몇 가지 사항을 발견했습니다.

ListView.cs에서를 ListViewItemsCollection.AddRange호출 ListViewNativeItemCollection.AddRange하여 감사를 시작했습니다.

ListViewNativeItemCollection.AddRange(줄에서 : 18120) 값의 전체 컬렉션을 통해 두 개의 패스를 가지고, 하나는 모두가 그 후 '복원'에 대한 항목이 다른 선택 수집 InsertItems(그들이 모두에 대한 검사를 지키고있는 호출을owner.IsHandleCreated 소유자는 ListView) BeginUpdate.

ListView.InsertItems(줄에서 : 12952), 첫 번째 호출은 전체 목록의 또 다른 트래버스를 가지고 있고 ArrayList.AddRange가 호출되고 (아마도 거기에 또 다른 패스) 그 후 또 다른 패스가 있습니다. 선도

ListView.InsertItems(줄에서 : 12952), 두 번째 호출 (을 통해 EndUpdate) HashTable, a에 추가되는 다른 패스를 통해 a Debug.Assert(!listItemsTable.ContainsKey(ItemId))는 디버그 모드에서 속도를 더 느리게합니다. 핸들이 생성되지 않은 경우, 그것은에 항목을 추가 ArrayList, listItemsArray하지만 if (IsHandleCreated)다음 호출,

ListView.InsertItemsNative(줄에서 : 3848) 실제로 네이티브 목록보기에 추가되는 목록을 최종 통과합니다. a Debug.Assert(this.Items.Contains(li)는 추가로 디버그 모드에서 성능을 저하시킵니다.

따라서 .net 컨트롤의 전체 항목 목록을 통해 항목을 실제로 네이티브 목록보기에 삽입하기 전에 많은 추가 패스가 있습니다. 일부 패스는 생성중인 핸들에 대한 검사로 보호되므로 핸들이 생성되기 전에 항목을 추가 할 수 있으면 시간을 절약 할 수 있습니다. 이 OnHandleCreated메서드는 모든 추가 소란없이 직접 listItemsArray및 호출을 받습니다 InsertItemsNative.

참조 소스ListView코드를 직접 읽고 살펴볼 수 있습니다. 제가 뭔가 놓친 것일 수도 있습니다.

MSDN 매거진의 2006 년 3 월 호에서 라는 글이 있었다 Winning Forms: Practical Tips for Boosting The Performance of Windows Forms Apps.

이 기사에는 무엇보다도 ListView의 성능을 개선하기위한 팁이 포함되어 있습니다. 핸들이 생성되기 전에 항목을 추가하는 것이 더 빠르지 만 컨트롤이 렌더링 될 때 대가를 지불한다는 것을 나타내는 것 같습니다. 주석에 언급 된 렌더링 최적화를 적용하고 핸들이 생성되기 전에 항목을 추가하면 두 가지 장점을 모두 얻을 수 있습니다.

편집 : 다양한 방법으로이 가설을 테스트했으며 핸들을 만들기 전에 항목을 추가하는 것이 훨씬 빠르지 만 핸들을 만들 때 기하 급수적으로 느립니다. 나는 핸들을 생성하기 위해 그것을 속이려고 노력한 다음 모든 추가 패스를 거치지 않고 어떻게 든 InsertItemsNative를 호출하도록 시도했지만 슬프게도 좌절했습니다. 내가 가능하다고 생각할 수있는 유일한 방법은 C ++ 프로젝트에서 Win32 ListView를 만들고 항목으로 채우고 후킹을 사용하여 핸들을 만들 때 ListView에서 보낸 CreateWindow 메시지를 캡처하고 win32에 대한 참조를 다시 전달하는 것입니다. 새 창 대신 ListView ..하지만 측면이 어떤 영향을 미치는지 아는 사람은 ... Win32 전문가는 그 미친 아이디어에 대해 말해야 할 것입니다. :)


10

이 코드를 사용했습니다.

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

나는 또한 설정했다 GenerateMember각 열에 false로 .

사용자 지정 목록보기 분류기 링크 : http://www.codeproject.com/Articles/5332/ListView-Column-Sorter


4
예, 항목을 추가하는 동안 분류기를 활성화하는 것은 *** 엄청나게 *** 느립니다. 하지만이 경우에는 분류 기가 없습니다. 그러나 이것은 .NET 목록보기 가 항목이 끝날 때가 아니라 추가 될 때마다 정렬을 호출한다는 사실을 인식하지 못하는 사람들에게 유용한 첫 번째 단계입니다 .
Ian Boyd

0

나도 같은 문제를 안고있어. 그런 다음 sorter너무 느리게 만든다는 것을 알았습니다 . 분류기를 null로 만듭니다.

this.listViewAbnormalList.ListViewItemSorter = null;

그런 다음 정렬기를 클릭하면 ListView_ColumnClick메소드에서

 lv.ListViewItemSorter = new ListViewColumnSorter()

드디어 정렬이 끝나면 sorter다시 null로

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

Slav2는 . 나는 또한 내 원래 질문에서 제안했습니다.
Ian Boyd

예, 위의 답변과 동일합니다. :)
Batur

-1

ListView 상자 추가

이것은 열로 구성된 목록 상자에 항목을 추가하기 위해 구성 할 수있는 간단한 코드입니다. 첫 번째 열은 항목이고 두 번째 열은 가격입니다. 아래 코드는 첫 번째 열에 항목 Cinnamon을 인쇄하고 두 번째 열에 0.50을 인쇄합니다.

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

인스턴스화가 필요하지 않습니다.


이것은 하나의 항목을 추가 하는 쉬운 방법입니다. 그러나 75,000 개 항목을 추가 하는 빠른 방법 은 아닙니다 .
Ian Boyd

나는 동의한다. ListView 클래스를 인스턴스화하여 여러 결과를 추가하는 구현을 게시 할 것입니다.
Demetre Phipps

-2

모든 ListViewItems FIRST 를 만든 다음 한 번에 ListView에 추가하십시오 .

예를 들면 :

    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 );
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.