Desktop Listbox: Detect When All Rows Have Been Collapsed

Picture a listbox with hierarchal rows. There are 4 “top level” folders, each of which can contain an undefined number of children, which can be a mixture of regular rows or more folders. The depth of children is anywhere from 1 to 4, depending on the row that has been expanded.

So, something like this:

Category 1 Child 1 Grandchild 1 Grandchild 2 Child 2 Child 3 Category 2 Child 1 Child 2 Grandchild 1 Grandchild 2 Great Granchild 1 Category 3 Category 4 Category 5

I need to detect when all the top level categories have been collapsed. i.e., in the above example, if the user collapses Category 1 and then Category 2, everything is now collapsed and I need to take action. The collapseRow event on ListBox fires before the collapse has occurred, so the listCount still counts up the expanded children, grandchildren, etc. Similarly, the “Expanded” property for the row that was just collapsed is still set to true, until after the collapseRow event exits.

Short of a stupid workaround like setting up a timer on the window with a very short period that fires when any row is collapsed and looks to see if all the rows now have expanded = false, is there a way to detect that all the rows have been collapsed?

Please tell me I’m missing something simple…

Setting up a timer in code (not on the window) is the time-honored technique. You’re not missing anything (that I’m aware of). You want an event that occurs after the framework has completed its processing. Timer is the tool for the job.

xojo.Core.Timer.Calllater(0, addressof ) is the way I’d do it.

What is the time-honored period for said programmatically created timer? I know the resolution of Timer on Windows is not as tight as on Mac, but I need things to feel “instant” to my users while still allowing the framework enough time to update the listbox correctly.


Newfangled framework call. I like it. :slight_smile:

Thanks Tim and Wayne - both approaches work great. I went with Wayne’s.

Timer may be the way to go but I like to find non-Timer solutions when possible.

In CollapseRow you can tell all will be collapsed by checking that all root level rows (RowDepth(i)=0) are collapsed, save for i=row which is about to be collapsed.

You can then raise your AllRowsCollapsed event at this point, but if your processing relies on sub rows being removed and all Expanded(i)=false then you can set a flag which is detected in CellBackgroundPaint which fires after CollapseRow with the cleaned state.

[code]Private Property justCollapsedAll As Boolean = false //optional flag for CellBackgroundPaint

Sub Open()
me.Hierarchical = true
for i As integer = 1 to 5
me.AddFolder "Category " + Str(i)
End Sub

Sub ExpandRow(row As Integer)
me.AddFolder “sub”
End Sub

Sub CollapseRow(row As Integer)

dim allCollapsed As Boolean = true //assume true, find false

for i As integer = 0 to me.ListCount - 1
if me.RowDepth(i) = 0 and i <> row and me.Expanded(i) then
allCollapsed = false
exit for i

//handle event here
//if allCollapsed then RaiseEvent AllRowsCollapsed

//or set flag to raise in CellBackgroundPaint
justCollapsedAll = allCollapsed

End Sub

Function CellBackgroundPaint(g As Graphics, row As Integer, column As Integer) As Boolean
if justCollapsedAll then
justCollapsedAll = false
RaiseEvent AllRowsCollapsed
End Function[/code]

  1. The timer doesn’t have to wait for processing to finish. It won’t fire until the processing has finished, period.