Moving multiple rows up and down programmatically in a listbox

I’d like to move multiple rows in a listbox up and down with buttons. I’m having trouble with multiple rows.

So, I found this snippet a while back for deleting multiple rows and it works great!

For X As Integer = SCRIPT_LISTBOX.ListCount-1 DownTo 0 
  If SCRIPT_LISTBOX.Selected(X) Then 
    SCRIPT_LISTBOX.RemoveRow(X) 
  End If 
Next X

So I figured I would start with this logic and modify it for moving rows and came up with this:

dim line as string
For X As Integer = SCRIPT_LISTBOX.ListCount-1 DownTo 0 
  If SCRIPT_LISTBOX.Selected(X) Then 
    line=SCRIPT_LISTBOX.SelectedRowValue
    SCRIPT_LISTBOX.RemoveRowAt(x)
    SCRIPT_LISTBOX.AddRowAt(x+1, line)
  End If 
Next X

While this will move all the selected rows down, they all end up with the same name as the highest row in the list.

I feel like I’m on the right track with this code, but I’ve been unable to figure out the naming problem. Also, I’d like all the rows to remain selected after the move.

Any ideas would be greatly appreciated!

By “moving down” you mean inserting blank ones at some point, correct?

No, just stopping when there are no more rows. The code I posted does this…it doesn’t create blank rows when it gets to the bottom…it’s just not handling the renaming correcty.

All you have to do is swap the text of each line.

Starting with this:
image

I used this code in a button: (Sorry - its API1, I don’t use API2 but it should be easy to convert)

for x as integer = listbox1.listcount-2 downto 0  //start at the second last item
  if listbox1.Selected(x) then
    dim tmp as string
    tmp = listbox1.list(x+1)  //record the line below

    listbox1.list(x+1) = listbox1.list(x)  //swap me and next line
    listbox1.list(x) = tmp   
    listbox1.selected(x) = false
  end if
  
next

And I get this:
image

Hey! Now we’re getting somewhere! Great that’s it’s in AP1, as I’m sticking to that for this project.

When multiple items get to the bottom and you try to move them again, they shift spots instead of doing nothing. In your example, if I selected 9 and 10 and then pressed the down button, 10 would swap places with 9. When only moving 1 item at the bottom, it stays put.

Well, you cannot move 10 down… where would it go?
If you want to add a rule to say ‘if the last (x) are selected, don’t move them’, then you will need to first work from 10 backwards, and unselect the last contiguous block

THEN do the move

If <lastrow>.selected then
dim x as integer = listbox1.listcount-1

while listbox1.selected(x)
listbox1.selected(x) = false
x = x -1
wend   //stops at first unselected item
end if

//then do the moving code

It would go nowhere, hence me mentioning it needs to stay put in my last post. :slight_smile:

Somethings off. Syntax error on this line.

If <lastrow>.selected then

That was just pseudo code rather than real code. I’m just typing straight into the forum

It would be more like

if listbox1.selected(listbox1.listcount-1) then

It would go nowhere, hence me mentioning it needs to stay put in my last post.

But not your first one. :slight_smile:
I think you have enough to work with now.

if you want an ‘up’ button, you will face the same challenges but in reverse.

Excellent code! And yes, I’ll be able to flip it for going up.

Now I just need to figure out how to keep all the rows selected once they move. That way, more presses of the down button keep those same selected rows moving down.

‘swap’ the selected property too

Not following you. Is this in relation to flipping the code for going up, or related to keeping the rows selected as the move?

If you want to keep the rows selected after moving (subject to the unselection of unmovable lumps), then use a tmpselected variable to swap the selected state of the rows,
in addition to the tmp variable to swap the text of the rows.

So this works pretty well! Couple of minor things I’d like to tweak, but the meat and potatoes of moving multiple rows and keeping them selected works.

if SCRIPT_LISTBOX.selected(SCRIPT_LISTBOX.listcount-1) then
  dim x as integer = SCRIPT_LISTBOX.listcount-1
  
  while SCRIPT_LISTBOX.selected(x)
    SCRIPT_LISTBOX.selected(x) = False
    x = x -1
  wend   //stops at first unselected item
end if

for x as integer = SCRIPT_LISTBOX.listcount-2 downto 0  //start at the second last item
  if SCRIPT_LISTBOX.Selected(x) then
    dim tmp as string
    dim tmpselected as Boolean
    tmp = SCRIPT_LISTBOX.list(x+1)  //record the line below
    tmpselected = SCRIPT_LISTBOX.Selected(x+1) = true 
    
    SCRIPT_LISTBOX.list(x+1) = SCRIPT_LISTBOX.list(x)  //swap me and next line
    SCRIPT_LISTBOX.list(x) = tmp   
    SCRIPT_LISTBOX.selected(x+1) = True
    SCRIPT_LISTBOX.selected(x) = False
    
    
  end if
  
next

However, converting this “Up” has been a disaster, despite me thinking I just had reverse a few + and - settings. Interesting, I had to go to -2 to get the row above, not -1 like would seem logical. It either blows up with out of bounds exceptions or does other odd behavior. This is merely one set of code I’ve tried.

if SCRIPT_LISTBOX.selected(SCRIPT_LISTBOX.listcount-1) then
  dim x as integer = SCRIPT_LISTBOX.listcount-1
  
  while SCRIPT_LISTBOX.selected(x)
    SCRIPT_LISTBOX.selected(x) = False
    x = x -1
  wend   //stops at first unselected item
