Loop listbox, run code and pause

garriew

Member
Joined
Sep 13, 2013
Messages
13
Programming Experience
Beginner
I hope I can explain this clearly...

I am looping through a listbox and on each loop I need to decrease a label by 1, perform a command and then wait for a specified time before it loops to the next listbox item.

The wait time should be a random number number based on user input and will be different for each list item. The countdown will also display in a label.

I also need a command button that will stop the listbox loop. I haven't worked on this yet. Just didn't know if it mattered for the method for the above.

To do the pause/wait, I tried using a WAIT class I found but it paused the entire program so the countdown wouldn't work nor a stop button. I did get a countdown working in a timer but I couldn't get the listbox loop synced with it. The listbox just loops pretty quick.

Here is my code to generate the random number to wait & countdown.
VB.NET:
   'time to delay
    Dim seconds As String() = TextBox1.Text.Split("-")
    Dim snum1 As Integer = seconds(0)
    Dim snum2 As Integer = seconds(seconds.Length - 1)
    Dim RandomClass2 As New Random()
    Dim RandomNumber2 As Integer    If snum2 < snum1 Then
        snum2 = snum1
    End If
    RandomNumber2 = RandomClass2.Next(snum1, snum2)
    timeLeft = RandomNumber2 'this is used in my timer to count down


This sets the loop label (how many time to loop) of the listbox value and loops the listbox

VB.NET:
 Label4.Text = ListBox1.Items.Count  
 'loop each item
    For i As Integer = 0 To ListBox1.Items.Count - 1
        Timer1.Enabled = True
        Label5.Text = (ListBox1.Items(i))
        Label4.Text = Label4.Text - 1
    Next
This is my timer1 code

VB.NET:
 'get time to wait
    Label7.Text = timeLeft & " seconds"
    If timeLeft > 0 Then
        timeLeft -= 1
        Label7.Text = timeLeft & " seconds"
    Else
        Timer1.Stop()
        Label7.Text = "---"
    End If
 
Hi,

To do this you need to change your logic since as you have already seen by your own code, the For Loop as you have it now completes all iterations with no pauses. The logic to Follow should be something along the lines of:-

1) Declare a Private Variable at the Class level called something like listBoxIndex. This will have a default value of 0 which will match the first Element of the ListBox.

2) Create a Subroutine to Perform the actions that you want using the listBoxIndex variable declared above to access that ONE element in the ListBox only. (Lets call this PerformMyActions for this explanation). Once you have completed these actions, Increment the listBoxIndex variable by 1 and then use the Random class to generate your Random time to wait before you want the next Action to begin. Then set the Timer Interval Property to this Random time (the Interval property of a Timer expects a millisecond value so do not forget that 1 second equals 1000 milliseconds) and finally enable the Timer.

3) Once the Timer fires, disable the Timer and then check to see if the listBoxIndex variable is less that or equal to ListBox.Items.Count - 1 and, if so, then call your PerformMyActions subroutine again to continue the Loop otherwise your routine has finished.

4) To start the whole thing running just call your PerformMyActions subroutine for the first time from a Button.

Hope that helps.

Cheers,

Ian

BTW, When using the Random Class make sure that you declare this variable at the Class level and not at the Local level otherwise you will get unexpected results.
 
check to see if the listBoxIndex variable is less that or equal to ListBox.Items.Count - 1
Or, more simply, less than ListBox.Items.Count. :)
 
Ian,

Thanks for the explanation. It helped a lot! I redid the code and got it working but with just one snag. I get an "argument out of range" error when I run out of listbox items on:

VB.NET:
[FONT=Consolas][SIZE=2][FONT=Consolas][SIZE=2]Label5.Text = ListBox1.Items(ListBoxIndex)
[/SIZE][/FONT][/SIZE][/FONT]

