I’ve just changed my PC and while I was copying over some of the old stuff I found several projects I’d created to answer questions people had asked on VB City. So, before those projects get lost in the mist of time again, I thought I’d use some of them as blog topics, because these questions seem to come up time after time. Here’s the first of them.
Display a random PictureBox
Someone had a group of PictureBoxes and wanted to display one at a time in a random sequence. Let’s say that there are 9 PictureBoxes. The first thing to do is to keep them under control and one way of doing that is to put them all in a Panel:
There are various ways that you can identify the individual PictureBoxes at run time, but one of the easiest ways is to assign a value to the Tag property of each of them. So each of the PictureBoxes you can see in that screenshot has an integer between 1 and 9 assigned to its Tag property.
At its most basic, all that’s needed is a timer to control the flow of PictureBox changes and code to select a random number between 1 and 9 to pick the next one to show. I used a Windows Forms Timer from the Toolbox and dragged it on to the form. Here’s code that does that the random timing and display:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
Timer1.Enabled = True
Timer1.Interval = 1000
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
' Create a random number variable
Dim rnd As Random = New System.Random(Int(DateTime.Now.Millisecond))
' Assign the random number to an Integer variable
Dim NextPic As Integer = rnd.Next(1, Panel1.Controls.Count + 1)
' Firstly, hide all picture boxes
For Each pb As PictureBox In Panel1.Controls
pb.Visible = False
Next
' Then select one picturebox to display
For Each pb As PictureBox In Panel1.Controls
If CInt(pb.Tag) = NextPic Then pb.Visible = True
Next
End Sub
In the Form Load, I set the Timer running and set the interval between picture changes. In the Timer’s Tick event, I first create a new Random object and then use the Next method of the Random class to limit the range of values between 1 and 9. I could have hard coded in that value of 9, because i know there are 9 picture boxes, but as a general rule it’s better to use code that calculates these kind of values. That way, if you want to change the number of picture boxes or reuse the same code somewhere else, you can do that without changing the code. When you hard code numbers, it’s really easy to forget you did so, and then after some changes you have to figure out why your code no longer works. Those hard coded values are often the culprit.
The next step in the Tick event is to hide all the picture boxes. Of course, once the application starts running there’ll only be one that’s visible, but this code is an easy way of hiding whichever one it is. if you really wanted to identify the PictureBox that's currently visible you could of course do that, but with a simple app like this one, there’s not a lot of point.
Finally, the last For Each block of code checks through each of the PictureBoxes in turn and finds the one whose Tag property equals the currently selected random number. That PictureBox is then displayed. In this example, the PictureBox that has a Tag value of 2 is shown:
That’s OK as far as it goes, but if the same number is chosen twice in succession – a very likely scenario with such a small range – the picture appears to flicker. What’s happening of course is that the display is cleared and then the same image is shown again. This often won’t be acceptable, so we’d better code round that problem.
The first step is to create a variable that will keep track of the random number that was last selected (i.e. the Tag property of the currently visible PictureBox).
Dim lastPic As Integer = 0
Then, to make things easier to read, I’ll move the random generation code into a function of its own:
Function GetRandomNumber() As Integer
' Create a random number variable
Dim rnd As Random = New System.Random(Int(DateTime.Now.Millisecond))
' Assign the random number to an Integer variable
Dim NextPic As Integer = rnd.Next(1, Panel1.Controls.Count + 1)
' Firstly, hide all pictureboxes
For Each pb As PictureBox In Panel1.Controls
pb.Visible = False
Next
Return NextPic
End Function
Then create a function that checks if the random number that’s just been generated is a repeat. If it is a repeat, then the value is changed.
Function RepeatChecker(ByVal lastPicture As Integer, ByVal potentialPicture As Integer) As Integer
If lastPicture <> potentialPicture Then
Return potentialPicture
ElseIf potentialPicture = Panel1.Controls.Count Then
Return potentialPicture - 3
Else : Return potentialPicture + 1
End If
End Function
The first ElseIf clause checks if the random value is (in this example that has 9 PictureBoxes) 9. if it is, Then the value is manually changed to a lower value. In this case, I’ve subtracted 3 from it, but I could have used any value under 9.
The final Else clause adds a value of 1 to the random number. You’ll see now why I dealt with the value of 9 first. If 1 is added to the total number of available picture boxes (in this case, making a figure of 10) and returned, an error will occur because (in this case) there isn’t a PictureBox that has 10 as its Tag property value.
Finally, the Tick event is changed so that these two functions are called:
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Dim NextToDisplay As Integer = GetRandomNumber()
NextToDisplay = RepeatChecker(lastPic, NextToDisplay)
' Then select one picturebox to display
For Each pb As PictureBox In Panel1.Controls
If CInt(pb.Tag) = NextToDisplay Then pb.Visible = True
Next
' Note currently selected picturebox number
lastPic = NextToDisplay
End Sub
Notice that the last line of this event updates the lastPic variable with the value of the currently visible PictureBox.