end if

for x as integer = SCRIPT_LISTBOX.listcount-2 downto 0  //start at the second last item
  if SCRIPT_LISTBOX.Selected(x) then
    dim tmp as string
    dim tmpselected as Boolean
    tmp = SCRIPT_LISTBOX.list(x-2)  //record the line above?
    tmpselected = SCRIPT_LISTBOX.Selected(x-1) = true 
    
    SCRIPT_LISTBOX.list(x-2) = SCRIPT_LISTBOX.list(x)  //swap me and next line
    SCRIPT_LISTBOX.list(x) = tmp   
    SCRIPT_LISTBOX.selected(x-1) = True
    SCRIPT_LISTBOX.selected(x) = False
    
    
  end if
  
next

For the ‘up’, you would start with X at row 1, and swap with row x-1 then work upwards.
Using a normal loop, not a downto loop

eg for x = 1 to listbox1.listcount-1

A
B // 1 start here
C // 2 head this way
D // 3 if this is selected, swap with X-1

Hello
it also works without a loop

var s1,s2 as string
rem down - altpos globale Variable = listbox1.cellpressed row
s1=listbox1.CellTextAt(altpos,0)
s2=listbox1.CellTextAt(altpos,1)
if altpos < 9 then
listbox1.RemoveRowAt(altpos)
altpos = altpos+1
listbox1.AddRowAt(altpos,s1)
listbox1.CellTextAt(altpos,1)=s2
listbox1.RowSelectedAt(altpos)=true

end if

https://www.dropbox.com/s/iewz34tw4xu6duc/listbox-zahlen1bis10.xojo_binary_project?dl=1

If you want to move everything, not just the selected rows

Thanks Jeff! I’ve spent some time testing out different things based on what you’ve given me, and I’ve come up with code that works great for up and down. I added some Booleans to stop the rows dead in there tracks while having them remain selected. This also solved the problem of non-continuous rows only stopping at the bottom selected row, while the other non-continuous selected rows would keep coming down with every button click.

Here the code for both buttons.

Down:

UpFinished = False

if DownFinished = True then
  return
end

if SCRIPT_LISTBOX.selected(SCRIPT_LISTBOX.listcount-1) then
  dim x as integer = SCRIPT_LISTBOX.listcount-1
  
  while SCRIPT_LISTBOX.selected(x)
    while DownFinished = false
      
      x = x -1
      
      DownFinished = true
      return
    wend   
  wend   //stops at first unselected item
end

for x as integer = SCRIPT_LISTBOX.listcount-2 downto 0  //start at the second last item
  if SCRIPT_LISTBOX.Selected(x) then
    dim tmp as string
    tmp = SCRIPT_LISTBOX.list(x+1)  //record the line below
    SCRIPT_LISTBOX.list(x+1) = SCRIPT_LISTBOX.list(x)  //swap me and next line
    SCRIPT_LISTBOX.list(x) = tmp   
    SCRIPT_LISTBOX.selected(x+1) = True
    SCRIPT_LISTBOX.selected(x) = False
    
  end if
  
next

And Up:

DownFinished = False

if UpFinished = True then
  return
end

for x as integer = -1 to SCRIPT_LISTBOX.listcount-1
  if SCRIPT_LISTBOX.Selected(x) then
    if x < 1 then
      UpFinished=True
      return
    else
      dim tmp as string
      tmp = SCRIPT_LISTBOX.list(x-1)  //record the line above
      SCRIPT_LISTBOX.list(x-1) = SCRIPT_LISTBOX.list(x)  //swap me and next line up.
      SCRIPT_LISTBOX.list(x) = tmp   
      SCRIPT_LISTBOX.selected(x-1) = True
      SCRIPT_LISTBOX.selected(x) = False
      
    end if
  end
next

I’m not sure what this part is for.
there is no point starting at -1… there is no row -1
So SCRIPT_LISTBOX.Selected(x) can never be true when x is -1 (I’m surprised it runs, tbh)
so UpFinished can never be true in the ‘up’ section, and doesn’t appear to be used anyway.

Also, you set selected to be false after the swap, but earlier you said you wanted to preserve the selected state? That code will unselected everything as my original example did.


 dim tmp as string
 dim tmpsel as boolean
for x as integer = 1 to SCRIPT_LISTBOX.listcount-1
  if SCRIPT_LISTBOX.Selected(x) then
   
      tmp = SCRIPT_LISTBOX.list(x-1)  //record the line above
      tmpsel = SCRIPT_LISTBOX.selected(x-1)  //record the line above

//swap the text
      SCRIPT_LISTBOX.list(x-1) = SCRIPT_LISTBOX.list(x)  //swap me and next line up.
      SCRIPT_LISTBOX.list(x) = tmp  

//swap the selected attribute
      SCRIPT_LISTBOX.selected(x-1) = SCRIPT_LISTBOX.selected(x)
      SCRIPT_LISTBOX.selected(x) = tmpsel
  
  end
next

The answer to why it is set to -1 is because that’s the way that works. If I set it 1, then multiple selected rows keep pushing up past the top selected row when it reaches the top of the listbox.

As to why I’m setting

SCRIPT_LISTBOX.selected(x-1) = True
SCRIPT_LISTBOX.selected(x) = False

again, this is the way it works. If I don’t set the X row to false, then all rows start becoming selected as I move the group up.