How can I shorten this repetitive C# TextBox.Clear code? - c#

Been reading through the threads a lot recently as I am learning to code!
In essence all I know is self taught and very basic, so I would like to start becoming more professional
I would like to shorten this code down (in the simplest way possible) since I often end up with very repetitive code! Here is a primary example
private void button2_Click(object sender, EventArgs e)
{
textBox1.Clear();
textBox2.Clear();
textBox3.Clear();
textBox4.Clear();
textBox5.Clear();
textBox6.Clear();
textBox7.Clear();
textBox8.Clear();
textBox9.Clear();
textBox10.Clear();
textBox11.Clear();
textBox12.Clear();
}
For context I can't clear all textboxes on my form since textBox13 always has a useful value that I can't have deleted!
EDIT * textBoxes 1 to 6 are in groupBox1, and textBoxes 7-12 are in groupBox2. This appears to be significant; I was just using the groupbox tool to make the program clear!
If anyone can help I would be truly grateful! Please keep in mind I am still a coding novice, so some features I am unfamiliar with.
Here is an image of the program to help!

Assuming all your textboxes name start in the format you displayed, you could use this:
foreach (Control control in Controls)
{
if (control.Name.StartsWith("textBox") && !control.Name.EndsWith("13") && control.GetType() == typeof(TextBox))
{
TextBox textBox = (TextBox)control;
textBox.Clear();
}
}
EDIT
If you want it to work with group boxes use this code by calling ClearTextBoxes (just write "ClearTextBoxes();")
private void ClearTextBoxes()
{
foreach (Control control in Controls)
{
ClearTextBox(control);
}
}
private void ClearTextBox(Control control)
{
if (control.GetType() == typeof(TextBox))
{
if (control.Name.StartsWith("textBox") && !control.Name.EndsWith("13"))
{
TextBox textBox = (TextBox)control;
textBox.Clear();
}
}
else if (control.GetType() == typeof(GroupBox))
{
GroupBox groupBox = (GroupBox)control;
foreach (Control groupBoxControl in groupBox.Controls)
{
ClearTextBox(groupBoxControl);
}
}
}

The current setting doesn not allow you to clear everything quickly because each variable, even if they're all of the same type and with similar name, is a different entity and you cannot iterate through them.
Anyway, you could group them in an array and iterate through them like this:
// Fill this array in an Init function
YourType[] textBoxes;
private void button2_Click(object sender, EventArgs e)
{
// Iterate through the whole array except for the last element
for(int i=0; i<textBoxes.Length-1; i++)
{
textBoxes[i].Clear();
}
}

One possible solution would be store textboxes in list
List<TextBox> textBoxes
And then iterate through this collection using for or foreach loop and calling Clear() method.
Or use extension method ForEach()
textBoxes.ForEach(x => x.Clear());

The simplest way is to prepare a collection containing all of your textbox objects
var textBoxes = new List<TextBox> { textBox1,textBox2,textBox3};// fill in the rest
and just iterate with a 'foreach' on it
textBoxes.ForEach(textBox => textBox.Clear());
This is a simple suggestion.
You can also use reflection to make it more implicit but it doesn't worth it

Usually having too many of certain control can be a sign that you can consider a different type of control, but that doesn't seem to be the case based on the screenshot. You can use array:
foreach (var t in new[] { textBox1, textBox2, textBox3 })
t.Clear();
If the controls are directly in the form then something like:
foreach (Control c in this.Controls)
if (c is TextBox && c.Name != "textBox13")
c.Text = ""; // .Text = "" is what TextBox.Clear() does
but because the controls are inside other controls:
foreach (Control c in this.groupBox1.Controls) if (c is TextBox) c.Text = "";
foreach (Control c in this.groupBox2.Controls) if (c is TextBox) c.Text = "";
or:
foreach (var gb in new[] { groupBox1, groupBox2 })
foreach (Control c in gb.Controls)
if (c is TextBox) c.Text = "";

Related

Iterating through checkboxes inside different panels that are inside a parent panel

