Hi,

I am trying to add multiple PictureBoxes dynamically at runtime using a loop but have come a little stuck.

Here is the code I am working on:-

XmlDocument xDoc = new XmlDocument();
xDoc.Load(System.IO.Path.Combine(Application.StartupPath, "getPublicPhotos.xml"));	

XmlNodeList photo = xDoc.GetElementsByTagName("photo");

for (int i = 0; i < photo.Count; ++ i)
			                    
{PictureBox[] pb = new PictureBox[i];

pb[i].Location = new Point(5, (i * 80) + 5);

pb[i].Size = new Size(75, 75);

pb[i].BorderStyle = BorderStyle.Fixed3D;

//pb.ImageLocation = "";

pb[i].Cursor = System.Windows.Forms.Cursors.Hand;

panel1.Controls.Add(pb[i]);

pb[i].BringToFront();

pb[i].Click += delegate(object s, EventArgs e2)
{
System.Diagnostics.Process.Start("");
};
}

When I try to run this I get an - IndexOutOfRangeException: Idex was outside the bounds of the array.

Any pointers in the right direction would be appreciated.

Kind regards..,

MT ;)

Look at this line: for (int i = 0; i < photo.Count; ++ i) It should likely be i++ as everything I can think of in the CLR uses 0-based indexes. In the code you provided your first iteration would start at "1" then end at (photo.Count+1) giving you an argument out of range exception.

Something like this would also work:

//Get test data
    private static Image[] GetImages()
    {
      return new Image[] {
        SystemIcons.Application.ToBitmap(),
        SystemIcons.Asterisk.ToBitmap(),
        SystemIcons.Error.ToBitmap(),
        SystemIcons.Exclamation.ToBitmap()
      };
    }

    private void button2_Click(object sender, EventArgs e)
    {
      List<Image> pictures = new List<Image>(GetImages());

      List<PictureBox> lst = new List<PictureBox>();
      for (int i = 0; i < pictures.Count; i++)
      {
        PictureBox pb = new PictureBox()
        {
          Size = new Size(75, 75),
          Location = new Point(5, (i * 80) + 5),
          BorderStyle = BorderStyle.Fixed3D,
          Image=pictures[i]
        };
        lst.Add(pb);
        this.Controls.Add(pb);
        pb.BringToFront();
        pb.Click += new EventHandler(pb_Click);
      }
    }

Edited 6 Years Ago by sknake: bug

Comments
Great post as always

Look at this line: for (int i = 0; i < photo.Count; ++ i) It should likely be i++ as everything I can think of in the CLR uses 0-based indexes. In the code you provided your first iteration would start at "1" then end at (photo.Count+1) giving you an argument out of range exception.

Something like this would also work:

//Get test data
    private static Image[] GetImages()
    {
      return new Image[] {
        SystemIcons.Application.ToBitmap(),
        SystemIcons.Asterisk.ToBitmap(),
        SystemIcons.Error.ToBitmap(),
        SystemIcons.Exclamation.ToBitmap()
      };
    }

    private void button2_Click(object sender, EventArgs e)
    {
      List<Image> pictures = new List<Image>(GetImages());

      List<PictureBox> lst = new List<PictureBox>();
      for (int i = 0; i < pictures.Count; i++)
      {
        PictureBox pb = new PictureBox()
        {
          Size = new Size(75, 75),
          Location = new Point(5, (i * 80) + 5),
          BorderStyle = BorderStyle.Fixed3D,
          Image=pictures[i]
        };
        lst.Add(pb);
        this.Controls.Add(pb);
        pb.BringToFront();
        pb.Click += new EventHandler(pb_Click);
      }
    }

Hi,

thanks for replying.

I had a look at the line you pointed out and tried it as for (int i = 0; i < photo.Count; i++) but still had the same exception.

I have used this same loop to fill a listbox and that worked fine, so I thought it would here also.

I need to use this loop as it reads the lines from an XML file to create the number of required PictureBoxes and will also be used when I further develop my app to read extra data from the same XML file to add information about each image that will populate the created PictureBoxes.

I assumed that if I could use the same loop over and over it would make my coding that much easier and keep things all relative to each other.

Kind regards..,

MT ;)

You're also instantiating a new array every time your loop iterates:

PictureBox[] pb = new PictureBox[i];

I also don't see where you are instantiating the picture boxes. It seems like that array would be all nulls. Can you post the code for that entire unit?

