views:

244

answers:

5

Okay, I have a TListBox that on occasion may be called upon to show 43,000 lines!

I know, this hardly ever makes any sense, but there it is.

Now here's the current problem:

Using the built-in Sort method, with its Compare callback function, takes nearly forever, like many minutes.

So I extract the strings out of the listbox into a plain old dynamic array of ShortStrintgs, do a QuickSort() on that, and that takes about three seconds. Whopee I think!

Doing a bit of thinking, I see that QuickSort is moving all those strings around, which there is no need for, so I cange the code to just move around pointers or indices to the strings, and voila, the sort is much faster again, takin under a second to sort 43,000 items. Big win, yes?

BUT, now if I do a LB.Items.Add() or LB.Items.Assign to move the sorted strings into the listbox, THAT takes like 30 seconds! Even with BEgin/EndUpdate happening. If I trace through the code I see a whole lot of stuff going on with delete() Insert() INsertObject() and Windows messages flying for no good reason.

A moment's though reveals that I HAVE all the strings in the LB.TStrings, I just need them shuffled around ala my QuickSorted() array. That should be trivial, just moving some pointers.

But I don't see any visible way to set the raw TStringList pointers. No, Exchange() is really really slow.

Any ideas how I can get to the TString string pointers? This should be trivial but I don't see it.

Thanks,

George

A: 

To disable updating the listbox control while reordering the strings, use BeginUpdate/EndUpdate:

ListBox.Items.BeginUpdate;
try
  // your sorting here...
finally
  ListBox.Items.EndUpdate;
end;

Edit: You could also try virtual style (Style = lbVirtual, set Count property and handle OnData event).

TOndrej
According to the OP, he's already done that and still has trouble with it.
Mason Wheeler
Did that, as I alluded to in the Q. Did not help much. Thanks anyway.
My apologies, I've overlooked that part. Edited the answer to also suggest virtual style.
TOndrej
+1 for suggesting lbVirtual then :)
Wouter van Nifterick
+5  A: 

All those messages are actually flying around for a very good reason. There's no magic "display whatever's in the list" function. It has to take the contents of the list, build a tree from it, one item at a time, and display whatever part of that tree happens to fall within the ClientRect of the visual control. Your external string list technique sounds like the fastest thing you're going to get.

If you want better performance, try looking at a faster list box. I've heard that Virtual TreeView is incredibly fast at adding large batches of new items, though I can't really recommend it since I haven't used it and tested it myself. But you might want to take a look and see if it suits your needs better than your existing setup.

Mason Wheeler
Why is TListbox building a three?
Vegar
Thanks but I'm already doing a BeginUpdate to squash all the display refreshing.And it's a TListBox, not a Tree, so VirtualTreeView doesn't fit (although I guess I could have a really FLAT tree :)Thanks.
A list box functions as "a really flat tree" under the hood, because its data nodes have links in two directions: down, to the next item, and right, to the sub-items (if any.)
Mason Wheeler
+1 for VirtualTreeView.
skamradt
+1  A: 

One other method that you can use (I've used it successfully myself) to help keep things speedy is to stop the application from redrawing the controls, copying the list to a second TStringList, sort that TStringList, and copy back. The key to stopping the redraw is a command as such:

SendMessage(Application.Handle, WM_SETREDRAW, 0, 0);

The first 0 tells the application to stop drawing to the window, so all of those messages get dropped immediately and the application can move much faster. When ready to redraw the screen, just swap the first 0 for a 1 and then call

RedrawWindow(Application.Handle, nil, 0, [Options])

to force an immediate redraw of everything.

Tom
+1  A: 

Try Virtual Treeview. I know it's not a listbox and it's a little bit complex than tlistbox but it can act like TListBox(or tlistview/ttreeview) and it's way faster than standart tlistbox/tlistview/ttreeview.(it can add 1000000 items in 125ms)

barism
+1 - You can easily configure a VirtualTreeView to look and behave exactly like a listbox to a user as well as eliminiate the need to "add" items to the virtuallistview since you have them already in a list, just set the rootnodecount to the count in your list and use the GetText function to retrieve from the list directly.
skamradt
+3  A: 

Have you tried setting TListBox.Style to lbVirtual? Then the listbox asks you for the data in the OnData() event

see http://www.delphi3000.com/articles/article_3761.asp?SK=

jasonpenny
+1 This is by far the best solution if you want to stick to the standard VCL controls. It's really fast, and in virtual mode it's a lot easier to keep your in-memory model and the GUI in sync.
Wouter van Nifterick