I have many PictureBoxes with the names picturebox_D1, picturebox_D2... D30.
What I'd like to do is to change the images in those PictureBoxes, but in loop. Something like this, but working
for (int i = 0; i < 30; i++)
{
if (ReceivedDataTextBox.Text[i].ToString()=="1")
"pictureBox_D"+i.Image= new Bitmap(#"Pictures\\green.png");
else
"pictureBox_D"+i.Image= new Bitmap(#"Pictures\\red.png");
}
How can I do it?
You can use indexing of the parent container's controls to get a Control by name. For example: If your pictureboxes are just put straight on your form (i.e, not in a Panel, GroupBox or other container):
for(int i = 0; i < 30; i++)
{
((PictureBox)this.Controls["pictureBox_D" + i.ToString()]).Image = new Bitmap(#"Pictures\\green.png");
}
For nested PictureBoxes (i.e, ones within GroupBoxes or Panels), just take the same approach but on the parent container:
for(int i = 0; i < 30; i++)
{
((PictureBox)this.panel1.Controls["pictureBox_D" + i.ToString()]).Image = new Bitmap(#"Pictures\\green.png");
}
On the surface it may seem logical that by just typing in the string you should be able to reference variables, but once you start to delve deeper into C# you'll realize that this isn't really how things work in C#. The code you write into the IDE is compiled, and during that compilation, all the nice things about high level code (variable names, function names..etc) vanish; your "pictureBox_D" string is meaningless once the code is compiled and makes no sense to the compiler.
The best way to do this would be to create a PictureBox array and fill it with your pictureboxes like this:
PictureBox[] pictures = {picturebox_D1, picturebox_D2, ...};
then you can iterate over them
foreach(var p in pictures)
{
p.Image = new Bitmap(#"Pictures\\green.png");
}
Very simple solution:
PictureBox mybox = (PictureBox)this.Controls.Find("pictureBox2", true)[0];
In your constructor create and set a local Dictionary<string, PictureBox>. Remember, Forms are classes just like any other C# class and should be treated as such.
Instead of for, you can now use foreach and access the .Key and .Value to get the file names and PictureBox objects. This only works because you have such a rigid relationship between PictureBox name and desired picture, however. If the pairing changes, or you ever want to use the same image for two pictures boxes you'd want to change this to a List<Tuple<string, PictureBox>>. That may even be the best want to handle the situation, in fact.
public class Form1 : Form
{
private Dictionary<string, PictureBox> pictureBoxes;
public Form1()
{
pictureBoxes = new Dictionary<string, PictureBox>()
{
{"Pictures\\green.png", pictureBox_D},
{"Pictures\\blue.png", pictureBox_B},
// Etcetera
}
}
}
When you want to loop over them you do the following:
foreach(var kvp in pictureBoxes)
{
kvp.Value.Image = new Bitmap(kvp.Key);
}
I had to deal with a similar situation sometime ago where I had variable names var1, var2, ...
The best way I could find to get around having a monster switch case is to use reflection. Given the name of the picture box, you can easily find out the variable itself using reflection.
This should work:
// Assuming this is inside the form where the pictures boxes are hosted
for (int i = 0; i < 30; i++){
FieldInfo field = GetType().GetField("pictureBox_D"+i, flags);
PictureBox pictureBox = (PictureBox)field.GetValue(this);
if (ReceivedDataTextBox.Text[i].ToString()=="1")
pictureBox.Image= new Bitmap(#"Pictures\\green.png");
else
pictureBox.Image= new Bitmap(#"Pictures\\red.png");
}
You can use Controls.OfType<PictureBox>() combined with LINQ to get the items you want. In fact, you can probably transform them all into a collection you can use:
var picBoxes = this.Controls
.OfType<PictureBox>()
.Where(pb => pb.Name.StartsWith("pictureBox_D"))
.ToDictionary(pb => int.Parse(pb.Name.Replace("pictureBox_D", string.Empty)));
for(int i = 0; i < picBoxes.Length; i++)
{
// Do what you need with picBoxes[i]
}
The nice thing is that you can do this once in your constructor (after InitializeComponent is called) and you have this collection to reuse for the rest of the life of the form. This also ensures that should you add any additional PictureBoxes (that follow the same naming convention) you don't need to change anything.
Related
I have an array of buttons used to select items from an array.
What I'm trying to do is use one function to handle this, like the code below, instead of writing a lot of functions just doing a small job.
However, it seems all of those buttons are set to the last i and it gives an "array out of bound" exception every time I click on the button.
Is there any better way to do this?
I considered to search for the index of clicked button, but that feels weird to me and could be slow.
public Button[] MPS;
for(int i = 0; i < gm.MP.Length; i++)
{
MPS[i].onClick.AddListener(() => MPButtonHandle(i));
}
void MPButtonHandle(int i)
{
MP = gm.MP[i];
};
Basically you need to make a local copy of variable i:
public Button[] MPS;
for(int i = 0; i < gm.MP.Length; i++)
{
int j = i;
MPS[i].onClick.AddListener(() => MPButtonHandle(j));
}
void MPButtonHandle(int i)
{
MP = gm.MP[i];
};
The reason behind all of this is a mechanizm called closures. You can find more info about this here:c# closures
I'm currently working on a game (console application) with 25 Chunks, that are 5x5. All Chunks are in a List(5x5) witch is the Level in the end.
I do not want to declare all arrays. I would like to write a method in witch the arrays will be declared but with changing names.
For example:
- ac_Array_1
- ac_Array_2
static void Level()
{
List<char[,]> ol_Level = new List<char[,]>();
}
static void Spielblock()
{
int i_Stelle = 1;
string s_ArrayName = "ac_Chunk_" + i_Stelle;
i_Stelle++;
char[,] /*NAME*/ = new char[5, 5];
}
Try something like this:
int numOfLevels = 5;
Dictionary<string, char[,]> ol_Level = Enumerable
.Range(1, numOfLevels)
.ToDictionary(k => $"ac_Chunk_{k}", v => new char[5,5]);
ac_Chunk = ol_Level["ac_Chunk_1"];//char[5,5]
for (int i_Row = 0; i_Row < ac_Chunk.getLength(0); i_Row++)
{
for (int i_column = 0; i_column < ac_Chunk.getLength(1); i_column++)
{
ac_Chunk[i_Row, i_column] = '#';
}
}
...
levels:
ac_Chunk_1, ac_Chunk_2, ac_Chunk_3, ac_Chunk_4, ac_Chunk_5
n.b. using System.Linq and c# 6.0 $ interpolation
To have a dynamic variable name like you are requesting is not a simple thing to accomplish.
Generally, variable names are known at compile time, and the compiler can make optimizations using that information. What you are requesting would keep that from happening.
So the suggestions that you are seeing: create a variable, such as a dictionary, known when compiling and writing the code. Make that variable one that can dynamically expand to contain as many "chunks" as you'd like. And with a Dictionary<string, char[,]> you can even give each of those chunks a name. They won't be individual variable names, but it will let you access them by string/name and iterate through the collection in different ways.
To add a detail to Johnny's answer, at any point you can use
var ac_chunk = ol_Level["ac_Chunk_1"];
if you want to repeatedly access an individual chunk.
Or, even easier, just keep using ol_Level[$"ac_Chunk_{chunkNumber}"]
I have a listbox, and a string array added to it
static sting[] demo = new string[] {"cat", "dog", "bird", "horse"};
public Form1()
{
InitializeComponent();
ListBox1.Items.AddRange(demo);
}
Then elsewhere I am trying to make a bool array based off of the listbox's selected items (it is multi select enabled)...
*the logic I am trying to do
//somewhere...
bool[] b = new bool[] { false, false, false, false};
//somewhere else
for(int i=0; i<4; i++)
{
b[i] = ListBox1.Items[i].IsSelected;
}
This does not work. I cannot access the .Items[i] properties and have only methods available (which for this case are pointless: Equals(), GetHashCode(), GetType(), ToString()).
Using a foreach loop for the selectedIndices will not work either as it will only give me the ones that are selected (and I need to iterate through each items value true|false).
I Tried adding the System.Windows.Controls.ListBoxItem but that did not work either...
System.Windows.Controls.ListBoxItem[] myItems = new System.Windows.Controls.ListBoxItem[4];
public Form1()
{
InitializeComponent();
for(int i = 0; i < 4; i++)
{
myItems[i] = new System.Windows.Controls.ListBoxItem();
myItems[i].Content = demo[i];
listbox1.Items.Add(myItems[i].Content);
}
}
This part goes through fine, but later when I try to cycle through the ListBoxItems it blows up throwing a string to ListBoxItem cast error (even though I added it as a ListBoxItem object???)
Again, logic I am trying* to do but not working
bool tmp;
foreach(System.Windows.Controls.ListBoxItem li in listbox1.Items)
{
tmp = li.IsSelected;
// do something
}
I assume that trying a for loop (which realistically I would need to use anyways to point to the bool array) would fail in the same manner. I don't need to use the ListBoxItem class (actually more references to add so I would rather not use it) but I thought it would work correctly. There has to be a more efficient way to do this. In addition I would also like to know why when I add a listboxitem object to the listbox, why it has a type conversion error when I try to cycle through it later.
You didn't tag your question, but it looks like you are mixing WPF classes with a WinForms project.
To mimic the IsSelected property, you can use something like this:
for (int i = 0; i < 4; i++) {
b[i] = listBox1.SelectedIndices.Contains(i);
}
have you tried something like this?
for(int i=0; i<4; i++)
{
if(ListBox1.Items[i].IsSelected)
b[i] = true;
}
I think the compiler is doesn't have a value for IsSelected() until it's run.
I am writing a C#/ASP.Net web application and I have a large number of text boxes that need to be set to variable values in the code behind. Currently I am doing the following:
AspTextBox0.Text = codeBehindVariable[0];
AspTextBox1.Text = codeBehindVariable[1];
AspTextBox2.Text = codeBehindVariable[2];
AspTextBox3.Text = codeBehindVariable[3];
…
Is there an easy way to do this in a simple “for” loop??
This is a very simplified example, the real program has a switch case and some other testing that needs to be performed at the time the variable is assigned. Therefore, a “for” loop would drastically simplify the writing and maintainability of the code. Back in the good-old-days of VB6 and control arrays this was a piece of cake.
The good old days of VB6 are long gone and better don't come back.
Create a control array or better a List<TextBox> yourself:
var textBoxes = new List<TextBox> {
AspTextBox0,
AspTextBox1,
// ...
};
Then Zip it with the codeBehindVariable:
textBoxes.Zip(codeBehindVariable,
(textBox, variable) => textBox.Text = variable);
Or, if you prefer a for loop:
for ( int i = 0; i < codeBehindVariable.Length; i++ )
{
textBoxes[i].Text = codeBehindVariable[i];
}
Keep in mind that in the for loop you will have to make sure that both textBoxes and codeBehindVariable have the same number of items (or make the loop run only over the shortest list's amount of entries). The Zip function will take care of this by itself.
Assuming you are in a class that implements System.Web.UI.Page you can try to look up the control to find the one you want like so:
for (int i = 0; i <= codeBehindVariable.Length; i++)
{
var textBox = FindControl("AspTextBox" + i, false);
if(textBox != null)
{
textBox.Text = codeBehindVariable[i];
}
}
you can do something like this...
int i = 0;
foreach (Control ctrl in this.Controls)
{
if (ctrl is TextBox)
{
TextBox tempTextBox = (TextBox)ctrl;
tempTextBox.Text = codeBehindVariable[i];
i++;
}
}
For example in Windows Form, you have textboxes textbox0 to textbox29 and you need to assign them all to an array. What I currently can think of is to do this:
array[0] = textbox0;
array[1] = textbox1;
...
array[29] = textbox29;
Is it possible for me to do something like this:
for(int i=0; i<30; i++)
{
array[i] = textbox + i;
//and some magic is done such tt this is a variable, eg. textbox1
}
this.Controls.OfType<TextBox>().ToArray()
should work. It selects the controls which are TextBox and then converts them to an array.
From the top of my head:
int i = 0;
foreach (Control c in FormX.Controls)
{
int i2;
if (c.Name.StartsWith("textbox") && int.TryParse(c.Name.Substring(7),out i2))
{
array[i] = c;
i++;
}
}
array = array.OrderBy(a => Convert.ToInt32(a.Name.Substring(7))).ToArray();
Well, you could use reflection... but personally I'd try to avoid creating all those separate variables to start with. For example, if you really need designer support, you could avoid creating separate variables but create the array by finding the controls by name.
Alternatively, if you can just autogenerate the controls programmatically in a loop, I'd do that.
I am modifying #rdkleine code
Control[] array = new Control[100];
foreach (Control c in FormX.Controls)
{
int index;
if (c.Name.StartsWith("textbox") && int.TryParse(c.Name.Substring(7),out index))
{
array[index] = c;
}
}
I think this should place the controls in the correct index in the array.