Accessing ListBox.Item[i] IsSelected property - c#

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.

Related

How can i set different values to buttons and pass it to listened functions?

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

IndexOutOfRange when it's clearly not

Sorry the title isn't really describing the issue, but it's a very weird one.
To make sure I wasn't doing any idiotic mistakes, I use breakpoints to track everything that was happening..
Basically, I have this code in a class which inherits from ObservableCollection<T>:
var n = new MyClass();
int startIndex = 0; // parameter
int length = 2; // parameter
for (int i = 0; i < length; i++)
{
n.Text += this[startIndex].Text;
this.RemoveAt(startIndex);
}
this.Insert(n);
When executing the code, my collection has 3 items ; the loop goes like this:
n.Text += "some string successfully gotten from this[startIndex]"
this.RemoveAt(startIndex)
n.Text += "some other string successfully gotten from this[startIndex]"
Exception: IndexOutOfRange.
I'm successfully getting the item, and yet there is an error when I'm trying to delete it. I'm lost here.
Any help would be very appreciated!
Thank you in advance.
EDIT 1
I've tried this, and had the same result.
var toRemove = this.Skip(startIndex).Take(length).ToList();
foreach (var b in toRemove)
{
this.Remove(b);
n.Text += b.Text;
}
Once again, I have an IndexOutOfRange Exception when Removing an item.
Whilst debugging, my Collection has 2 items, and RemoveAt(0) still throws this Exception.
EDIT 2
I tried to manually call OnCollectionChanged when modifying this.Items. The IndexOutOfRange Exception is triggered when calling OnCollectionChanged, but not when removing the item from this.Items.
for (int i = 0; i < length; i++)
{
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, Items[startIndex], startIndex));
Items.RemoveAt(startIndex);
}
After calling this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)), I also have a problem.
Looks like the whole problem comes from the ListBox. I'll try playing with the Bindings, and other things, and report.
I guess the problem is about you´re referecing a removed index here:
var toRemove = this.Skip(startIndex).Take(length).ToList();
foreach (var b in toRemove)
{
this.Remove(b); <<< removed
n.Text += b.Text; <<< referencing the removed
}
This way, obviously you have the error described. Invert the order from n.text and the remove.
I wouldn't recommend to write this.RemoveAt(startIndex); in the loop, sometimes it's not ok when observable collection enumeration changes inside the loop, so I would get it out of the loop definitely
Seems like you've realized your own realization of Insert for Obs.Coll. maybe the issue is there
UPD
I think your problem in bad design. you said your class inherits from observable collection, what is this then ? try better design and define method outside of collection class, as it pretends that your collection is 3 elems at least ALL THE TIME
UPD 2
Design is still ugly, but in case you want to stick with it, here what I did and it worked :
Your collection definition (NOT RECOMMEND DOING IT THIS WAY)
public class MyObs : ObservableCollection<MyClass> {
public void Fun() {
var n = new MyClass();
int startIndex = 0; // parameter
int length = 2; // parameter
for (int i = 0; i < length; i++) {
n.Text += this [startIndex].Text;
this.RemoveAt(startIndex);
}
this.Insert(0,n); // PAY ATTENTION THAT I INSERT AT 0 !
}
}
public class MyClass {
public string Text { get; set; }
public override string ToString() {
return Text;
}
}
Then your XAML:
<ListBox ItemsSource="{Binding MyCollection}"/>
Then your declaration code:
public MyObs MyCollection { get; set; }
Then initialization and processing:
MyCollection = new MyObs();
MyCollection.Add(new MyClass() {Text = "Item 1"});
MyCollection.Add(new MyClass() { Text = "Item 2" });
MyCollection.Add(new MyClass() { Text = "Item 3" });
MyCollection.Fun();
seems like your problem in your Insert
I found the solution. I feel bad.
Basically, SelectionChanged was triggered everytime the ObservableCollection changed, trying to do some stuff and encountering an error. The thing is, nothing in the Stacktrace led to that idea.
Sorry for wasting your time (considering how the comments / answers were good), and keep going.

How can I compose variables names through loop in C#?

