locked
bug when working on selecteditems of listbox? RRS feed

  • Question

  • When changing selecteditems (the items themselves, not which ones are selected) in a multiselect listbox, sometimes the operation does not happen and items are deselected.  Here is a simple example of what I mean, just a form with a multiselect listbox.

    Public Class Form1
    
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            ListBox1.Items.Add("item at index 0")
            ListBox1.Items.Add("item at index 1")
            ListBox1.Items.Add("item at index 2")
            ListBox1.Items.Add("item at index 3")
            ListBox1.SelectedIndex = 0
            ListBox1.SelectedIndex = 1
            ListBox1.SelectedIndex = 2
            ListBox1.SelectedIndex = 3
            For Each index In ListBox1.SelectedIndices
                ListBox1.Items(index) &= " operated on"
            Next
        End Sub
    End Class
    When this example runs, items at index 0 and 3 are deselected and item at index 3 is not changed.  If this example has more items, the flaw repeats everyother item. Am I missing something and writing bad code, or is this a bug in visual basic itself?
    Sunday, May 27, 2012 6:43 PM

Answers

  • Sorry Frank for replying out of order and the unclear details.

    Thanks Paul.  I was hoping to avoid duplicating the selectedindices collection needing more variables and lines of code, but can live with it.  It still bugs me why with my original and seemingly most efficient for next loop, some items get deselected and not others.

    Its because you are changing which "index numbers" that are inside the "listbox1.selectedindices" collection. You are not directly changing these numbers, but by editing the value at that index, the listbox automatically is changing the selection state of the item at that index. Each time this happens it updates the listbo's "selectedindices" collection, even if you are in the middle of a loop.

    If you want something you've never had, you need to do something you've never done. If you believe something to be true, then one day you will be called upon to demonstrate that truth.


    • Edited by Paul Ishak Tuesday, May 29, 2012 12:30 AM
    • Marked as answer by zuckerberg Tuesday, May 29, 2012 2:07 AM
    Tuesday, May 29, 2012 12:28 AM

