Updating the Right ListBox Row When Data Changes

Each row of my listbox has a rowtag containing the object which has all the data for this row. Call it a RowObject. The RowObject needs to sometimes update its row with new data as external data comes in.

How can the RowObject know which row to update?

I see either:

a) Everytime there is a sort or insert, we cycle through the entire listbox contents and update each and every RowObject with a new row number. Ouch.

b) Everytime an update is needed, search every row until you find this RowObject. Ouch.

Am I missing some fundamental way of updating a listbox with changing external data?

Yes. Just have each cell update itself by using the CellTextPaint, eg

Me.cell(row,targetColum) = myObject( me.RowTag(row) ).SomeStringProperty

Markus, he still needs to invalidate the row to show the update when it is made

Thanks Karen :wink:

There is no other way AFAIK because you need to invalidate the row or cell to make sure it displays the change.

That said, with reasonable # of rows searching for the row is usually fast enough. If No that do option a.

Markus’ suggestion could work if you also invalidate all cells or all cell in a column…

See what is fastest and do that.

  • Karen

Ah, that’s brilliant. Thanks @Markus Winter

I realize I was going about this wrong. I was having the object update the listbox row whenever data changed regardless of whether it’s in view.

What I should do instead is whenever a celltextpaint even occurs, grab the most recent data for that event and apply it to that cell.

@Karen Atkocius : I did a quick sample project and if I put this in the celltextpaint event, it seems to update the row without needing invalidate.

Function CellTextPaint(g As Graphics, row As Integer, column As Integer, x as Integer, y as Integer) As Boolean me.cell(row, column) = "Boo! New data!" return false End Function

[quote=165590:@Stephen Dodd]@Karen Atkocius : I did a quick sample project and if I put this in the celltextpaint event, it seems to update the row without needing invalidate.

[/quote]

DO NOT COUNT ON THAT. IT WILL NOT ALWAYS BE THE CASE!!!

To speed up listbox display, unless a cell is marked dirty by the framework it not redrawn. In this case you must be doing something which is dirtying it.

  • karen

To see what I mean download this project and take a look at it:

https://www.dropbox.com/s/4vdaxxr30juzwga/ListboxInvalidate.xojo_binary_project?dl=0

the thing is you want only the changed cells to actually redraw
so you need some mechanism for an object in a rowtag to tell the listbox at they very least that it changed so that one row can be redrawn

you can do it with an observer pattern that you can implement in one of many ways

you could do this with add handler - but you still have to hunt the visible rows for the one that raised the event the handler is attached to
and you do have to make sure you remove handlers

or you could use something more broad that is more like a notification center for your app

again you still need to locate the row that changed to make sure you invalidate just that row

Thanks Karen. That’s interesting.

The above example is an empty project with only that celltextpaint event and some initial different values assign to the listbox. Here it is: https://dl.dropboxusercontent.com/u/7273799/Assign%20values%20during%20ListBox.Celltextpaint.xojo_binary_project

Doesn’t the celltextpaint event only get called for dirty cells?

What I don’t understand is why call invalidate during CellTextPaint doesn’t cause an infinite loop.

@Norman Palardy If I set the value for the cell on every celltextpaint event, am I causing a lot more work for the listbox than if I smartly only set it if the data has changed?

possibly - basically you;'re already in a paint event (for some reason) and now you’re invalidating what you just painted
so you may paint 2 x as much

It does… Change your code in CellTextPaint to:

[code] me.cell(row, column) = “Boo!”

Static Count as Integer
Count = count + 1

If Count = 1000 then Break
[/code]

Run your project and wait about 20seconds …

@Karen Atkocius Well, my code didn’t break by adding the above. So I tried limiting my listbox to 1 row and adding a beep during CellTextPaint. It didn’t repeatedly beep at me which suggests that somehow it doesn’t loop during invalidate.

@Norman Palardy With the beep test above, it seems that invalidating doesn’t actually cause the routine to fire more than once. The next question is does changing the values during CellTextPaint cause extra work? I suspect not really as it has to do a paint event anyways.

The big problem though with relying on CellTextPaint to update your data is that it only fires when the user scrolls or similar. If he is just staring at the listbox the data won’t change. And if the data changes, we have to find the right row to update. Doing listbox.invalidate everytime data changes seems to work (in conjunction with CellTextPaint) but I’m not sure how expensive than is.

Here’s an example with the invalidate FYI: https://dl.dropboxusercontent.com/u/7273799/Assign%20values%20during%20ListBox.Celltextpaint2.xojo_binary_project

[quote=165616:@Stephen Dodd]@Karen Atkocius Well, my code didn’t break by adding the above. So I tried limiting my listbox to 1 row and adding a beep during CellTextPaint. It didn’t repeatedly beep at me which suggests that somehow it doesn’t loop during invalidate.

[/quote]

That code won’t break it BUT it will keep looping. My code proves it. When you add teh code does not you break into teh debugger if you wait long enough? I you have a slow machine change teh 1000 to 1000…

The comment out the assignment and run again and wait and you will see you don’t break into the debugger.

At this point I think you have enough info to understand what is going on so i will let you think about it.

  • Karen

I appreciate your help Karen. Strangely, if a single row listbox with invalidate in the celltextpaint and have it static count to 10, it never breaks into the debugger. Tried on Mavericks. Maybe Windows is different?

I’n on 10.9.5 and it DOES break into the debugger for me even with a single row… I don’t understand how you can see anything else!

  • Karen

Interesting. I guess it’s the difference between the renderer on 10.9 vs 10.10 or maybe even retina resolution (which I have). I think the important thing is it does infinite loop for you so some workaround is needed to prevent the loop.

Turns out using listbox.invalidate to cause listbox.CellTextPaint to fire to update your data doesn’t work on Windows.

Yes it does or my listbox subclass would not work on Windows…
http://katkosoft.xojonews.com/Mergable%20Listbox%20Page/MergeableCellListbox.html

  • karen

That looks like a pretty awesome listbox project you have! Nice.

Strange about the listbox.invalidate. The previous example
updates in real time on Mac and just sits there until scrolled on Windows even if you add the invalidate line to CellTextPaint.

But it’s no biggie. I’m not going to use listbox.invalidate. I think in the end the best solution is going to be to search my listbox for the appropriate row to update.