Search code examples
c#loopsrefactoringpictureboxloadimage

How to load multiple images into picture boxes using a loop without repeating code?


I want to use the incrementing of the for loop to access different picture boxes and images in the resource folder. I could type it all out as I have shown below but I would like to "program" it more. (Hope my question makes sense)

private void btnUse19_Click(object sender, EventArgs e)
{
    string applicationDirectory = Application.ExecutablePath;

    //Using my student number as an index to get the file path of the application.
    string editedApplicationDirectory = applicationDirectory.Substring(0, applicationDirectory.IndexOf("19001700"));

    for (int i = 1; i <= 10; i++)
    {
        string spineNumber = i.ToString();

        string pbSpineNum = "pbSpine" + spineNumber;
        string picNameNum = "Spine " + spineNumber + ".png";

        //this is what im trying to do but does not work
        pbSpineNum.Image = Image.FromFile(editedApplicationDirectory + "19001700" + "\\Resources\\"+picNameNum);
    }

    //Dont want to have to do this
    pbSpine1.Image = Image.FromFile(editedApplicationDirectory + "19001700" + "\\Resources\\Spine 1.png");
    pbSpine2.Image = Image.FromFile(editedApplicationDirectory + "19001700" + "\\Resources\\Spine 2.png");
    pbSpine3.Image = Image.FromFile(editedApplicationDirectory + "19001700" + "\\Resources\\Spine 3.png");
    pbSpine4.Image = Image.FromFile(editedApplicationDirectory + "19001700" + "\\Resources\\Spine 4.png");
    pbSpine5.Image = Image.FromFile(editedApplicationDirectory + "19001700" + "\\Resources\\Spine 5.png");
    pbSpine6.Image = Image.FromFile(editedApplicationDirectory + "19001700" + "\\Resources\\Spine 6.png");
    pbSpine7.Image = Image.FromFile(editedApplicationDirectory + "19001700" + "\\Resources\\Spine 7.png");
    pbSpine8.Image = Image.FromFile(editedApplicationDirectory + "19001700" + "\\Resources\\Spine 8.png");
    pbSpine9.Image = Image.FromFile(editedApplicationDirectory + "19001700" + "\\Resources\\Spine 9.png");
    pbSpine10.Image = Image.FromFile(editedApplicationDirectory + "19001700" + "\\Resources\\Spine 10.png");
}

Solution

  • You can use a list of picture boxes and the counter to refactor and reduce all to one line moved in the loop:

    using System.IO;
    using System.Collections.Generic;
    
    var pictureBoxes = new List<PictureBox>();
    
    string pathExe = Path.GetDirectoryName(Application.ExecutablePath);
    string pathImagesPattern = Path.Combine(pathExe, "Resources", "Spine {0}.png");
    
    for ( int index = 1; index <= 10; index++ )
    {
      var pictureBox = new PictureBox();
      string pathImage = string.Format(pathImagesPattern, index);
      pictureBox.Image = Image.FromFile(pathImage);
      pictureBoxes.Add(pictureBox);
    }
    

    I changed names to be more talking and clean, as well as the path according to what I seem to have understood that you were doing.

    Now you can use the list to process items like adding them in a panel, or the form:

    MyPanel.Controls.AddRange(pictureBoxes.ToArray());
    

    And you can access them like that:

    pictureBoxes[0].Location = ...
    

    But you should initialize each control in the loop using certain calculated variables for rendering:

    pictureBox.Location = new Point(posX, posY);
    

    You might need to promote this list as a class data member if you plan to use it outside of the method:

    private List<PictureBox> PictureBoxes = new List<PictureBox>();
    

    Another improvement for a cleaner code will be to use a constant or a variable instead of the literal in the loop itself:

    private int PictureBoxesCount = 10;
    
    PictureBoxes.Clear();
    for ( int index = 1; index <= PictureBoxesCount; index++ )
    

    If this number is fixed, you can use a const and also a fixed array instead of a List:

    const private int PictureBoxCount = 10;
    
    private PictureBox[] PictureBoxes = new PictureBox[PictureBoxCount];
    
    for ( int index = 0; index < PictureBoxes.Length; index++ )
    {
      string pathImage = string.Format(pathImagesPattern, index);
      PictureBoxes[index] = new PictureBox();
      PictureBoxes[index].Image = Image.FromFile(pathImage);
    }
    

    If the picture boxes are created using the Visual Studio Form Designer and are already created, you can simply use this preallocated array:

      PictureBoxes = Controls.OfType<PictureBox>()
                             .Where(p => p.Name.StartsWith("pbSpine"))
                             .OrderBy(p => p.Name.Length)
                             .ThenBy(p => p.Name)
                             .ToArray();
    
      for ( int index = 0; index < PictureBoxes.Length; index++ )
      {
        string pathImage = string.Format(pathImagesPattern, index);
        PictureBoxes[index].Image = Image.FromFile(pathImage);
      }
    

    But unlike the fully dynamic version, here we may encounter matching problems between the indexes of controls and images.

    I therefore do not recommend to use this with controls already placed on the form unless we know what we are doing and that we control the process in a robust way.