Edited 6 Years Ago by sknake: n/a

You're also instantiating a new array every time your loop iterates:

PictureBox[] pb = new PictureBox[i];

I also don't see where you are instantiating the picture boxes. It seems like that array would be all nulls. Can you post the code for that entire unit?

Sure, here is the full form code, less for security I have * out some links.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Net;
using System.IO;
using System.Xml;

namespace Panel_Display
{
	/// <summary>
	/// Description of MainForm.
	/// </summary>
	public partial class MainForm : Form
	{
		public MainForm()
		{
			//
			// The InitializeComponent() call is required for Windows Forms designer support.
			//
			InitializeComponent();
			
			
			
			//
			// TODO: Add constructor code after the InitializeComponent() call.
			//
		}
		
		void MainFormLoad(object sender, EventArgs e)
		{
			
			WebClient Client = new WebClient ();
			Client.DownloadFile("*****", (System.IO.Path.Combine(Application.StartupPath, "getPublicPhotos.xml")));
			                    
			                    
XmlDocument xDoc = new XmlDocument();
xDoc.Load(System.IO.Path.Combine(Application.StartupPath, "getPublicPhotos.xml"));	

XmlNodeList photo = xDoc.GetElementsByTagName("photo");

for (int i = 0; i < photo.Count; ++ i)
			                    
{PictureBox[] pb = new PictureBox[i];

pb[i].Location = new Point(5, (i * 80) + 5);

pb[i].Size = new Size(75, 75);

pb[i].BorderStyle = BorderStyle.Fixed3D;

//pb.ImageLocation = "*****";

pb[i].Cursor = System.Windows.Forms.Cursors.Hand;

panel1.Controls.Add(pb[i]);

pb[i].BringToFront();

pb[i].Click += delegate(object s, EventArgs e2)
{
System.Diagnostics.Process.Start("*****");
};
}
}
	}}

Regards..,

MT ;)

Ahhh.