So I am making a "Disable all checked checkboxes" button for my windows form application in c#. The code I have works fine when set to loop through a specific panel, like so:
private void LockChecked_Click(object sender, EventArgs e)
{
foreach (Control c in block1Panel.Controls)
{
if (c is CheckBox)
{
CheckBox cb = (CheckBox)c;
if (cb.Checked == true)
{
cb.Enabled = false;
}
}
}
}
But what I'd like to do is loop through all the block panels (block1Panel, block2Panel, block3Panel, etc.) that are inside a main panel (Assignments_Panel).
So, how can I iterate through all the checkboxes from all panels, without having to write a loop for each panel? I know it's possible, but since I'm only a beginner I'm not able to figure this one out, even after hours of searching...
Thank you in advance! And if anything in my question is unclear please say so, so I can explain further!
This method may help. It loops through each control from a parent control, which in your case looks as tough it'd be Assignments_Panel, then for each control that belongs to the parent control, it will either loop through all child controls again, or disable the control, if it is a checkbox.
private void DisableCheckboxes(Control parentControl)
{
foreach (Control childControl in parentControl.Controls)
{
if (childControl is Panel childPanel)
{
DisableCheckboxes(childPanel);
}
else if (childControl is CheckBox childCheckBox)
{
childCheckBox.Enabled = false;
}
}
}
Well, we can enumerate all the controls within form while checking if the control is of type CheckBox; there are many implmentations for this, say
How to get ALL child controls of a Windows Forms form of a specific type (Button/Textbox)?
let's write good old Bread First Search (no Linq, recursion etc.)
private void LockChecked_Click(object sender, EventArgs e) {
Queue<Control> agenda = new Queue<Control>(new [] { this });
while (agenda.Count > 0) {
Control control = agenda.Dequeue();
if (control is CheckBox cb && cb.Checked)
cb.Enabled = false;
foreach (Control child in control.Controls)
agenda.Enqueue(child);
}
}

Remove() function seems only removes a couple of controls at a time

I'm trying to remove multiple controls at a time (textboxes and checkboxes) in a windows form application. Basically, I have a row of textboxes and a corresponding checkbox. When the "delete rows" button is clicked, it should remove all rows that have been selected. But it only seems to remove two or three at a time (the same two or three in each row, but there doesn't seem to be any reason it selects those same two or three). I've attached a couple of screenshots showing what is happening.
Here I've selected a couple of rows:
After hitting delete once:
After hitting delete twice:
This just shows the names of each element. As you can see, the names are the same in each row:
Here is the relevant code:
//Gets a list of all ticked checkboxes
public List<string> checkForChecked()
{
var allCheckboxes = tabPage1.GetAllControlsOfType<CheckBox>();
int count = allCheckboxes.Count<CheckBox>();
List<string> checkedChecks = new List<string>();
foreach(Control c in tabPage1.Controls)
{
if(c is CheckBox && ((CheckBox)c).Checked)
{
checkedChecks.Add(c.Name.ToString());
}
}
return checkedChecks;
}
//The button click. Loop through elements and remove ones with the right name
private void button2_Click(object sender, System.EventArgs e)
{
List<string> toDelete = checkForChecked();
foreach (var val in toDelete)
{
foreach (Control item in tabPage1.Controls.OfType<Control>())
{
if (item.Name == val.ToString())
{
tabPage1.Controls.Remove(item);
}
}
}
}
I'm using a windows form app, no asp or other web technology.
It's a common error of removing items from a collection while enumerating it. For example:
foreach (Control c in Controls)
Controls.Remove(c);
will remove only half of the controls and leave every second control.
Some of the common solutions to the general problem are:
removing items in reverse order starting from the end of the collection
removing the first found item until no items found
enumerating a copy of the collection
In your case, both methods can be combined to something like this ( .Find returns Control[] ) :
foreach (var cb in tabPage1.Controls.OfType<CheckBox>())
if (cb.Checked)
foreach (var c in tabPage1.Controls.Find(cb.Name, false))
tabPage1.Controls.Remove(c)
You could try something more like:
private void button2_Click(object sender, EventArgs e)
{
List<CheckBox> CheckedBoxes = new List<CheckBox>();
foreach (CheckBox cb in tabPage1.Controls.OfType<CheckBox>())
{
if (cb.Checked)
{
CheckedBoxes.Add(cb);
}
}
foreach (CheckBox cb in CheckedBoxes)
{
string cbName = cb.Name;
cb.Dispose();
// ... probably more code in here to find the other controls
// ... in the "row" based on "cbName"
}
}
You are iterating the controls collection and removing a control, which effects the index of every item in the collection every time you call the Remove() method. This probably messes with your iterator in the foreach, thus the 'skipping' behavior. When removing from any controls collection it is best not to be iterating it at the same time. Moreover, when looping on a Controls collection where you are removing items, code your loops as iterators working backwards.
for (var i=Parent.Controls.Count-1; i >=0; i--)
{
if (someCondition) Parent.Controls.Remove(Parent.Controls[i]);
}
Here is a bit cleaner version of your code. The list returned from the CheckForChecked method is now a list of controls which is easier to use than a list of the controls names.
In the remove, we are iterating the list of things to delete, not the controls collection from which we are deleting.
//Gets a list of all ticked checkboxes
public List<Control> CheckForChecked()
{
var tabPage1 = new TabPage();
var results = new List<Control>(0);
results.AddRange(from Control c in tabPage1.Controls
where c is CheckBox && (c as CheckBox).Checked
select c);
return results;
}
//The button click. Loop through elements and remove ones with the right name
private void button2_Click(object sender, System.EventArgs e)
{
var toDelete = CheckForChecked();
var tabPage1 = new TabPage();
foreach (var val in toDelete.Where(val => tabPage1.Controls.Contains(val)))
{
tabPage1.Controls.Remove(val);
}
}
Hope this helps someone.

