locked
Move up items in a listbox RRS feed

  • Question

  • Hi,

    I designed a VB project at work to help in sorting out tools for various machines.

    The feedback I got was that the software was good but there is one thing the users wanted me to change.

    I have some items in a listbox. The users would like to move the items up and down (reordering).

    At the moment the user can only move one item up or down at a time. They would like to move multiple items at one time.

    I tried my own code as below but it repeats the first items for all selected.

    If Me.ListBox5.SelectedIndex < 1 Then

    Exit Sub

    Else

    For Each i As Integer In Me.ListBox5.SelectedIndices

    Dim s As String = Me.ListBox5.SelectedItem()

    Me.ListBox5.Items.RemoveAt(i)

    Me.ListBox5.Items.Insert(i - 1, s)

    Me.ListBox5.SetSelected(i - 1, True)

    Next

    End If

    Can anyone help?
    thanks
    Mark
    Tuesday, July 8, 2008 2:40 PM

Answers

  •  I got inspired so I wrote you a tweaked version of a ListBox called EnhancedListBox. Use the EnhancedListBox instead of the normal ListBox when you design your form. (Add the code below to a new class and compile your code -> the EnhancedListBow will then appear in you ToolBox so you can use it when you design your form).
    This specialized version of the ListBox has two public methods called MoveSelectedUp and MoveSelectedDown. Link two buttons to these methods and the EnhancedListBox will do the rest. The selected items will stay selected after you have moved the items.

    Public Class EnhancedListBox  
        Inherits ListBox  
     
        Private DisableChangedEvents As Boolean 
     
        ' Move all selected items up one step. If the first item in the  
        ' list is selected no move can be done.  
        ' Moved items will preserve their selected state after the move.  
        '  
        Public Sub MoveSelectedUp()  
     
            If (Me.SelectedIndices.Count > 0) Then 
                If (Me.SelectedIndices(0) > 0) Then 
     
                    Dim selectedSnapShot(Me.SelectedIndices.Count - 1) As Integer 
                    Me.SelectedIndices.CopyTo(selectedSnapShot, 0)  
                    DisableChangedEvents = True 
     
                    For Each i As Integer In selectedSnapShot  
                        Dim o As Object = Me.Items(i)  
                        Me.Items.RemoveAt(i)  
                        Me.Items.Insert(i - 1, o)  
                        Me.SetSelected(i - 1, True)  
                    Next 
                    DisableChangedEvents = False 
     
                End If 
            End If 
        End Sub 
     
     
        ' Move all selected items down one step. If the last item in the   
        ' list is selected no move can be done.  
        ' Moved items will preserve their selected state after the move.  
        '  
        Public Sub MoveSelectedDown()  
     
            If (Me.SelectedIndices.Count > 0) Then 
                If (Me.SelectedIndices(Me.SelectedIndices.Count - 1) < (Me.Items.Count - 1)) Then 
     
                    Dim selectedSnapShot(Me.SelectedIndices.Count - 1) As Integer 
                    Me.SelectedIndices.CopyTo(selectedSnapShot, 0)  
                    DisableChangedEvents = True 
     
                    For p As Integer = selectedSnapShot.Length - 1 To 0 Step -1  
                        Dim i = selectedSnapShot(p)  
                        Dim o As Object = Me.Items(i)  
                        Me.Items.RemoveAt(i)  
                        Me.Items.Insert(i + 1, o)  
                        Me.SetSelected(i + 1, True)  
                    Next 
                    DisableChangedEvents = False 
     
                End If 
            End If 
        End Sub 
     
        ' We need to override the OnSelectedIndexChanged and OnSelectedValueChanged to   
        ' disable the events from firing when we're moving selected items up or down.  
        '  
        Protected Overrides Sub OnSelectedIndexChanged(ByVal e As System.EventArgs)  
            If (Not DisableChangedEvents) Then 
                MyBase.OnSelectedIndexChanged(e)  
            End If 
        End Sub 
     
        Protected Overrides Sub OnSelectedValueChanged(ByVal e As System.EventArgs)  
            If (Not DisableChangedEvents) Then 
                MyBase.OnSelectedValueChanged(e)  
            End If 
        End Sub 
    End Class 

    If you have any problems using the control please let me know.

    /Calle
    • Proposed as answer by Dig-Boy Thursday, July 10, 2008 12:21 AM
    • Marked as answer by Spaceman1972 Thursday, July 10, 2008 11:04 AM
    Wednesday, July 9, 2008 9:03 PM

