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