How to insert text into Textboxes dynamically?

I am working on a Project in C#, i need to insert text into textfields which are more then 250,
i stored the data in an array of string , now i have to insert data from array into these 250 textboxes in sequence like
textbox1.Text=StringArray[1];
textbox2.Text=StringArray[2];
. .
. .
. .
textbox250.Text=StringArray[250];
i google it no positive results ,
i did code to clear text from all the textboxes, i.e
Action<Control.ControlCollection> func = null;
func = (controls) =>
{
foreach (Control control in controls)
if (control is TextBox)
(control as TextBox).Clear();
else
func(control.Controls);
};
func(Controls);
i tried to insert text like this
Action<Control.ControlCollection> func = null;
int i=0;
func = (controls) =>
{
foreach (Control control in controls)
{
if (control is TextBox)
(control as TextBox).Text = result_set[i++].ToString();
else
func(control.Controls);
}
};
func(Controls);
but got an exception of type 'System.IndexOutOfRangeException'.
The error is because you access a member outside of the array. It could be because you have other textboxes on the page which are found by the loop and then your array index runs out of range. Maybe you could do something like this:
for(int i = 1; i <= StringArray.Length; i++)
{
// I don't know which technology you use, it might be a different method to find
Control control = controlCollection.FindByName("Textbox" + i.ToString();
if (control is TextBox)
(control as TextBox).Text = StringArray[i];
}
You can add an attribute "index" to every textbox on your page with the index of the array, and attach a function to the event "OnInit" in order to insert the text. An example:
PAGE:
[asp:TextBox ID="TextBox1" runat="server" OnInit="setText" index="1"][/asp:TextBox]
[asp:TextBox ID="TextBox2" runat="server" OnInit="setText" index="2"][/asp:TextBox]
...
CODE BEHIND:
public void setText(object sender, System.EventArgs e) {
TextBox tbx;
tbx = sender;
tbx.Text = StringArray[sender.attributes["index"]];
}
Hope this helps!

Gathering textboxes created in design

I added several textboxes by drag_n_dropping. Now I want to gather them all under a textbox array. I know how to create array of textboxes in code but not how to gather the textboxes created during design. Could anyone help please?
Sometimes the textboxes are not placed on the form directly but on a container control like a tab control or a split container. If you want to find all these textboxes, a recursion will help
private List<TextBox> _textboxes = new List<TextBox>();
private void GetTextBoxes(Control parent)
{
foreach (Control c in parent.Controls) {
var tb = c as TextBox;
if (tb != null) {
_textboxes.Add(tb);
} else {
GetTextBoxes(c);
}
}
}
Then you call GetTextBoxes by passing the form as argument
GetTextBoxes(this);
This is possible, since Form itself derives from Control.
This assumes your TextBoxes are within same GroupBox or Panel.
var groupOfTextBoxes = groupBox1.Controls.OfType<TextBox>();
MessageBox.Show(groupOfTextBoxes.Count().ToString());
var textBoxesWithinForm = this.Controls.OfType<TextBox>();
MessageBox.Show(textBoxesWithinForm.Count().ToString());
Requires using System.Linq;. Please note that textBoxesWithinForm will ignore TextBoxes that are within groupBox and vice versa.
Or like #Jeff suggests but instead of going thru this.Controls and comparing if Control is Textbox:
foreach (TextBox in this.Controls.OfType<TextBox>())
{
//add to your array
}
foreach (Control c in this.Controls)
{
if (c is TextBox)
{
//add to your array
}
}

Iterating through textboxes using asp.net

I am building a page with asp.net. I have a form with a table that contains TextBoxes and a submit button. When the form is submitted, I want to grab all the text that was entered into the TextBoxes and operate on them. To do this, I have the following method:
protected void Button1_Click(object sender, EventArgs e)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (Control c in this.Controls)
{
if (c.GetType().Name == "TextBox")
{
TextBox tb = (TextBox)c;
sb.AppendLine(tb.Text);
}
}
Label1.Text = sb.ToString();
}
The problem with this is that the controls apparently doesn't include any of my textboxes. When I iterate through the controls and print out their names, the only one I get is "site_master." (I also tried Controls and Page.Controls instead of this.Controls).
Is there something wrong with my iterator? Is there another way in which I could iterate through all of the textboxes in the table or page? What is the best way to accomplish this?
Would it be too much to build a List<Textbox>, given you know all your textbox controls?
List<Textbox> txtBoxes = new List<Textbox>();
txtBoxes.Add(tb1);
txtBoxes.Add(tb2);
//etc..
Then you have a nice list to work with
If I knew the controls were all in a given containing control, I would simply poll the controls of that control. For example, this.Form.Controls. However, if they could be nested within other child controls, then you could recursively explore the depths from a common outer container.
private IEnumerable<T> FindControls<T>(Control parent) where T : Control
{
foreach (Control control in parent.Controls)
{
if (control is T)
yield return (T)control;
foreach (T item in FindControls<T>(control))
yield return item;
}
}
So this would allow you to retrieve all TextBox children.
List<TextBox> textBoxes = this.FindControls<TextBox>(this).ToList();
string output = string.Join(",", textBoxes.Select(tb => tb.Text));
I'm going to assume that you are using web forms ASP.NET. Typically you declare your controls on the aspx page using something similar to
<asp:TextBox ID="someId" runat="server/>
If you have done this then in your code behind your should just be able to reference the variable someId and the property Text to get/set the text in the control.
If you are building the controls dynamically on the server you should be able to stick them in a list and iterate through it. Make sure you are creating the controls and adding them to the table during the correct part of the page lifecycle. When you add them to a cell in the table you could also keep a reference to the control in a list and just enumerate through that list in your event handler.
Maybe something along the lines of (I didn't compile this so there are probably issues):
public class MyPage: Page
{
private List<TextBox> TxtBoxes = new List<TextBox>();
//registered for the preinit on the page....
public void PreInitHandler(object sender, EventArgs e)
{
for(var i = 0; i < 2; i++)
{
var txtBox = new TextBox{Id = textBox+i};
//...add cell to table and add txtBox Control
TxtBoxes.Add(txtBox);
}
}
}

Categories