It shouldn't be going to that line when the listbox items are all used because in the timer I put:
VB.NET:
[FONT=Consolas][SIZE=2][COLOR=#0000ff][FONT=Consolas][SIZE=2][COLOR=#0000ff][FONT=Consolas][SIZE=2][COLOR=#0000ff]If
[/COLOR][/SIZE][/FONT][/COLOR][/SIZE][/FONT][/COLOR][/SIZE][/FONT][FONT=Consolas][SIZE=2][FONT=Consolas][SIZE=2]ListBoxIndex <= ListBox1.Items.Count [/SIZE][/FONT][/SIZE][/FONT][FONT=Consolas][SIZE=2][COLOR=#0000ff][FONT=Consolas][SIZE=2][COLOR=#0000ff][FONT=Consolas][SIZE=2][COLOR=#0000ff]Then[/COLOR][/SIZE][/FONT][/COLOR][/SIZE][/FONT][/COLOR][/SIZE][/FONT]
[FONT=Consolas][SIZE=2][FONT=Consolas][SIZE=2]                JustDoIt()

It's like it's not stopping.

Incase it helps, all of my code is below.

VB.NET:
[FONT=Consolas][SIZE=2][COLOR=#0000ff] 
[FONT=Consolas][SIZE=2][COLOR=#0000ff]Public Class Form1
    Dim ListBoxIndex = 0
    Private timeLeft As Integer
 
    Private Sub TimeToWait()
        'time to delay
        Dim seconds As String() = DelayTime.Text.Split("-")
        Dim snum1 As Integer = seconds(0)
        Dim snum2 As Integer = seconds(seconds.Length - 1)
        Dim RandomClass2 As New Random()
        If snum2 < snum1 Then
            snum2 = snum1
        End If
        timeLeft = RandomClass2.Next(snum1, snum2)
        countdownLabel.Text = timeLeft & " seconds"
        Timer1.Enabled = True

    End Sub

    Private Sub JustDoIt()

        Label5.Text = ListBox1.Items(ListBoxIndex)
        ListBoxIndex = ListBoxIndex + 1
        Call TimeToWait()

    End Sub

    Private Sub ListBox1_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
    End Sub
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        ListBox1.Items.Add("Line 1")
        ListBox1.Items.Add("Line 2")
        ListBox1.Items.Add("Line 3")
        ListBox1.Items.Add("Line 4")
        ListBox1.Items.Add("Line 5")
    End Sub
    Private Sub StartButton_Click(sender As System.Object, e As System.EventArgs) Handles StartButton.Click
        countdownSearch.Text = ListBox1.Items.Count
        Call JustDoIt()
    End Sub
    Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
        If timeLeft > 0 Then
            timeLeft -= 1
            countdownLabel.Text = timeLeft & " seconds"
        Else
            Timer1.Stop()
            countdownSearch.Text = countdownSearch.Text - 1
            If ListBoxIndex <= ListBox1.Items.Count Then
                JustDoIt()
            Else
                countdownSearch.Text = "---"
                countdownLabel.Text = "---"
                ListBoxIndex = 0
            End If
        End If
    End Sub
    Private Sub StopButton_Click(sender As System.Object, e As System.EventArgs) Handles StopButton.Click
        Timer1.Stop()
        countdownSearch.Text = "---"
        countdownLabel.Text = "---"
        ListBoxIndex = 0
    End Sub
End Class

[/COLOR][/SIZE][/FONT][/COLOR][/SIZE][/FONT][FONT=Consolas][SIZE=2][COLOR=#0000ff][FONT=Consolas][SIZE=2][COLOR=#0000ff][FONT=Consolas][SIZE=2][COLOR=#0000ff][/COLOR][/SIZE][/FONT][/COLOR][/SIZE][/FONT][/COLOR][/SIZE][/FONT]
[FONT=Consolas][SIZE=2][COLOR=#0000ff][FONT=Consolas][SIZE=2][COLOR=#0000ff][FONT=Consolas][SIZE=2][COLOR=#0000ff][/COLOR][/SIZE][/FONT][/COLOR][/SIZE][/FONT][/COLOR][/SIZE][/FONT][FONT=Consolas][SIZE=2][FONT=Consolas][SIZE=2][/SIZE][/FONT][/SIZE][/FONT]


[/SIZE][/FONT][/SIZE][/FONT]
 
Thanks for the explanation. It helped a lot! I redid the code and got it working but...

Good for you and glad I could help.

You have made two errors based on what I said in my logic statement:-

1) You have declared your Random Variable at the Local level so, even though you may have not noticed an issue at the moment, this will at some point start to give you unexpected results. This declaration needs to be moved to the Class level.

2) In my statement I said "the listBoxIndex variable is less than or equal to ListBox.Items.Count - 1" and you did:-

VB.NET:
If ListBoxIndex <= ListBox1.Items.Count Then

You missed the "minus 1" and therefore you are trying to access an element at the end of the Loop which does not exist.

If you then look at post #3, jmcilhinney mentioned that you could also simplify this by using:-

VB.NET:
If ListBoxIndex < ListBox1.Items.Count Then

Notice the sight differences between what you have and the two correct statements.

Hope that helps.

Cheers,

Ian
 
Thanks again, Ian. Can't believe I missed the "-1" or just using "<".

One last question. Could you explain what you mean by moving to the class level? Does it mean do project -> add class? I'm really new.

Thanks again.
 
Hi,

No. What I mean is to move the declaration of the Random variable to where you have declared your listBoxIndex variable. That being, just under the declaration of the Class, often referred to as the Class Level. This variable then has scope throughout all the routines within the Class.

To try and explain better why you need to do this, have a read through this and run the demonstrations to see the different results:-

The Random Class works by using a Logical Algorithm to generate a number. However, that logical algorithm returns Random numbers each time a Method in the Class is called by using a Starting Seed. If you do not provide your own Seed then the Class will use the System Time as its starting seed.

So, knowing this, if you were to Declare a variable of Type Random within a Local routine and you called that routine numerous times in succession, lets say to generate a list of 30 Random numbers, then what would happen is that the Random Class would be seeded with the same system time for every Second the routine was called and you would end up with a list of the SAME number for each Second of time.

To better explain this, run this code and see what happens:-

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
  Dim myListOfRandomNumbers As New List(Of Integer)
 
  For Counter As Integer = 1 To 30
    Dim myRandom As New Random
    myListOfRandomNumbers.Add(myRandom.Next(1000, 10000))
  Next
 
  MsgBox(String.Join(Environment.NewLine, myListOfRandomNumbers.ToArray))
End Sub


As you can see, all the numbers are the same since the For Loop completed its task under one second.

By declaring the Random variable at the Class level, then this Random variable only gets seeded once, and due to this, each time you call a Method of the Random class then the Algorithm moves to the next logical number in the Random sequence rather than starting the sequence again with the same seed.

Again, to demonstrate this, run this code and see what happens:-

Private myCorrectRandomDeclaration As New Random
 
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
  Dim myListOfRandomNumbers As New List(Of Integer)
 
  For Counter As Integer = 1 To 30
    myListOfRandomNumbers.Add(myCorrectRandomDeclaration.Next(1000, 10000))
  Next
 
  MsgBox(String.Join(Environment.NewLine, myListOfRandomNumbers.ToArray))
End Sub


As you can see, you now get Random numbers as expected.

Hope that helps.

Cheers,

Ian

BTW, another bit of advice I can give you is to turn Option Strict On. This will help you to identify and correct all Type Conversion errors as you encounter them within your coding. To change this in your current project go to Project->Properties on the IDE's Menu and change the options on the Compile Tab and for all future projects you can do this by going to Tools->Options on the IDE's Menu and changing the VBDefaults.
 
Back
Top