speed up listbox refresh

Is the best way to reduce the percieved load time of a list box to

  • set VISIBLE=false
  • load the data
  • set VISIBLE=true

and when you do LB.INVALIDATECELL(-1,-1)
does that attempt to refresh ALL the cells, or just all the VISIBLE one?

Just tested : only visible rows and columns are concerned in CellTextPaint.

I would tend to prefer doing stuff invisible. It usually greatly speed up things anyway, and instant display is great for the user.

On Mac, I remember seeing a declare that freezes the display of the listbox posted by Norman.

Fill the listbox with rows.
.drawinto a picture
put the picture in a canvas over /instead of the listbox
Fill the listbox fully
Swap the canvas and listbox

Or:
Start with rows in the listbox at design time
Instead of adding rows at runtime , change the text of row at runtime
That way you arent adding rows and seeing things jump about, the changes you are making mostly dont affect the visible area at all.

[quote=328349:@Dave S]Is the best way to reduce the percieved load time of a list box to

  • set VISIBLE=false
  • load the data
  • set VISIBLE=true

[/quote]

That was the standard way to do it years ago… but IIRC on some platforms the listbox actually disappears when loading the data, so that fell out of favor.

Just visible.

  • karen

Something to keep in mind… adding rows of data is very time consuming. If you are starting from a listbox that already has rows, don’t call DeleteAllRows and start over. Instead, reassign the rows that you already have and only add or remove rows at the end as needed.

my current plan is to create a custom listbox with a backing datastore that can be accessed randomly … Perhaps an array of DatabaseRecord objects , and add an external vertical scrollbar to load only what ever number of rows are actually visibile.
but that still needs some design, testing, research…

The built-in listbox is rather slow.

And if you create 10ks of DatabaseRecord objects this will be slow, too. Forget dictionaries for holding the selection. And for fun download the example for the canvas based piDog DataView and do a Cmd-A on 50.000 records: OutOfMemoryException.

All of this is doable but it will be a lot of work. A lot lot.

I think it is always very helpful to back up these statements with actual numbers. Not everyone might think 0.02 seconds for a thousand rows (just tested on my ancient Macbook) is “very time consuming”.

Here is my test code:

	dim start as Double=Microseconds
	for i as integer=1 to 1000
			Self.Listbox1.AddRow ""
	next
	dim ittook as Double=(Microseconds-start)/1000000
	MsgBox("Loading 1000 rows took "+ittook.ToText+" seconds.")

And that’s without any messing with the visibility. I never thought the XOJO Listbox was slow actually.

I am near finished a new project that uses a hierarchical listbox.
My stress test is a 12000 row file of nested data.
Takes about 7 seconds to fill and display completely, although most of the time it only shows 30 items and is easily fast enough.

Display the whole 12000 rows so that the user can scroll up and down the whole list: 7 seconds
Equivalent code from an older VB application that did the same work: 45 minutes.

Jeff: Linux / macOS / Windows ?

Loading (~800) folder’s titles into a 1 Column Listbox takes nearly forever on Windows, 1 s (?) on macOS
That is using a For Next loop / Me.AddRow f.Name(Idx).

Windows. It was faster on a Mac.

Almost certainly the slowdown is the use of folderitem.
Change the code to

for x = 1 to 800 listbox .addrow "Hello" next

and it will be instantaneous.

What you need is a faster way to get the folder names - dont use folderitem calls…

Dont do this: For i as Integer = 1 to dir.count because the .count takes a long time too.
Im sure there is a way to shell a DIR command , pipe it to a text file , and read the result… I’ll see if I can find it.

Got it:

dir > *.dcm /b   fileslist.txt

On Mac or Linux, something like this :

ls *.dcm > filelist.txt

When using AppleScript, it is also slow as hell to get a reference to the file one at a time.

Something like select all items, then deal with the reference (list) you get is faster (I forgot the real AppleScript code…).

I started to stop using AppleScript when it comes slo, slower, slowest… and use Xojo for simple (very simple) task. *

A run on Windows of my code is… slower than macOS. A solution there was given in this forum years ago.

On AppleSoft (for oldsters or nostalgics…) one had to write the programs with sub on top (low value line numbers) and the main part at the end of the program (around line numbers 65,000 or so). Because AppleSoft scan the program from the beginning when it encounters a goto or gosub or… (I start to forgot that too)…
Edit: something like a C program…

  • The last time I used AppleScript was to prototype a DrawPicture method (determine the image file orientation in a if … boolean… way).It tooks me seconds to fire the script editor and find the correct way, then I copy/paste the code in Xojo :wink:
    Doh ! I digress.

I beg to differ.

I have a custom combobox that uses a listbox for the dropdown list. Here is a gif showing the speed with 10k items. And I’m not even messing with the visible property. In my experience the fastest way to refresh a list box is to delete all rows and then adding all row. This is much faster than editing the text of existing rows.

Ok, to be honest I did use a little magic to achieve that. I have a 15 ms timer that loads the listbox rows in chunks of 100. The result is that the first 100 items load ‘instantly’. While the remaining invisible items continue to load the UI remains responsive and the user never know unless he tries to scroll to item 100k in the first few seconds.

Update: With 100k items…

Nice, but am I the only one that hates those control repaint flashes?

Annoying for sure. But I haven’t found another way to achieve this. Any suggestions?

A quick test reveals following:

MacBook Pro Core I5 8 Gb SSD, time in secs. Listbox with 4 columns, code:

[code]dim startTime as Double = Microseconds
dim i as integer
for i = 0 to 5000000
ListBox1.AddRow(Str(i),Str(i+1),Str(I+2),Str(I+3))
next
dim endTime as Double = (Microseconds-startTime)/1000000

MsgBox(“Loading " + str(i) + " rows takes " + endTime.ToText + " seconds” )[/code]

If you want, send me a test project with it in and I’ll take a look at removing the flashing.

[quote=328352:@Jeff Tullin]Fill the listbox with rows.
.drawinto a picture
put the picture in a canvas over /instead of the listbox
Fill the listbox fully
Swap the canvas and listbox[/quote]

@
Thanks for pointing that out. I was able to get rid of the flashing using Jeff’s advice.

@Alexander V

I tested Alexander’s code on Windows 10.

In general, Windows’ results were slower. Also, timings varied
a lot during tests, especially for the last test (4.5 M).
So I give only rounded values.

#rows Time (seconds)

100K 1,9
1M 18,6
2M 38,6
3M 58
4M 83
4.5M 79 - 172 (!)

As in Alexander’s case, the 5M test could not be finished,
as Windows displays an error and the program must be shut down.
In this case Windows Task Manager showed > 99% memory used.

Windows 10, v. 143.93
Intel Core i7 - 479, @ 3.6 Ghz
8 GB RAM
1 TB hd

Monitoring Task Manager, one can see that for the larger
number of rows memory usage plays an important role and the
time needed grows nonlinear.

My PC will be upgraded later this week to 16GB Memory; I
will test again then.