I have rewritten this question because not everyone understood. Hope it's ok, it's the same main problem.Very sorry
I have a winform with 15 progress bars called: "baraClasa1", "baraClasa2", "baraClasa3" ... "baraClasa15". I have to assign the .VALUE property (as in int) to all of them, from some database records. (The records access the different values from different time periods)
I was thinking that maybe it is possible to use a loop to assign the .Value property to all of them by doing something like:
for(int i=0; i<value; i++)
{
"baraClasa+i".Value = 20 + i;
}
Is it possible to compose the name of the variables like that?
I don't know much about dictionaries, lists but looking into. If nothing works il just do the ugly:
int value = 20;
baraClasa1 = value;
baraClasa2 = value +1;....
Thank you for all help
You have to do a little reflection.
public string variable0, variable1, variable2, variable3, variable4, variable5;
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 6; i++)
{
//pretending my variable names are variable1, variable2.. ("variable" is NOT an array! just the "assign" variable)
System.Reflection.FieldInfo info = this.GetType().GetField("variable" + i.ToString());
// replace "testing" with the value you want e.g. assign[i]
info.SetValue(this, "testing");
}
// Do something with your new values
}
No need to use reflection with the updated question. The control collection has a built in find for getting a control by the name string.
for (int i = 0; i < 6; i++)
{
ProgressBar bar = (ProgressBar)this.Controls["baraClasa" + i.ToString()];
bar.Value = 50;
}
This is a design problem. Create a collection for items with common use (like progress bars for that matter) and iterate over the collection to perform actions on them.
If these are prorgress bars you might want to use an event-driven design (another link) to update their progress, meaning that each time a bar has made some progress, the event for the progress will send an update only to that bar, and not iterate over the entire list.
You may want to read an introduction to event driven programming in C# before re-factoring your code.
It really isn't possible in C# to refer to local variables in a dynamic fashion as you are trying to do. Instead what you would do in C# is store the value in a dictionary where the key can be generated in a dynamic fashion.
For example let's say all of your variable1, variable2, ... variableN were of type int. Instead of
int variable1 = 0;
int variable2 = 0;
...
int variableN = 0;
You would instead do the following
Dictionary<string, int> map = new Dictionary<string, int>();
for (int i = 0; i < N; i++) {
map[i.ToString()] = 0;
}
If the values are a of a fixed number and always linear in progress it may make sense to use an array instead of a dictionary
int[] array = new int[N];
for (int i = 0; i < N; i++) {
array[i] = 0;
}
You can't do it that way. You need an array. Every time you notice yourself having a variable2, you need an array. You may not know it yet, but you do.
No, you can't do it in C#, it's syntactically impossible. But if you want access form controls which has different names like this you can do the following:
for(int i=0; i<20; i++)
{
var name = "variable" + i;
this.Controls[name].Text = "etc..." // here you can access your control
}
If you want to have names for your objects, use a dictionary:
Dictionary<string, type> myDict = new Dictionary<string, type>()
string naming = "MyPattern{0}";
for (int i = 0; i <value; i++) {
myDict.add(string.Format(naming, i.ToString()), assign[i]);
}
And then you can access them by doing, for example:
myDict["MyPattern1"]
However, I suggest you would be better off using a collection like a List or array.
Arrays, lists, dictionaries, hash maps... collections in general are what you would use here. For example, if you have a dictionary, then it consists of key/value pairs. So a dictionary might look like this:
var variable = new Dictionary<int, string>();
Where the int is the key for any given entry, and the string is the value. You'd assign values in something like this:
for(int i = 0; i < value; i++)
variable.Add(i, assign[i]);
Of course, since i is just an incrementing integer in this case (unless you have some other key in mind?), then it works just as well as an indexer on a list. Something like this:
var variable = new List<string>();
for (int i = 0; i < value; i++)
variable.Add(assign[i]);
In both cases, you'd access the assigned value later by referencing its key (in a dictionary) or its index (in a list, or any array):
var someOtherVariable = variable[x];
Where x is an integer value present in the dictionary's keys or in the array's size.
If you can put names of all variables in an array such as 'variable', and they are unique, you can try to use dictionary :
Dictionary<object, object> dictionary = new Dictionary<string, object>();
for(int i=0; i<value; i++)
{
dictionary.Add(variable[i], assign[i]);
}

Name of picturebox

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.

Possible to loop through a list of variables, with names differ by numbers only?

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.

Categories

Resources