ListBox.selected problem

I have a listbox on a form next to a Page Control with datafields displayed to the right for the selected record.
The listbox contains 1 column with a member Name in the Lastname, Firstname format (as one field)
There is a textfield above the listbox where the user can start typing the first few characters of the last name of the desired member.
On the KeyUp event I reload the listbox from a RecordSet built from a select:

sFilter = sStart + "%"
sSQL = "SELECT * FROM Members WHERE LastName LIKE '" + sFilter + "' ORDER By LastName"

Where sStart is the field containing the characters typed in by the user, e.g. “plu” would create an sFilter of “plu%”

When the user double-clicks a row in the listbox, the member clicked is displayed in the data fields. The code in the double-click event of the listbox is:

dim iRow     As Integer
dim iRecID    As Integer

iRow   = Me.ListIndex
iRecID = Me.RowTag(iRow)

GetMemberObject(iRecID)

LoadRS("ALL")
PopulateMembersLB
me.selected(iRow) = true 
tfLastNameSearch.Text = ""

return

This all works perfectly but I included it for context.

After this double-click event is processed and the selected record displayed, I clear the textfield where the 1st characters of last name are typed.
Reload the listbox with ALL records again.
But now the problem. I want the row the user double-clicked to be selected and within the view of the displayed listbox.

The 1st problem is the statement lbMembers.selected(iRow) = true does not cause the clicked row to be highlighted (selected?)
The 2nd problem is how do I cause the listbox to scroll to the selected record - that is after I can get it to be selected?

thanks guys…

I know what’s wrong, the listbox gets reloaded so the row numbers are different. I need to work from the iRecID field instead.

But I still have the problem of scrolling to the selected record.

You need to use

ListBox.ScrollPosition = therownumberwhichisselected

I am having a similar problem and would like some guidance.

I have a listbox that is populated in a separate method from a database. If I change any data items (using the listbox editing) I update the record in the database and reload the listbox. I then want the originally selected (and modified) record line in the listbox to be selected and within view.

I store the database table key in the row tag property, and the reload of the table data updates the listbox lines accordingly.

I then set up a loop from 0 to listcount-1 and check the row tag value at each iteration. If it matches the changed record’s key then I set the listindex to the loop counter and exit.

Code snippet here:

[code]Dim row As Integer = -1

For lp As Integer = 0 To lbA.ListCount - 1
If lbA.RowTag(lp) = ac.mKey Then
row = lp
Exit For
End If
Next

If row <> -1 Then
lbA.ScrollPosition = row
lbA.ListIndex = row
End If
[/code]
My issue is that this just has no effect. I have tried it with commenting out both the scrollpostion and listindex lines individually.

I have traced the program under debug and gone through the whole loop process and viewing the listbox in the debugger. This shows that the listindex is being set to the correct value - but - when the listbox is actually displayed the selected row is not being shown.

This is particularly annoying when the edited field is part of the order by clause of the sql and its position in the listbox now changes.

Some advice would be welcomed.

Thanks all.

MacOS 10.12.6
Xojo 2017R2.1

lbA.Selected( row ) = TRUE

Thank you, Markus, but I have already tried that. Did not work!

Works fine her.

In YOUR code you do not actually set a row.

If row <> -1 Then lbA.ScrollPosition = row <- you scroll to the row lbA.ListIndex = row <- you say which row is the selected one (ListIndex is a property of ListBox) lbA.Selected( row ) = TRUE <- this line actually sets the row to selected, so without it your code won't work the way you think it does End If

P.S. I recommend you make a minimal example, and at the very least explain which events you are using.

P.P.S.

[quote]ListBox.Selected
Property (As Boolean)

aListBox.Selected(Row as Integer) = newBooleanValue

or

BooleanValue = aListBox.Selected(Row as Integer)

Gets or sets the selection status of the passed Row.

Notes

Selected is True if the row passed is selected. This property can be used to determine if the row is selected and to select the row. For example,

Listbox1.Selected(1) = True  //selects the second item in the first column.

[code]If row <> -1 Then
lbA.ScrollPosition = row ← you scroll to the row

lbA.ListIndex = row ← you say which row is the selected one (ListIndex is a property of ListBox)

No. Here I set the listbox index to the one I want.

lbA.Selected( row ) = TRUE ← this line actually sets the row to selected, so without it your code won’t work the way you think it does
End If[/code]
Normally, setting the listbox list index to a value selects it and highlights it. Unfortunately this is not happening for me.

I can’t create a simple project as these lines are within a class I have created that inherits from listbox. I just don’t understand why any of the normal things work.

I think it has to do with what happens internally to listbox. My class reacts to a click on a cell, opens the edit and allows an edit. In the subsequent Cell Action method I RaiseEvent for my child listbox to react with the program. My issue is within this raised event in the child where I am attempting to highlight the changed row. I think something else must happen in the base class after the CellAction has been called. The ListIndex and Selected properties have to be recharged within the parent Listbox - I don’t do anything with them after the CellAction call.