All replies

  • You are aware, I assume, that each time you set the selected index, it's just that - it's changed to whatever you set it to be in code, right?

    Please call me Frank :)

    • Proposed as answer by Paul Ishak Sunday, May 27, 2012 7:51 PM
    • Unproposed as answer by zuckerberg Monday, May 28, 2012 6:47 PM
    Sunday, May 27, 2012 7:15 PM
  • I was hoping you'd reply back by now, but at any rate, to make multiple selections with a listbox, use the SetSelected Method. I have an example on a page of my website here (I hate this new code formatter this forum uses now) and following demonstrates it:


    Please call me Frank :)

    • Proposed as answer by Paul Ishak Sunday, May 27, 2012 8:13 PM
    • Unproposed as answer by zuckerberg Monday, May 28, 2012 6:47 PM
    • Proposed as answer by .paul. _ Monday, May 28, 2012 11:49 PM
    Sunday, May 27, 2012 7:50 PM
  • Public Class Form1
        Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
            ListBox1.Items.Add("item at index 0")
            ListBox1.Items.Add("item at index 1")
            ListBox1.Items.Add("item at index 2")
            ListBox1.Items.Add("item at index 3")
            ListBox1.SelectionMode = SelectionMode.MultiExtended
            ListBox1.SetSelected(0, True)
            ListBox1.SetSelected(1, True)
            ListBox1.SetSelected(2, True)
            ListBox1.SetSelected(3, True)
            For I = 0 To ListBox1.SelectedIndices.Count - 1
                  ListBox1.Items(I) = ListBox1.Items(I).ToString & " operated on"
            Next
        End Sub
    End Class

    I just tested it this way too, and this also works:

    Public Class Form1
        Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
            ListBox1.Items.Add("item at index 0")
            ListBox1.Items.Add("item at index 1")
            ListBox1.Items.Add("item at index 2")
            ListBox1.Items.Add("item at index 3")
            ListBox1.SelectionMode = SelectionMode.MultiExtended
            ListBox1.SelectedIndex = 0
            ListBox1.SelectedIndex = 1
            ListBox1.SelectedIndex = 2
            ListBox1.SelectedIndex = 3
            For I = 0 To ListBox1.SelectedIndices.Count - 1
                  ListBox1.Items(I) = ListBox1.Items(I).ToString & " operated on"
            Next
        End Sub
    End Class
    Which means that you needed "ListBox1.SelectionMode = SelectionMode.MultiExtended"


    If you want something you've never had, you need to do something you've never done. If you believe something to be true, then one day you will be called upon to demonstrate that truth.



    • Proposed as answer by Frank L. Smith Sunday, May 27, 2012 8:15 PM
    • Edited by Paul Ishak Sunday, May 27, 2012 8:22 PM
    • Unproposed as answer by zuckerberg Monday, May 28, 2012 6:47 PM
    Sunday, May 27, 2012 8:07 PM
  • When changing selecteditems (the items themselves, not which ones are selected) in a multiselect listbox, sometimes the operation does not happen and items are deselected.  Here is a simple example of what I mean, just a form with a multiselect listbox.

    Public Class Form1
    
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            ListBox1.Items.Add("item at index 0")
            ListBox1.Items.Add("item at index 1")
            ListBox1.Items.Add("item at index 2")
            ListBox1.Items.Add("item at index 3")
            ListBox1.SelectedIndex = 0
            ListBox1.SelectedIndex = 1
            ListBox1.SelectedIndex = 2
            ListBox1.SelectedIndex = 3
            For Each index In ListBox1.SelectedIndices
                ListBox1.Items(index) &= " operated on"
            Next
        End Sub
    End Class
    When this example runs, items at index 0 and 3 are deselected and item at index 3 is not changed.  If this example has more items, the flaw repeats everyother item. Am I missing something and writing bad code, or is this a bug in visual basic itself?

    You are writing bad code.


    Success
    Cor

    Sunday, May 27, 2012 8:19 PM
  • Hi Zuckerberg

    Welcome to MSDN

    I assume this is not a bug. Look closely this part of the code

       For Each index In ListBox1.SelectedIndices
                ListBox1.Items(index) &= " operated on"
            Next

     the logic of the code. You can use break point to see what is goin on with the index. I am newbie, so I can only assume.

    Be a good forum member. Make this forum a great place to meet and interact with others around the world.
    Helpful Links:

    Sunday, May 27, 2012 8:23 PM
  • I stepped through the loop and see that the selectedindices collection changes upon certain loops, but I still don't understand why.  Please explain.

    Monday, May 28, 2012 6:15 PM
  • I think its because before you did NOT have the following line:

    "ListBox1.SelectionMode = SelectionMode.MultiExtended"

    Comment out that line and see what happens


    If you want something you've never had, you need to do something you've never done. If you believe something to be true, then one day you will be called upon to demonstrate that truth.


    • Edited by Paul Ishak Monday, May 28, 2012 6:17 PM
    Monday, May 28, 2012 6:16 PM
  • Sorry my code block didn't show it, but I already had the selectionmode set to multi on the [Design] page.  That was not my problem.

    And changing the for next loop is not the solution I'm looking for.  For example, what if there were more items and the selected ones to change were just 1, 3 and 4.

    Public Class Form1
    
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            ListBox1.Items.Add("item at index 0")
            ListBox1.Items.Add("item at index 1")
            ListBox1.Items.Add("item at index 2")
            ListBox1.Items.Add("item at index 3")
            ListBox1.Items.Add("item at index 4")
            ListBox1.Items.Add("item at index 5")
            ListBox1.Items.Add("item at index 6")
            ListBox1.SelectionMode = SelectionMode.MultiExtended
            ListBox1.SetSelected(1, True)
            ListBox1.SetSelected(3, True)
            ListBox1.SetSelected(4, True)
            For I = 0 To ListBox1.SelectedIndices.Count - 1
                ListBox1.Items(I) = ListBox1.Items(I).ToString & " operated on"
            Next
        End Sub
    End Class
    The for next loop you suggested would just change 0,1 and 2.  To change 1,3 and 4, I would think my original for next loop (For each I in ListBox1.SelectedIndices) would work, but 3 gets deselcted and 4 not operated on which is what I don't understand.

    • Edited by zuckerberg Monday, May 28, 2012 6:34 PM
    Monday, May 28, 2012 6:30 PM
  • Sorry my code block didn't show it, but I already had the selectionmode set to multi on the [Design] page.  That was not my problem.

    And changing the for next loop is not the solution I'm looking for.  For example, what if there were more items and the selected ones to change were just 1, 3 and 4.

    Public Class Form1
    
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
            ListBox1.Items.Add("item at index 0")
            ListBox1.Items.Add("item at index 1")
            ListBox1.Items.Add("item at index 2")
            ListBox1.Items.Add("item at index 3")
            ListBox1.Items.Add("item at index 4")
            ListBox1.Items.Add("item at index 5")
            ListBox1.Items.Add("item at index 6")
            ListBox1.SelectionMode = SelectionMode.MultiExtended
            ListBox1.SetSelected(1, True)
            ListBox1.SetSelected(3, True)
            ListBox1.SetSelected(4, True)
            For I = 0 To ListBox1.SelectedIndices.Count - 1
                ListBox1.Items(I) = ListBox1.Items(I).ToString & " operated on"
            Next
        End Sub
    End Class
    The for next loop you suggested would just change 0,1 and 2.  To change 1,3 and 4, I would think my original for next loop (For each I in ListBox1.SelectedIndices) would work, but 3 gets deselcted and 4 not operated on which is what I don't understand.

    I don't know who you're talking to - you seem to have bypassed everything I put yesterday but to answer your question, it makes perfect sense.

    Look at what you're doing there - you're not doing anything with the selected items or selected indices; you're getting the count but telling the loop the progress through the listbox from zero to that count (the latter having nothing to do with the SELECTED ones).

    Make sense?


    Please call me Frank :)

    • Proposed as answer by .paul. _ Monday, May 28, 2012 11:51 PM
    Monday, May 28, 2012 6:50 PM
  • OK, so I clearly understand what you are talking about, but I am unsure as to if it is a bug, or simply a mus-implementation of the code.

    Anyways, I have taken the liberty to make a little code that better points out the problem. Change the test condiition variable to see.

    Very interesting, I will continue to look into this.

        Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
            ListBox1.Items.Add("item at index 0")
            ListBox1.Items.Add("item at index 1")
            ListBox1.Items.Add("item at index 2")
            ListBox1.Items.Add("item at index 3")
            ListBox1.Items.Add("item at index 4")
            ListBox1.Items.Add("item at index 5")
            ListBox1.Items.Add("item at index 6")
            ListBox1.SelectionMode = SelectionMode.MultiExtended
            ListBox1.SetSelected(0, True)
            ListBox1.SetSelected(1, True)
            ListBox1.SetSelected(2, True)
            ListBox1.SetSelected(3, True)
            ListBox1.SetSelected(4, True)
            ListBox1.SetSelected(5, True)
            ListBox1.Update()
            Dim TestCondition As Integer = 1
            For Each SelectedIndex As Integer In ListBox1.SelectedIndices
                Select Case TestCondition
                    Case 1
                        ListBox1.Items(SelectedIndex) = String.Concat(ListBox1.Items(SelectedIndex), " operated on")
                        MsgBox(SelectedIndex)
                    Case 2
                        MsgBox(SelectedIndex)
                End Select
            Next
        End Sub


    If you want something you've never had, you need to do something you've never done. If you believe something to be true, then one day you will be called upon to demonstrate that truth.

    Monday, May 28, 2012 7:03 PM
  • Thats my fault frank haha!

    Then you see my point I hope?

    For all that's being done, he may as well have the loop set up as "For i As Integer = 0 to 2", because in the code that follows, it's not looking at the selected items, just the items by index number.


    Please call me Frank :)

    Monday, May 28, 2012 7:09 PM
  • Thats my fault frank haha!

    Then you see my point I hope?

    For all that's being done, he may as well have the loop set up as "For i As Integer = 0 to 2", because in the code that follows, it's not looking at the selected items, just the items by index number.


    Please call me Frank :)

    I fully see your point, and I take the blame for that, because he was using for-each originally!

    If you want something you've never had, you need to do something you've never done. If you believe something to be true, then one day you will be called upon to demonstrate that truth.

    Monday, May 28, 2012 7:15 PM
  • I think it's still being missed. As much as I hate this code formatter, it's really nothing more than this:

            For Each itm As String In ListBox1.SelectedItems
                ' Do something
            Next


    Please call me Frank :)

    Monday, May 28, 2012 7:28 PM
  • ok try this! The problem was that the SelectedIndices collection would update while you were changing the values in the collection. You need to copy the values of the collection first to an array, then iterate through the copy of the collection instead of directly iterating through the SelectedIndices collection. This is because items become selected/unselected as you change their values. If after you have modified the values that you have selected, you wish them to remain selected, you are going to have to use the copy of the array to return all items to their original selection state.

    Option Strict On
    Public Class Form1
        Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
            ListBox1.Items.Clear() 'Clear The Listbox
            ListBox1.Items.Add("item at index 0") 'Populate The Listbox
            ListBox1.Items.Add("item at index 1")
            ListBox1.Items.Add("item at index 2")
            ListBox1.Items.Add("item at index 3")
            ListBox1.Items.Add("item at index 4")
            ListBox1.Items.Add("item at index 5")
            ListBox1.Items.Add("item at index 6")
            ListBox1.SelectionMode = SelectionMode.MultiExtended 'Set to multiextended
            ListBox1.SetSelected(0, True) 'Set the selection state of some items
            ListBox1.SetSelected(1, True)
            ListBox1.SetSelected(2, True)
            ListBox1.SetSelected(3, False)
            ListBox1.SetSelected(4, True)
            ListBox1.SetSelected(5, True)
            ListBox1.SetSelected(6, True)
            'Copy all values to an array
            Dim Collection(ListBox1.SelectedIndices.Count - 1) As Integer
            For I = 0 To ListBox1.SelectedIndices.Count - 1
                Collection(I) = ListBox1.SelectedIndices(I)
            Next
            'cycle through the array
            For Each SelectedIndex As Integer In Collection
                'concatonate each selected item
                'The selected indices collection changes here, as you change values
                ListBox1.Items(SelectedIndex) = String.Concat(ListBox1.Items(SelectedIndex), " operated on")
            Next
            'restore the selected indexs to their original state
            For Each SelectedIndex As Integer In Collection
                ListBox1.SetSelected(SelectedIndex, True)
            Next
        End Sub
    End Class

    If you want something you've never had, you need to do something you've never done. If you believe something to be true, then one day you will be called upon to demonstrate that truth.


    • Edited by Paul Ishak Monday, May 28, 2012 7:49 PM
    Monday, May 28, 2012 7:41 PM
  • Sorry Frank for replying out of order and the unclear details.

    Thanks Paul.  I was hoping to avoid duplicating the selectedindices collection needing more variables and lines of code, but can live with it.  It still bugs me why with my original and seemingly most efficient for next loop, some items get deselected and not others.

    Tuesday, May 29, 2012 12:20 AM
  • Sorry Frank for replying out of order and the unclear details.

    Thanks Paul.  I was hoping to avoid duplicating the selectedindices collection needing more variables and lines of code, but can live with it.  It still bugs me why with my original and seemingly most efficient for next loop, some items get deselected and not others.

    Its because you are changing which "index numbers" that are inside the "listbox1.selectedindices" collection. You are not directly changing these numbers, but by editing the value at that index, the listbox automatically is changing the selection state of the item at that index. Each time this happens it updates the listbo's "selectedindices" collection, even if you are in the middle of a loop.

    If you want something you've never had, you need to do something you've never done. If you believe something to be true, then one day you will be called upon to demonstrate that truth.


    • Edited by Paul Ishak Tuesday, May 29, 2012 12:30 AM
    • Marked as answer by zuckerberg Tuesday, May 29, 2012 2:07 AM
    Tuesday, May 29, 2012 12:28 AM
  • After more tinkering, I found a shorter fix.

            For Each index In ListBox1.SelectedIndices
                ListBox1.Items(index) &= " operated on"
                ListBox1.SetSelected(index, True)
            Next
    I guess the setselected line in the loop forces or reminds the system not to change it.
    Tuesday, May 29, 2012 2:41 AM