vb.net “For Each” iterates only once over a list-Collection of common programming errors
I seem to remember running into a similar issue, the trick if memory serves is to remove the form objectively from the for each like so
Sub Butt_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Butt.Click
Dim Forms As New List(Of Form)
Dim Pics As New Dictionary(Of Form, PictureBox)
Forms.Add(me)
Forms.Add(form2)
Forms.Add(form3)
Forms.Add(form4)
For x As Integer = 0 To Forms.Count - 1
If Not Pics.Contains(Forms(x), DirectCast(Me.Controls("PictureBox" + CStr(i + 1))(0), PictureBox)) Then
Pics.Add(Forms(x), DirectCast(Me.Controls("PictureBox" + CStr(i + 1))(0), PictureBox))
Else
Pics(Forms(x)) = DirectCast(Me.Controls("PictureBox" + CStr(i + 1))(0), PictureBox)
End If
Next
########################################################################################## UPDATE explore and expand on the above ##############################################################################
What follows is a more detailed explanation and break down of this problem and why it isn’t actually a myth or an oddity in .NET
The .NET OO framework is incredibly large complex and powerful, to understand and remember it all is not humanly impossible, but there are a few concepts that need to be grasped in order to navigate your way through the maze.
Lets examine what was going wrong.
You declared and populated a List of forms List(Of Form) with each form in your project
Dim Forms as New List(Of Form)
You then declared a dictionary to hold a (key, value) collection of the shape (Form, PictureBox)
Dim Pics as New Dictionary(Of Form, PictureBox)
This means your “Key” is of type Form and your “Value” is of type PictureBox
Now, using the list of Forms you were iterating through each form in the list, saying that you want the “Value”(PictureBox) of the “Key”(Form) in the Dictionary*(Pics)* to be set to a PictureBox found on the current form (Where the code is running) with name “PictureBox(x)” where x is your incremental integer value of some kind. Strangely you are then asking for the first instance of something of that picturebox (thats the (0) part on the end)
For Each frm As Form In Forms
pics(frm) = Me.Controls("PictureBox" + CStr(i + 1))(0)
Next
Fundamentally the method invoked from pics(frm) only gets or sets the value of an existing item in the dictionary, since the dictionary is not loaded with anything yet, Pics is empty, at no point did you add any of the forms to the Dictionary and as such nothing would be set at all.
A more suitable approach to achieve the Key Value assignment would be as follows, however this is still overkill to simply set the background image of the forms
Sub BuildDictionary()
'1. Declare the array of forms and the dictionary of (Key,Values) of type (Form,PictureBox)
Dim Forms As New List(Of Form) 'list of forms that you want to assign BG
Dim Pics As New Dictionary(Of Form, PictureBox) 'declare your dictionary list ("Key","Value") = (Form,Picturebox)
With Forms 'fill the list
.Add(Me)
.Add(Form1)
.Add(Form2)
.Add(Form3)
End With
'2. Snag the picturebox we want to assign to the forms
Dim PictureBoxToAssign As PictureBox = DirectCast(Me.Controls("PictureBox" + CStr(i + 1)), PictureBox) 'grab your picturebox
'3 option 1 (prefered)
'#### Either Do this next
Pics.Clear() 'if your logic allows that you can clear the dictionary first
For x As Integer = 0 To Forms.Count - 1 'for each form in the array
Pics.Add(Forms(x), PictureBoxToAssign) 'add the form and picturebox to the dictionary
Next
'3 option 2 (only if you cannot clear the dictionary)
'#### Or Do this
'you cannot clear the dictionary first due to logic
For x As Integer = 0 To Forms.Count - 1 'for each form in the array
'check existance of the pair
If Pics.Contains(New Generic.KeyValuePair(Of Form, PictureBox)(Forms(x), PictureBoxToAssign)) Then
Pics(Forms(x)) = PictureBoxToAssign 'update the value
Else
Pics.Add(Forms(x), PictureBoxToAssign) 'add the new pair
End If
Next
End Sub
If the objective is merely to set the background image of each form to the image of a picturebox, then perhaps a faster more efficient route might be the following example
Sub SetBG()
Dim Forms As Form() = {Me, Form1, Form2, Form3} 'array of forms that you want to assign BG, occupies less memory than a list
Dim PictureBoxToAssign As PictureBox = DirectCast(Me.Controls("PictureBox" + CStr(i + 1)), PictureBox) 'grab your picturebox
For x As Integer = 0 To Forms.Count - 1 'for each form in the array
Forms(x).BackgroundImage = PictureBoxToAssign.Image 'assign the image to the background
Next
End Sub
The important thing to remember here is that when changing the object in any way, while iterating through the collection of objects within the array, while possible, must NOT be done using a “for each obj as type in array of object” approach, ie not like this
For Each obj as ObjectType in ArrayofObjects
Change the properties of obj = (BIG NO CAN DO)
Next
you MUST separate the loop from interacting directly with the object in collection, in this example by using the for each of an index like so
For x As Integer = 0 To Forms.Count - 1 'using an integer not the object itself
'referencing the object through the index of the collection Forms(x)
Forms(x).BackgroundImage = PictureBoxToAssign.Image
Next
I hope this answer brings light to the darkness