for (int i = 0; i < photo.Count; ++i)
      {
        PictureBox[] pb = new PictureBox[i];

On the first iteration you're creating an array with 0 length. Thus when you call pb its outside the bounds.

I dont understand why you are instantiating an array inside of the loop like that, when not using more than 1 element. Perhaps you mean something like this:

PictureBox[] pb = new PictureBox[photo.Count];
      for (int i = 0; i < photo.Count; i++)
      {
        pb[i] = new PictureBox();

Note -- you should still use i++ .

[edit]
Wait .. scratch that. Since you're using ++counter on the first iteration you are creating an array of 1. However you access arrays with a 0-based index. So it would effectively be doing this:

PictureBox[] array = new PictureBox[1];
array[1] = something; //This is the problem. It should be accessing array[0]

[/edit]

Edited 6 Years Ago by sknake: n/a

Ahhh.

for (int i = 0; i < photo.Count; ++i)
      {
        PictureBox[] pb = new PictureBox[i];

On the first iteration you're creating an array with 0 length. Thus when you call pb its outside the bounds.

I dont understand why you are instantiating an array inside of the loop like that, when not using more than 1 element. Perhaps you mean something like this:

PictureBox[] pb = new PictureBox[photo.Count];
      for (int i = 0; i < photo.Count; i++)
      {
        pb[i] = new PictureBox();

Note -- you should still use i++ .

[edit]
Wait .. scratch that. Since you're using ++counter on the first iteration you are creating an array of 1. However you access arrays with a 0-based index. So it would effectively be doing this:

PictureBox[] array = new PictureBox[1];
array[1] = something; //This is the problem. It should be accessing array[0]

[/edit]

Thanks for your help but I am going to have to rethink this 'cos I am lost.

I have got the correct amount of PictureBoxes showing using:-

for(int i = 0; i < photo.Count; ++i)
      {
      	PictureBox pb = new PictureBox();

      	pb.Location = new Point(5, (i * 80) + 5);

      	pb.Size = new Size(75, 75);

      	pb.BorderStyle = BorderStyle.Fixed3D;

      	pb.ImageLocation = "";

      	pb.Cursor = System.Windows.Forms.Cursors.Hand;

      	panel1.Controls.Add(pb);

      	pb.BringToFront();

      	pb.Click += delegate(object s, EventArgs e2)
{
      		System.Diagnostics.Process.Start("http://www.example.com/index"+i+".htm");
};
}

but if you see the click event, I thought this would also open a different webpage for each image, it doesn't, they all open the same webpage.

From the beginning, here is what I am trying to do.

The form loads and reads an XML file.

It gets the number of lines in that XML file with the TAG photo.

Then the correct number of PictureBoxes are created, each one corresponds to one line in the XML file, i.e. 10 lines in the XML file = 10 PictureBoxes, this number can vary so that is why I am trying to code this dynamically.

Each PictureBox is loaded with a separate image based on data held within the XML file, i.e. line 0 of the XML file has the string information for the image for the 1st PictureBox and so on.

Each PictureBox is populated with a separate Click event based on data held within the XML file, i.e. line 0 of the XML file has the string information for the link for the 1st PictureBox and so on.

Really this app is an dynamically created thumbnail image gallery.

I don't know what is happening with this issue with index[0] etc..

I have created another small test app just to read the XML file and output the required data to a ListBox and it worked a treat, the first line of the XML file is [0].

void Button2Click(object sender, EventArgs e)
		{
	    XmlDocument xDoc = new XmlDocument();
            xDoc.Load(System.IO.Path.Combine(Application.StartupPath, "api.xml"));

            XmlNodeList photo = xDoc.GetElementsByTagName("photo");
            for ( int _id = 0; _id < photo.Count;  _id ++)
            
            {string my_img = "http://www.flickr.com/photos/" + photo[_id].Attributes["owner"].Value + "/" + photo[_id].Attributes["id"].Value + "/";
            
            listBox1.Items.Add(my_img);
            
           }
            listBox1.Click += delegate(object s, EventArgs e2)
           {
            System.Diagnostics.Process.Start(listBox1.Text);
           };
		}

each line of the ListBox contains a different link and each link when clicked goes to it's own webpage.

So I am just trying to achieve this same procedure but populate dynamically created PictureBoxes instead.

I am totally lost and have got hardly any hair left to pull out.

Kind regards..,

MT ;)

Got it figured out.

for(int i = 0; i < photo.Count; i ++)
      {
      	string img_link = "http://www.flickr.com/photos/" + photo[i].Attributes["owner"].Value + "/" + photo[i].Attributes["id"].Value + "/";
      	
      	string img_thu = "http://farm" + photo[i].Attributes["farm"].Value + ".static.flickr.com/" + photo[i].Attributes["server"].Value + "/" + photo[i].Attributes["id"].Value + "_" + photo[i].Attributes["secret"].Value + "_s.jpg";
      	
      	PictureBox pb = new PictureBox();

      	pb.Location = new Point(5, (i * 80) + 5);

      	pb.Size = new Size(75, 75);

      	pb.BorderStyle = BorderStyle.Fixed3D;

      	pb.ImageLocation = img_thu;

      	pb.Cursor = System.Windows.Forms.Cursors.Hand;

      	panel1.Controls.Add(pb);

      	pb.BringToFront();

      	pb.Click += delegate(object s, EventArgs e2)
{
      		System.Diagnostics.Process.Start(img_link);
};
}

All thumbnails load correctly and have their correct links.

Regards..,

MT :)

Good work! :)

Please mark this thread as solved if you have found an answer to your question and good luck!

Hi there, this is a notification to the guys who that that ++i would start at "1" whereas i++ would start at 0.

This is an incorrect assertion. The difference between ++i and i++ you are correct to a degree. ++i does addition before assignment and i++ does assignment before addition.
However, only the variable i is being assigned to. This means that the order of operation is not important.

Here is an example for loop:

for(int i = 0; i < 10; i++)
{
    Console.WriteLine(i);
}

Let's take the first line. On the first iteration the first argument is read. "int i = 0"
This creates the variable i of int type and assigns it to 0. It then reads the second argument, which is an if statement (effectively). If i is less than 10, perform an iteration, otherwise exit the loop.

The third argument is what should happen at the end of an iteration.

1. How should I begin?
2. When should I continue?
3. What should I do at the end of an iteration?

So back to the difference in ++i and i++. In this case there is none.

int i = 10;
int j = 10;
i = i++;
j = ++j;

In this case. If you printed out "i" it would read "10". If you printed out "j" it would read "11"

In self assignment (which a for loop uses)
i++ and ++i give the same result.

N.B (The only time I've found it makes a difference is in C++ when using Vectors. ++iterator is faster than iterator++)

This question has already been answered. Start a new discussion instead.