While searching a method for a fast ListBox I found the ‘Data On Demand’ from Kem Tekinay.
The samples I downloaded are working in XOJO 2019r3.1, so i suppose it is still usable.
Before putting a lot of time in testing it, I would first like to know if this software is still available for purchase (license).
The downloaded version works in the XOJO IDE but it is not possible to build without the license.
he is still here and can answer your question :)[/quote]
Think I prefer to put all 10,000 rows into the listbox. It only takes a few seconds. Otherwise I’m just throwing away the advantages of a listbox: simple management and automatic, correctly proportioned, scrollbar. Further, the begin/end buttons on the keyboard (or whatever they’re called) work properly and for nothing.
Otherwise I have a scrollbar I have to manage, which will have an incoreectly-sized thumb if I’m not careful, and begin/end buttons too. I could probably do it, but I’m not sure I can be bothered.
We already see the irritating effects of it not being done properly: web sites where the thumb seems to indicate you’re near the top, but which suddenly loads more content and then the thumb shows you’re nowehere near the top - and no idea how far away it is, either.
Kem’s Data On Demand listbox works great and super fast, even when pulling the data from a remote database server. It also properly handles the scrollbar and thumb size/placement for you. Highly recommended.
I also really like the piDogSoftware DataView and it can do dynamic data sources as well as LOTS of other stuff you can’t easily do with a Xojo Listbox (or a subclass from it).
Why do I need a fast listbox :
It’s about a app that manage midi files. The midi events are a sequential list of data.
The data is read and managed by a DLL library and all the functions are declared as external methods in my XOJO app.
When loading a file all the data is read into a sequential eventlist by the dll.
I need to read and encode each line of data into the listbox. That takes a little time (1 to 20 sec), and depends on the number of midi events.
If this ‘Listbox load’ would only happen once after the file was loaded, it wouldn’t bother me.
But everytime I make a change /move/delete event in the list, this will also be changed in the eventlist maintained by the dll, and I need to reload the listbox after each action. This is necessary for keeping the sequence of the midi events.
Perhaps this explanation can help forum members to give a solution that fits best my needs. Data on demand / DataView / …
[quote=496211:@Volbragt Etienne]If this ‘Listbox load’ would only happen once after the file was loaded, it wouldn’t bother me.
But everytime I make a change /move/delete event in the list, this will also be changed in the eventlist maintained by the dll, and I need to reload the listbox after each action. This is necessary for keeping the sequence of the midi events.[/quote]
The purpose of @Kem’s Data on Demand, or the dynamic data source option in DataView is that you NEVER populate the listbox with actual contents. Instead, when a row needs to be drawn, the contents is obtained “on demand” using logic you provide. In your case that would be returning the appropriate line from your eventlist.
In other words, provided your dll has a way to give you a specific line number from your eventlist, you can make either of these solutions work very efficiently. I think more typical usage of @Kem’s Data on Demand is retrieving rows from a database, but the data can be any source.
In my case I might have 10k (for example) rows read from a database. In general I’ve got n rows. The number n is simple to obtain from the database, so then I could do AddRow n times to size the listbox correctly. Now I’ve got n empty rows, so the scroll bar is correct. Now I would need, inside the paint event, to read the right row from the database and paint the cells.
Trouble is, I’ve only got a CellTextPaint event, not a RowTextPaint event. So I’d have to read the required bits of the row in the first paint event that fired for that row and put that RowSet row into (say) the RowTag for the row and copy each bit into other cells as other CellPaint events fired. Hmm, that might even work.
This is a large part of what either solution does for you. You tell it the value of n (your total possible row count) and it handles the scrollbar and scrolling for you. Then as the listbox wants various rows to become visible, you supply the current data for the row it tells you it needs. You do need to put rowtags in all the rows. It can literally support millions of rows if your data source (e.g. database) can supply it.
I strongly suggest you try @Kem’s demo project. And the piDog DataView demo. If all you need is Data on Demand, you can’t go wrong with @Kem’s solution. The piDogSoftware DataView can do LOTS of stuff in addition to a dynamic data source. I suggest you check it out as well.
I’m trying to test the sprit of this idea by creating the required number of rows, with a class instance in the rowtag of each row. Amongst other things, the class contains an id so I can get the data for each row from a database. So now, I’m doing all the writing into the cells inthe CellPaint Event.
For a couple of the columns, I’m using g.drawpicture() to put a small image into a cell, but for most of the columns I’m just writing into them, doing such as me.CellvalueAt(row, col) = "a string of text". At the same time I’m writing into various columns’ celltags with values to be used for sorting. For example, there’s a column showing the date in human-readable form, but the sort value (seconds since the epoch) is stored in the celltag.
Now this is giving me trouble. It seems that the CellTextPaint event only fires for column zero; all other columns are blank. I checked in the debugger that the event doesn’t fire for other column values.
Is there some fundamental reason that prevents writing to cell value or cell tag, inside the paint event?
I have a vague recollection the standard listbox does not fire a paint event for a cell when there is no value in that cell. But I could mismember that, as I pretty always use piDog’s DataView now and Kem’s Data on Demand in a few places.
Others will have to chime in for a better answer; I just don’t use the standard listbox much at all anymore.
I found it easier to use a containercontrol with a listbox and a scrollbar inside.
the listbox always has the size of the visible rows+1
the scrollbar has the number of rows values.
each time you move the scrollbar, you only redraw the listbox with the visible rows
using offset and limit sql queries.
In the end I stuck with my CellTextPaint event, and moved the logic which fills the listbox into it. That meant I could leave the CellBackgroundPaint event alone; this one is concerned with my logic for selecting rows. In CellTextPaint I just had it so that when the event fires, it fills the whole row. This gave a problem for the two columns in which I draw a small icon; both icons appeared in column zero only
It seemed as if the event was firing for column zero but no others, so I added a space to every column 1 cell, thus causing the event to fire for column 1 also. Scroll speed with 10k rows appears unaffected with no lags or jerky behaviour.
Sorting I’m now having SQLite do, since you can’t sort a listbox that has partial content.
So at this point it’s looking good in a testbed I put together.
Assuming your listbox allows multiple selections, how do you deal with multiple selections that aren’t showing in the same listbox view? Are you replicating the standard selection extension behavior for Command-click and Shift-click?
Yes, and the reason is that I want to distinguish between the “main” selected row and others that might also be selected. So my CellClick handler does all that and stores the current state in a dictionary that can be looked at elsewhere in the app.
I just checked that this mechanism works in my test rig with selected rows that are widely separated and thus not visible at the same time and it does. Thanks for mentioning that very good point.