tags:

views:

133

answers:

4

Hi,

I tried to use a TListView component to display rather large data lists (like 4000 rows large), and creating the list is incredibly slow - it takes something like 2-3 secs, which makes the UI all laggy and close to unusable.

I fill the TListView.Items inside a BeginUpdate/EndUpdate block, with only preallocated strings - I mean : I build a list of all strings to store (which takes no humanly noticeable time), then I put them in the TListView.

I wish to display the TListView's content in vsReport mode with several columns.

The code looks like this :

MyList.Items.BeginUpdate;
for i := 0 to MyCount - 1 do
begin
  ListItem := MyList.Items.Add;
  ListItem.Caption := StrCaptions[i];
  ListItem.SubItems.Add(StrSubItems1[i]);
  ListItem.SubItems.Add(StrSubItems2[i]);
end;
MyList.Items.EndUpdate;

Is there some other hack I missed in the TListView component's logic ? or should I just forget about using this component for performances ?

A: 

@4000 rows I get only ~700 ms (D2009) times. For more responsiveness you could separate to other thread or add dirty Application.ProcessMessages() into loop.

rows generated with this code in 16 ms:

  MyCount := 4000;

  dw := GetTickCount();
  for i := 0 to MyCount - 1 do begin
    StrCaptions.Add('caption'+IntToStr(i));
    StrSubItems1.Add('sub1'+IntToStr(i));
    StrSubItems2.Add('sub2'+IntToStr(i));
  end;
  ShowMessageFmt('%u ms', [GetTickCount() - dw]);

Printed with:

  MyList.Clear;

  dw := GetTickCount();
  MyList.Items.BeginUpdate;
  for i := 0 to MyCount - 1 do
  begin
    ListItem := MyList.Items.Add;
    ListItem.Caption := StrCaptions[i];
    ListItem.SubItems.Add(StrSubItems1[i]);
    ListItem.SubItems.Add(StrSubItems2[i]);
  end;
  MyList.Items.EndUpdate;
  ShowMessageFmt('%u ms', [GetTickCount() - dw]);

EDIT: I inserted Application.ProcessMessages() into print, but somewhy performance stays same

Im0rtality
I just tried this in D6 on a 6 year old P4, and it only took 600ms. Tried with SortType = stText, and using a random number for the caption.
Gerry
If you need to measure as short intervals as 16 ms, you cannot rely on `GetTickCount`. Use `QueryPerformanceCounter` and `QueryPerformanceFrequency` instead. Divide the counter values with the frequency to get the interval in seconds.
Andreas Rejbrand
Maybe even {function RDTSC:Int64; asm dw 310Fhend;} for lower overhead, but here milliseconds are enough, as LeGEC stated time in seconds.
Im0rtality
Did you consider that perhaps he has more columns than you? Also I would very strongly advise against using `Application.ProcessMessages`. The problem is that it creates the possibility that your application can be in the middle of a process which changes the state of certain objects, only to be interrupted by starting another process that may now interrogate objects in an _unstable state_.
Craig Young
Exactly for that reason one should disable controls before going into `for` and enable them after `try...finally`
Im0rtality
+4  A: 

You can try Virtual Treeview component. It says "Virtual Treeview is extremely fast. Adding one million nodes takes only 700 milliseconds"

SimaWB
I am trying to rewrite some UI code, which was written with TListViews, and can't handle the data size anymore. I already (successfully) turned some into TVirtualTrees - and the updating is instantaneous.Moving to TVirtualTrees is pretty time-consuming, though, and I was wondering if I could move the existing code quicker.
LeGEC
+3  A: 

Use separate structure for holding your data. Set OwnerData of TListView to True.

Andrei K.
+6  A: 

You can use listview in virtual mode. Have a look at the virtuallistview.dpr demo.

TOndrej