All replies

  • Hi,

    Set the ListBox SelectionMode property to MultiExtended or leave it set in the Form_Load event as here.>>

    I can't work it out for MultiSimple at the moment.

    Delete at least the blue highlighted lines after you have tried this.

    You get strange things happening sometimes especially if you select item1 as well.

    Select 2 or more items by dragging your mouse while holding down the left button.

    I will see if I can sort the problem out.




    Regards,

    John
    ________________________________________________________________________

    Option Strict On

    Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    For num As Integer = 1 To 100
    ListBox5.Items.Add(num)
    Next

    ListBox5.SelectionMode = SelectionMode.MultiExtended


    End Sub

    Private Sub ListBox5_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ListBox5.SelectedIndexChanged

    Dim s As String
    For Each i As Integer In Me.ListBox5.SelectedIndices
    If ListBox5.Items(i).ToString = ListBox5.Items(0).ToString Then Continue For
    s = Me.ListBox5.SelectedItem.ToString
    Me.ListBox5.Items.RemoveAt(i)
    Me.ListBox5.Items.Insert(i - 1, s)
    Next

    End Sub

    End Class


    I have previously been, until recently, an MSP ( Microsoft Student Partner ).
    Wednesday, July 9, 2008 12:45 AM
  • Hi,

    Then I tried the mousedown event so when you finish selecting, click the right-mouse-button to rearrange.


    Regards,

    John
    _______________________________________________________________________________




    Option Strict On

    Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    For num As Integer = 1 To 100
    ListBox5.Items.Add(num)
    ListBox5.SelectionMode = SelectionMode.MultiExtended
    Next

    End Sub

    Private Sub ListBox5_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox5.MouseDown

    If e.Button = Windows.Forms.MouseButtons.Right Then
    Dim s As String
    For Each i As Integer In Me.ListBox5.SelectedIndices
    If ListBox5.Items(i).ToString = ListBox5.Items(0).ToString Then Continue For
    s = Me.ListBox5.SelectedItem.ToString
    Me.ListBox5.Items.RemoveAt(i)
    Me.ListBox5.Items.Insert(i - 1, s)
    Next
    End If

    End Sub

    End Class


    I have previously been, until recently, an MSP ( Microsoft Student Partner ).
    Wednesday, July 9, 2008 1:02 AM
  • Hi John,

    Thanks for your reply.

    Is there any chance of moving say 4 items at a time but keep them all selected.

    So every time you press the move up or down button all four items move up or down by one.

    thanks

    Mark

    Wednesday, July 9, 2008 10:16 AM
  •  I got inspired so I wrote you a tweaked version of a ListBox called EnhancedListBox. Use the EnhancedListBox instead of the normal ListBox when you design your form. (Add the code below to a new class and compile your code -> the EnhancedListBow will then appear in you ToolBox so you can use it when you design your form).
    This specialized version of the ListBox has two public methods called MoveSelectedUp and MoveSelectedDown. Link two buttons to these methods and the EnhancedListBox will do the rest. The selected items will stay selected after you have moved the items.

    Public Class EnhancedListBox  
        Inherits ListBox  
     
        Private DisableChangedEvents As Boolean 
     
        ' Move all selected items up one step. If the first item in the  
        ' list is selected no move can be done.  
        ' Moved items will preserve their selected state after the move.  
        '  
        Public Sub MoveSelectedUp()  
     
            If (Me.SelectedIndices.Count > 0) Then 
                If (Me.SelectedIndices(0) > 0) Then 
     
                    Dim selectedSnapShot(Me.SelectedIndices.Count - 1) As Integer 
                    Me.SelectedIndices.CopyTo(selectedSnapShot, 0)  
                    DisableChangedEvents = True 
     
                    For Each i As Integer In selectedSnapShot  
                        Dim o As Object = Me.Items(i)  
                        Me.Items.RemoveAt(i)  
                        Me.Items.Insert(i - 1, o)  
                        Me.SetSelected(i - 1, True)  
                    Next 
                    DisableChangedEvents = False 
     
                End If 
            End If 
        End Sub 
     
     
        ' Move all selected items down one step. If the last item in the   
        ' list is selected no move can be done.  
        ' Moved items will preserve their selected state after the move.  
        '  
        Public Sub MoveSelectedDown()  
     
            If (Me.SelectedIndices.Count > 0) Then 
                If (Me.SelectedIndices(Me.SelectedIndices.Count - 1) < (Me.Items.Count - 1)) Then 
     
                    Dim selectedSnapShot(Me.SelectedIndices.Count - 1) As Integer 
                    Me.SelectedIndices.CopyTo(selectedSnapShot, 0)  
                    DisableChangedEvents = True 
     
                    For p As Integer = selectedSnapShot.Length - 1 To 0 Step -1  
                        Dim i = selectedSnapShot(p)  
                        Dim o As Object = Me.Items(i)  
                        Me.Items.RemoveAt(i)  
                        Me.Items.Insert(i + 1, o)  
                        Me.SetSelected(i + 1, True)  
                    Next 
                    DisableChangedEvents = False 
     
                End If 
            End If 
        End Sub 
     
        ' We need to override the OnSelectedIndexChanged and OnSelectedValueChanged to   
        ' disable the events from firing when we're moving selected items up or down.  
        '  
        Protected Overrides Sub OnSelectedIndexChanged(ByVal e As System.EventArgs)  
            If (Not DisableChangedEvents) Then 
                MyBase.OnSelectedIndexChanged(e)  
            End If 
        End Sub 
     
        Protected Overrides Sub OnSelectedValueChanged(ByVal e As System.EventArgs)  
            If (Not DisableChangedEvents) Then 
                MyBase.OnSelectedValueChanged(e)  
            End If 
        End Sub 
    End Class 

    If you have any problems using the control please let me know.

    /Calle
    • Proposed as answer by Dig-Boy Thursday, July 10, 2008 12:21 AM
    • Marked as answer by Spaceman1972 Thursday, July 10, 2008 11:04 AM
    Wednesday, July 9, 2008 9:03 PM
  • I had a similar question myself and came to one solution similar to the one above. It is in C#, but hopefully you'll find it helpful. Good luck.

    http://www.sixthandclarkson.com/2007/07/29/move-items-up-and-down-in-a-multiselect-listbox-with-c/
    • Edited by nmadd Wednesday, July 9, 2008 9:28 PM spelling
    Wednesday, July 9, 2008 9:28 PM
  • Calle -- excellent solution.  I did something similar using the data gridview a little while back on this forum but I can't seem to find the thread now.  I used the KeyDown event to bypass the need for extra buttons.  If you add the following code to your class it will all be implemented internally...  you just need to let the user know to press Control + Up/Down to get the functionality.

     
         Protected Overrides Sub OnKeyDown(ByVal e As System.Windows.Forms.KeyEventArgs)     
            If e.Control Then    
                Select Case e.KeyCode     
                    Case Keys.Down     
                        MoveSelectedDown()     
                        e.Handled = True    
                        Return    
                    Case Keys.Up     
                        MoveSelectedUp()     
                        e.Handled = True    
                        Return    
                End Select    
            End If    
            MyBase.OnKeyDown(e)     
        End Sub    
     
     

    • Marked as answer by Spaceman1972 Thursday, July 10, 2008 11:03 AM
    • Unmarked as answer by Spaceman1972 Thursday, July 10, 2008 11:03 AM
    Thursday, July 10, 2008 12:25 AM
  • nmadd said:

    I had a similar question myself and came to one solution similar to the one above. It is in C#, but hopefully you'll find it helpful. Good luck.

    http://www.sixthandclarkson.com/2007/07/29/move-items-up-and-down-in-a-multiselect-listbox-with-c/



    Looks just the same nmadd, however, when you re-select the items that has been moved you will have multiple changed events that don't make sense, so I temporarily disabled them when moving.


    Dig-Boy, that's a nice twitch, I'm sure the owner of the thread likes the idea as well.

    /Calle

    Thursday, July 10, 2008 6:55 AM
  • Calle,

    Thanks for your answer.

    It worked great! Everyone is happy :-)

    thanks again
    Mark
    Thursday, July 10, 2008 11:05 AM
  • Hi,

    I thought I would add to the code from Calle slightly and add a default option.

    Now every time you drop an EnhancedListBox on the FORM from your ToolBox it can have the
    SelectionMode as MultiSimple or MultiExtended by default.

    You could of course add another CLASS

    where one is always  MultiSimple

    and the other always MultiExtended

    as below. :-)   ;-)

    In addition to that I've added a MouseRoutine so if you Right-Click it moves the items up,

    if you have a middle mouse button a Middle-Click moves the items down!! ;-)  :-)

    I have highlighted the additions to the code.>>




    Regards,

    John

    ______________________________________

    'Enhanced_Multi_Simple_ListBox
    Public Class Enhanced_MS_ListBox
        Inherits ListBox

    'Use this if you don't want a default.>>

    'Private _default_selection_Mode As SelectionMode = Windows.Forms.SelectionMode.None

    'then comment out the line below or delete it.

    Private _default_selection_Mode As SelectionMode = Windows.Forms.SelectionMode.MultiSimple

    Public Sub New()

    If Not Me._default_selection_Mode = Windows.Forms.SelectionMode.None Then
    Me.SelectionMode = Me._default_selection_Mode
    End If

    End Sub

        Private DisableChangedEvents As Boolean

        ' Move all selected items up one step. If the first item in the  
        ' list is selected no move can be done.  
        ' Moved items will preserve their selected state after the move.  
        '  
        Public Sub MoveSelectedUp()

            If (Me.SelectedIndices.Count > 0) Then
                If (Me.SelectedIndices(0) > 0) Then

                    Dim selectedSnapShot(Me.SelectedIndices.Count - 1) As Integer
                    Me.SelectedIndices.CopyTo(selectedSnapShot, 0)
                    DisableChangedEvents = True

                    For Each i As Integer In selectedSnapShot
                        Dim o As Object = Me.Items(i)
                        Me.Items.RemoveAt(i)
                        Me.Items.Insert(i - 1, o)
                        Me.SetSelected(i - 1, True)
                    Next
                    DisableChangedEvents = False

                End If
            End If
        End Sub


        ' Move all selected items down one step. If the last item in the   
        ' list is selected no move can be done.  
        ' Moved items will preserve their selected state after the move.  
        '  
        Public Sub MoveSelectedDown()

            If (Me.SelectedIndices.Count > 0) Then
                If (Me.SelectedIndices(Me.SelectedIndices.Count - 1) < (Me.Items.Count - 1)) Then

                    Dim selectedSnapShot(Me.SelectedIndices.Count - 1) As Integer
                    Me.SelectedIndices.CopyTo(selectedSnapShot, 0)
                    DisableChangedEvents = True

                    For p As Integer = selectedSnapShot.Length - 1 To 0 Step -1
                        Dim i = selectedSnapShot(p)
                        Dim o As Object = Me.Items(i)
                        Me.Items.RemoveAt(i)
                        Me.Items.Insert(i + 1, o)
                        Me.SetSelected(i + 1, True)
                    Next
                    DisableChangedEvents = False

                End If
            End If
        End Sub

        ' We need to override the OnSelectedIndexChanged and OnSelectedValueChanged to   
        ' disable the events from firing when we're moving selected items up or down.  
        '  
        Protected Overrides Sub OnSelectedIndexChanged(ByVal e As System.EventArgs)
            If (Not DisableChangedEvents) Then
                MyBase.OnSelectedIndexChanged(e)
            End If
        End Sub

        Protected Overrides Sub OnSelectedValueChanged(ByVal e As System.EventArgs)
            If (Not DisableChangedEvents) Then
                MyBase.OnSelectedValueChanged(e)
            End If
        End Sub

         Protected Overrides Sub OnKeyDown(ByVal e As System.Windows.Forms.KeyEventArgs)
            If e.Control Then
                Select Case e.KeyCode
                    Case Keys.Down
                        MoveSelectedDown()
                        e.Handled = True
                        Return
                    Case Keys.Up
                        MoveSelectedUp()
                        e.Handled = True
                        Return
                End Select
            End If
            MyBase.OnKeyDown(e)
        End Sub

    Protected Shadows Sub MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown

    If e.Button = Windows.Forms.MouseButtons.Middle Then MoveSelectedDown()
    If e.Button = Windows.Forms.MouseButtons.Right Then MoveSelectedUp()

    End Sub

    End Class

    I have previously been, until recently, an MSP ( Microsoft Student Partner ).
    Thursday, July 10, 2008 1:35 PM
  • Hi ALL,

    Here is the Multi_Extended version.

    It is a completely separate CLASS so by default it is MultiExtended in its SelectionMode .

    So now you can have two versions in your ToolBox!!    :-)    ;-)




    Regards,

    John
    _______________________________

    'Enhanced_Multi_Extended_ListBox
    Public Class Enhanced_ME_ListBox
        Inherits ListBox

    'Use this if you don't want a default.>>

    'Private _default_selection_Mode As SelectionMode = Windows.Forms.SelectionMode.None

    Private _default_selection_Mode As SelectionMode = Windows.Forms.SelectionMode.MultiExtended

    Public Sub New()

    If Not Me._default_selection_Mode = Windows.Forms.SelectionMode.None Then
    Me.SelectionMode = Me._default_selection_Mode
    End If

    End Sub

        Private DisableChangedEvents As Boolean

        ' Move all selected items up one step. If the first item in the  
        ' list is selected no move can be done.  
        ' Moved items will preserve their selected state after the move.  
        '  
        Public Sub MoveSelectedUp()

            If (Me.SelectedIndices.Count > 0) Then
                If (Me.SelectedIndices(0) > 0) Then

                    Dim selectedSnapShot(Me.SelectedIndices.Count - 1) As Integer
                    Me.SelectedIndices.CopyTo(selectedSnapShot, 0)
                    DisableChangedEvents = True

                    For Each i As Integer In selectedSnapShot
                        Dim o As Object = Me.Items(i)
                        Me.Items.RemoveAt(i)
                        Me.Items.Insert(i - 1, o)
                        Me.SetSelected(i - 1, True)
                    Next
                    DisableChangedEvents = False

                End If
            End If
        End Sub


        ' Move all selected items down one step. If the last item in the   
        ' list is selected no move can be done.  
        ' Moved items will preserve their selected state after the move.  
        '  
        Public Sub MoveSelectedDown()

            If (Me.SelectedIndices.Count > 0) Then
                If (Me.SelectedIndices(Me.SelectedIndices.Count - 1) < (Me.Items.Count - 1)) Then

                    Dim selectedSnapShot(Me.SelectedIndices.Count - 1) As Integer
                    Me.SelectedIndices.CopyTo(selectedSnapShot, 0)
                    DisableChangedEvents = True

                    For p As Integer = selectedSnapShot.Length - 1 To 0 Step -1
                        Dim i = selectedSnapShot(p)
                        Dim o As Object = Me.Items(i)
                        Me.Items.RemoveAt(i)
                        Me.Items.Insert(i + 1, o)
                        Me.SetSelected(i + 1, True)
                    Next
                    DisableChangedEvents = False

                End If
            End If
        End Sub

        ' We need to override the OnSelectedIndexChanged and OnSelectedValueChanged to   
        ' disable the events from firing when we're moving selected items up or down.  
        '  
        Protected Overrides Sub OnSelectedIndexChanged(ByVal e As System.EventArgs)
            If (Not DisableChangedEvents) Then
                MyBase.OnSelectedIndexChanged(e)
            End If
        End Sub

        Protected Overrides Sub OnSelectedValueChanged(ByVal e As System.EventArgs)
            If (Not DisableChangedEvents) Then
                MyBase.OnSelectedValueChanged(e)
            End If
        End Sub

         Protected Overrides Sub OnKeyDown(ByVal e As System.Windows.Forms.KeyEventArgs)
            If e.Control Then
                Select Case e.KeyCode
                    Case Keys.Down
                        MoveSelectedDown()
                        e.Handled = True
                        Return
                    Case Keys.Up
                        MoveSelectedUp()
                        e.Handled = True
                        Return
                End Select
            End If
            MyBase.OnKeyDown(e)
        End Sub

    Protected Shadows Sub MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown

    If e.Button = Windows.Forms.MouseButtons.Middle Then MoveSelectedDown()
    If e.Button = Windows.Forms.MouseButtons.Right Then MoveSelectedUp()

    End Sub

    End Class


    I have previously been, until recently, an MSP ( Microsoft Student Partner ).
    Thursday, July 10, 2008 1:44 PM
  • Hi Calle Mellergardh,

    I've given you an extra "Helpful" vote.

    If I could I would give you your other 3 medals for that code!!

    Excellent code!!

    Try this with one of each CLASS above on a FORM.
    With the Enhanced_ME_ListBox1 which is MultiExtended just drag your mouse vertically to make selections. :-)




    Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    For num As Integer = 1 To 100
    Enhanced_ME_ListBox1.Items.Add(num)
    Enhanced_MS_ListBox1.Items.Add(num)
    Next

    End Sub

    End Class




    Regards,

    John




    I have previously been, until recently, an MSP ( Microsoft Student Partner ).
    Thursday, July 10, 2008 1:52 PM
  • Hi ALL,

    I tried this in the MouseDown event to try to continuously move
     the items as if you were holding the keys down.

    At run-time it doesn't like this though. :-(

    Any ideas on continuous movement with the Right and Middle mouse buttons?




    Regards,

    John
    ______________________________________________________________




    Protected Shadows Sub MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown

    While e.Button = Windows.Forms.MouseButtons.Middle
    MoveSelectedDown()
    End While

    While e.Button = Windows.Forms.MouseButtons.Right
    MoveSelectedUp()
    End While

    End Sub

    I have previously been, until recently, an MSP ( Microsoft Student Partner ).
    Thursday, July 10, 2008 2:01 PM