If you select a row then the number of the row is saved in the ListBox.ListIndex property.

I don’t see ANYWHERE in the documentation that setting the ListIndex will select the row.

I think you were relying on some undocumented behavior.

On the contrary: selecting a row in code is done by passing the row number to ListBox.Selected and setting it to True.

If you think I’m wrong then I’m happy to be corrected - just show me in the documentation where it says setting the ListIndex selects the row.

P.S. Common logical fallacy: a lot of people think that if A then B also means if B then A but that is not the case.

The documentation indicates that ListIndex is a writable property. Also from the doc page “The selected item number. The index is zero-based.” So, with the property being writable and this description, only a small leap of ingenuity needs to be made. I tend to use Listbox.ListIndex = x to set a listbox selection, and haven’t had an issue with it (yet!)

By the way, you can see in the code example here: http://documentation.xojo.com/index.php/ListBox.Selected the use of setting ListIndex. It is commented as “deselecting all rows” by setting it to -1.

And here, http://documentation.xojo.com/index.php/ListBox.AddRow, uses ListIndex to select the newly added row to automatically scroll to it.

It’s in the documentation, just not in big bold

This can happen if you’re changing the selection during a Listbox event. A quick’n’dirty fix is to use a timer to make a delay, but this can be the start of unpredictable behaviors.

Then I stand corrected :wink:

It could be made more obvious on the Listbox.ListIndex page though!

[quote=352377:@Markus Winter]If you select a row then the number of the row is saved in the ListBox.ListIndex property.
[/quote]

Markus,
After all these years I a bit surprised at your post and understanding on how listboxes work.

What you wrote is a bit misleading… That is only true when you only allow a single selection in the listbox.

But a listbox can be set to have more than one row selected at a time. In fact it is for that usage that there IS a selected method. For single selection listboxes, ListIndex alone suffices and is what should be used.

For multi-selection listboxes the listIndex is set the the first (lowest row index) selected row in the listbox, and changing it will deselect all previously selected rows and select the one specified (unless it is set to -1. In that case no row is selected)

  • Karen

This, I think, is where my problem lies.

As I am attempting to set the list index to a new one within an instance of a sub-classed ListBox of my own then the root Listbox class is obviously still doing something after the end of my raised event.

I think I will explore the Timer suggestion.

Markus, thanks for the help and suggestions so far but I have always used the lb.ListIndex = myrow to set the selection. It just isn’t working in this instance!

Over the years I have created many sub-classed list boxes to do various common tasks (see my website Simcar Software) and I have noticed this behaviour before. I have never found a solution and found workarounds. However, in my current project I really need to set the selected rows correctly and I have been agonising over this for a long time!

[quote=352432:@Simon Berridge] This can happen if you’re changing the selection during a Listbox event. A quick’n’dirty fix is to use a timer to make a delay, but this can be the start of unpredictable behaviors.
This, I think, is where my problem lies.[/quote]
That can only happen if you as a developer already use a timer (might be combined with a thread) to load data into the listbox. “changing the selection during a Listbox event” is not possible without a timer. Events are fully processed before any other function or method of the application is executed.

Eli, this is not the case. I don’t use a timer at all in my listbox class. I have now tried the following:

I created a new timer class called slbResetRow.

Constructor:

Public Sub Constructor() Period = 500 ' 1/2 second Mode = Timer.ModeOff End Sub
Action:

Sub Action() Handles Action System.DebugLog "In the timer Action." LBox.ListIndex = RowID System.DebugLog "Executed the timer." End Sub
Properties:

Public Property LBox as Listbox Public Property RowID as Integer

In my raised event of the listbox (sub-classed from ListBox) on my window I have the following code:

For lp As Integer = 0 To me.ListCount - 1 If Me.RowTag(lp) = ac.mKey Then System.DebugLog "Setting up timer." System.DebugLog "Current List Index : " + Str(me.listindex) System.DebugLog "New List Index : " + Str(lp) Dim tmr As New slbResetRow tmr.LBox = Me tmr.RowID = lp tmr.Mode = timer.ModeSingle tmr.Enabled = True ' Me.listindex = lp ' Me.Selected(lp) = True Exit For End If Next

Running my app the listbox is not updated. Looking at the debug log after execution I can see that the Action event of the timer is never called! I get the first three lines (“Setting up timer”, "Current List Index : " and "New List Index : ") but the log never shows “In the timer Action.” or “Executed the timer.”.

I am now seriously confused!

[quote=352444:@Simon Berridge]@Eli Ott … already use a timer …
Eli, this is not the case. I don’t use a timer at all in my listbox class. I have now tried the following:
I created a new timer class called slbResetRow.
[/quote]
What I meant is, if you did not use a timer, then it is not possible that an event (like click or so) interrupts another event, as you suspected further above. There is no need to introduce a timer.

I have the same setup as you both and never did encounter that problem. After reloading data to the listbox, just walk over it and use ListIndex to select the correct row (by comparing to rowId in RowTag). Do not forget to first empty the listbox (this happened to me once).