I have a method in a windows form application that tries to remove 2 text boxes from a panel.
In the method, I loop through all the controls in the panel. There are always supposed to be 2 panels removed and added together, but when removing, it randomly removes 1 or 2 containers when I press the button.
Here is the code to remove the textboxes:
private void removeRows()
{
string descName = "Desc" + (textBoxCounter - 1).ToString();
string costName = "Cost" + (textBoxCounter - 1).ToString();
if (textBoxCounter >= 0)
{
foreach (Control c in costItems.Controls)
{
if (c.Name == descName)
{
// Remove the control from the panel and dispose of it
panel.Controls.Remove(c);
c.Dispose();
}
if(c.Name == costName)
{
// Remove the control from the panel and dispose of it
panel.Controls.Remove(c);
c.Dispose();
}
}
// Decrement the counter
// This happens only once since two controls need to be removed
if (textBoxCounter == 0)
textBoxCounter = 0;
else
textBoxCounter--;
}
else
MessageBox.Show("There are no more rows to remove", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
testlabel1.Text = textBoxCounter.ToString();
testlabel2.Text = panel.Controls.Count.ToString();
}
Here is the code to add a button:
private void addRows(string desc, string cost)
{
if (textBoxCounter >= maxExpenses)
{
MessageBox.Show("Maximum number of expenses entered", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
TextBox Desc = new TextBox();
TextBox Cost = new TextBox();
// Give the text boxes names
Desc.Name = "Desc" + textBoxCounter.ToString();
Cost.Name = "Cost" + textBoxCounter.ToString();
// Format the text boxes
Desc.Width = panel.Width / 2;
Cost.Width = panel.Width / 4;
// Add the items to the costItems panel
panel.Controls.Add(expenseDesc);
panel.Controls.Add(expenseCost);
// Add the items to the expenses dictionary
panel.Add(Desc, Cost);
// Increment the text box counter variable
textBoxCounter++;
testlabel1.Text = textBoxCounter.ToString();
testlabel2.Text = costItems.Controls.Count.ToString();
}
}
Some info to know.
There will always be 2 textboxes added and removed, they relate to each other.
The textBoxCounter is initialized to 0, so the first two boxe names will be "Desc0" and "Cost0".
When I press the button to remove rows the first time, one text box is removed, and then if i press it again it might remove 2, it might only remove 1.
I tried debugging and I noticed that the foreach loop that iterates over all the controls in the panel seems to loop one time short of the full number of controls.
Any help with my code would be great.
Your problem is caused by the foreach, modifying the collection in foreach may cause some unexpected behavior. You just want to remove the TextBoxes with names being known beforehand, so why not using the method ControlCollection.RemoveByKey?
If you want to remove the last added textBoxes (Desc... and Cost...) do this:
panel.Controls.RemoveByKey(descName);
panel.Controls.RemoveByKey(costName);
If you want to remove all the added textBoxes (suppose you have other kinds of TextBoxes, otherwise we can use a little LINQ to remove all the textboxes easily):
for(int i = 0; i < textBoxCounter; i++){
panel.Controls.RemoveByKey("Desc" + i);
panel.Controls.RemoveByKey("Cost" + i);
}
Your code has two problems: you are disposing something you cannot dispose and you are iterating through a collection (which you are modifying) in the wrong way. You can delete all the Controls by doing:
panel.Controls.Clear();
Or iterating backwards by relying on the indices:
for (int i = panel.Controls.Count - 1; i >= 0; i--)
{
panel.Controls.RemoveAt(i);
}
Regarding the Dispose, you can use it if you wish but don't need to use Remove:
for (int i = panel.Controls.Count - 1; i >= 0; i--)
{
panel.Controls[i].Dispose();
}
PS: I asked something identical to this and got -6. One of the reasons for maintaining this question was precisely being helpful to others (I saw the code you are using to delete controls in internet and I knew that quite a few people were using it). Pretty ironical, indeed.
Related
I have a TabControl in my C# form. According to the program step I have to remove a range of the tabs programmaticaly. I do that currently in this way, but with changing conditions I have to remove the tabpages always one by one.
tabControl1.TabPages.Remove(tabPage5);
tabControl1.TabPages.Remove(tabPage6);
tabControl1.TabPages.Remove(tabPage7);
// remove other tabs
I want to be able to remove the tabs in a shorter way, in a loop something like that: Here Instead of the ??? I want to write the tabpage(n) indirectly with a variable. Is that possible?
for(int n=start; n<=stop; n++)
{
tabControl1.TabPages.Remove(???)
}
Is it not possible to define the "tabpagex" as a control and define
the control name dynamicaly?
Sure, you can SEARCH for controls "by name" using Controls.Find(name, true):
int start = 5;
int stop = 8;
for (int n = start; n <= stop; n++)
{
TabPage tb = this.Controls.Find("tabPage" + n, true).FirstOrDefault() as TabPage;
if (tb != null)
{
tabControl1.Controls.Remove(tb);
}
}
I've been working on a project where I need on a button press that this line gets executed.
if (listView1.SelectedItems[0].SubItems[3].Text == "0") //Checks to see Value
{
listView1.SelectedItems[0].SubItems[3].Text = "1";// If Value is Greater, Increase and Change ListView
questionNumberLabel.Text = listView1.SelectedItems[0].SubItems[3].Text;// Increase and Change Label
}
Now I have this repeated about 10 times with each value increasing by one. But I know that this is ugly, and dysfunctional. As well as conflates the file size. I've tried a few things. Primarily this method.
if (listView1.SelectedItems[0].SubItems[3].Text == "0")
{
for (var i = 1; i < 100;)
{
if (!Int32.TryParse(listView1.SelectedItems[0].SubItems[3].Text, out i))
{
i = 0;
}
i++;
listView1.SelectedItems[0].SubItems[3].Text = i.ToString();
Console.WriteLine(i);
}
}
But instead of just adding one, it does the 100 instances and ends. The reason this is becoming a pain in the *** is because the
listView1.SelectedItems[0].SubItems[3].Text
is just that - it's a string, not an int. That's why I parsed it and tried to run it like that. But it still isn't having the out come I want.
I've also tried this
string listViewItemToChange = listView1.SelectedItems[0].SubItems[3].Text;
Then parsing the string, to make it prettier. It worked like it did before, but still hasn't given me the outcome I want. Which to reiterate is, I'm wanting the String taken from the list view to be changed into an int, used in the for loop, add 1, then restring it and output it on my listView.
Please help :(
You say you want the text from a listview subitem converted to an int which is then used in a loop
so - first your creating your loop variable, i, then in your loop you're assigning to it potentially 3 different values 2 of which are negated by the, i++. None of it makes sense and you shouldn't be manipulating your loop variable like that (unless understand what you're doing).
if you move statements around a little..
int itemsToCheck = 10; // "Now I have this repeated about 10 times "
for (var item = 0; item < itemsToCheck; item++)
{
int i;
if (!Int32.TryParse(listView1.SelectedItems[item].SubItems[3].Text, out i))
{
i = 0;
}
i++;
listView1.SelectedItems[item].SubItems[3].Text = i.ToString();
Console.WriteLine(i);
}
Something along those lines is what you're looking for. I haven't changed what your code does with i, just added a loop count itemsToCheck and used a different loop variable so your loop variable and parsed value are not one in the same which will likely be buggy.
Maybe this give you an idea. You can start using this syntax from C# 7.0
var s = listView1.SelectedItems[0].SubItems[3].Text;
var isNumeric = int.TryParse(s, out int n);
if(isNumeric is true && n > 0){
questionNumberLabel.Text = s;
}
to shortcut more
var s = listView1.SelectedItems[0].SubItems[3].Text;
if(int.TryParse(s, out int n) && n > 0){
questionNumberLabel.Text = s;
}
I have a multiple text boxes that I want to assign their string content to a variable however I'm not sure how to increment the text boxes. They are named, tbVarName1, tbVarname2, et cetera. Below is the for loop I have, right now I just have tbVarName1 hard coded in.
I have researched some of what other people have done and have only found tips for doing it in VB.
for(seriesIndex = 1; seriesIndex <= 4; seriesIndex++)
{
dataChart.Series["Variable " + seriesIndex].LegendText = tbVarName1.Text
}
At the end of this I would like the the legends to be updated to what's in the text boxes
Another way to do it is using the Controls collection from the Form (assuming that all TextBoxes are direct children of the form)
var ctrl = this.Controls.OfType<TextBox>();
for(seriesIndex = 1; seriesIndex <= 4; seriesIndex++)
{
TextBox t = ctrl.FirstOrDefault(c => c.Name == "tbVarName" + i);
if(t != null) dataChart.Series["Variable " + seriesIndex].LegendText = t.Text;
}
This will not require an array but you could end with a bigger loop if you have many controls of type textbox and it is not worth the effort if the TextBox are children of different containers (panels, groupboxes)
There are various ways to do this, one way is to add the controls into an array, for example:
var controls = new [] { tbVarName1, tbVarName2, tbVarName3 };
And now you can access them by index:
for(seriesIndex = 1; seriesIndex <= 4; seriesIndex++)
{
dataChart.Series["Variable " + seriesIndex].LegendText = controls[seriesIndex - 1].Text;
// ^^^^^^^^^^^^^^^^^^^^^^^^^
// Like this
// Note: arrays start at zero
}
I have, in a form, consecutive textboxes and labels named tb1,tb2,tb3... and label1,label2,label3....
I have a dictionary holding number of Key value pairs.
How to populate the labels and textboxes corresponding to the value pairs in the dictionary?
Eg: dic.Key[1] -> label1 and dic.value[1] to tb1... like that.
I don't get any idea to try this.
In the other answer it is suggested to create a collection of labels and textboxes. My concern with that approach is that a developer may forget to do that or the order may get changed.
Every control has the Name property which stores that control's name. This property is set by Visual Studio. In your code, if you are not playing (read changing) with the Name property of controls, you can use the below code to achieve what you wanted to.
for(int i = 0; i < dic.Count; i++)
{
// As Control.Find returns an array of controls whose name match the specified string,
// in this example I had picked the first control
// you can make it more robust by checking
// - the number of controls returned,
// - the type of control, etc
TextBox txt = (TextBox) this.Controls.Find("tb" + (i + 1).ToString(), true)[0];
Label lbl = (Label) this.Controls.Find("label" + (i + 1).ToString(), true)[0];
txt.Text = dic[i].Value;
lbl.Text = dic[i].Key;
}
Your best bet would simply be to initialize a List (or two) in your form's constructor, putting all your labels and text boxes inside, so you can check them while looping through your dictionary.
private List<Label> labels = new List<Label>();
private List<TextBox> textBoxes = new List<TextBox>();
public MyForm()
{
labels.Add(myLabel1);
labels.Add(myLabel2);
labels.Add(myLabel3);
textBoxes.Add(myTB1);
textBoxes.Add(myTB2);
textBoxes.Add(myTB3);
}
private void addValuesFromDictionary(Dictionary<string, string> dic)
{
for (int i = 0; i < dic.Count; i++)
{
labels[i].Text = dic[i].Key;
textBoxes[i].Text = dic[i].Value;
}
}
I created a dynamic UI that shows each module that the user has added to their selection and all the assessments for that module underneath the module name,
All data is saved in array lists - including objects as textBoxes and labels.
Here's what it looks like:
http://www.4shared.com/photo/fullsize/Ja3jHpRJ/screen1.html
Then the user should enter their marks for each assessments
And the Calculate button should calculate their overall mark for each module
At this stage I am just adding the marks together.
The program works fine when there's only one module added, but when there are more modules it seems to think that a TextBox is empty, even though i fill them all with numbers.
here is my code
private void calculateButton_Click(object sender, EventArgs e)
{
int modLength = dat.ModuleTitle.Count;
int modAsses = 0;
int loopCount = 0;
for (int i = 0; i < modLength; i++)
{
int total = 0;
if ((textboxComputer[loopCount] as TextBox).Text == "")
{
Console.WriteLine("!!!!!");
}
else
{
modAsses = int.Parse(dat.ModuleAssessNum[i].ToString());
for (int y = 0; y < modAsses; y++)
{
total = total + int.Parse((textboxComputer[loopCount] as TextBox).Text.ToString());
loopCount = loopCount + 1;
}
Console.WriteLine(total);
}
}
}
When i add two or more modules it displays the "!!!!!" message, but calculates the mark for the first module added.
here's some output
http://www.4shared.com/photo/fullsize/PlY29BMQ/screen2.html
You need to make sure that the code that creates the extra dynamic elements on screen is being called as part of the page load, so that then the viewstate is inserted back into the elements and can be read back out by your code.
there is something wrong with your code, probably a lot wrong(except the names of your variables).
Take a look at your loopCount variable - you will only conditionally change it in your loop but you always use it at the start of the loop(where you do textboxComputer[loopCount]).
So what's the expected behavior of that? The first time you get in to the loop if you go to your 'else' part you may print the total as you say but you also change the loopCount variable, once you get to the Console.WriteLine("!!!!!"); you never change it again - so your iterations will behave the same.