16

I have a winforms app that has 37 textboxes on the screen. Each one is sequentially numbered:

DateTextBox0
DateTextBox1 ...
DateTextBox37

I am trying to iterate through the text boxes and assign a value to each one:

int month = MonthYearPicker.Value.Month;
int year = MonthYearPicker.Value.Year;
int numberOfDays = DateTime.DaysInMonth(year, month);

m_MonthStartDate = new DateTime(year, month, 1);
m_MonthEndDate = new DateTime(year, month, numberOfDays);

DayOfWeek monthStartDayOfWeek = m_MonthStartDate.DayOfWeek;
int daysOffset = Math.Abs(DayOfWeek.Sunday - monthStartDayOfWeek);

for (int i = 0; i <= (numberOfDays - 1); i++)
{
 //Here is where I want to loop through the textboxes and assign values based on the 'i' value
   DateTextBox(daysOffset + i) = m_MonthStartDate.AddDays(i).Day.ToString();
}

Let me clarify that these textboxes appear on separate panels (37 of them). So in order for me to loop through using a foreach, I have to loop through the primary controls (the panels), then loop through the controls on the panels. It starts getting complicated.

Any suggestions on how I can assign this value to the textbox?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Taryn
  • 242,637
  • 56
  • 362
  • 405

12 Answers12

40

To get all controls and sub-controls recursively of specified type, use this extension method:

public static IEnumerable<TControl> GetChildControls<TControl>(this Control control) where TControl : Control
{
    var children = (control.Controls != null) ? control.Controls.OfType<TControl>() : Enumerable.Empty<TControl>();
    return children.SelectMany(c => GetChildControls<TControl>(c)).Concat(children);
}

usage:

var allTextBoxes = this.GetChildControls<TextBox>();
foreach (TextBox tb in allTextBoxes)
{
    tb.Text = ...;
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
  • 7
    +1 - that is almost exactly the code that I was about to post. – Fredrik Mörk Feb 01 '11 at 13:37
  • Don't you mean "foreach (TextBox tb in allTextBoxes)" for the loopiness? – B. Clay Shannon-B. Crow Raven May 01 '15 at 22:42
  • 2
    Seems like the most elegant solution, but i get the error: "TControl ---> The type or namespace name 'TControl' could not be found (are you missing a using directive or an assembly reference?) ". I searched MSDN, but didnt find that class. What to do? – kiltek May 13 '15 at 08:12
  • @kiltek: this is the generic parameter declared as `where TControl : System.Web.Ui.Control` iirc. – abatishchev May 13 '15 at 14:46
  • I'm getting the error "Constraints are not allowed on non-generic declarations" (.net 4.0) – TEK Jun 14 '15 at 20:06
  • @TEK: Right! Don't know how I missed it and nobody noticed for so long time. Fixed. Thanks! – abatishchev Jun 14 '15 at 20:08
  • 1
    @abatishchev Wow. That has to be the fastest edit of all time. Thank you! (Ignore my previous comment, I am dumb). – TEK Jun 14 '15 at 20:12
  • @TEK: Yea, me is getting this error, too. And I'm applied the fix, please check it out. I'm just was digging into my very old code to figure out how this code being copy-pasted was working before. Hm. weird. Anyway, now should work. Let me know if it doesn't, please. – abatishchev Jun 14 '15 at 20:15
  • @abatishchev In it's current state, it works great! Thank you kindly for the extremely rapid response to a 4+ year old post. This has saved me having to do it the old dumb way. – TEK Jun 14 '15 at 20:17
  • Oddly enough, this works dandy except for a combobox that is populated with string values; in that case, the loop to set its SelectedIndex to -1 doesn't work. I have to set it explicitly after the loop for it to work. Why this would be, I don't know, but here is my code: var allComboBoxes = this.GetChildControls(); foreach (ComboBox cmbx in allComboBoxes) { cmbx.SelectedIndex = -1; } comboBoxDaysOfMonth.SelectedIndex = -1; – B. Clay Shannon-B. Crow Raven Feb 18 '16 at 18:44
  • On third thought, this is happening to all the controls that are not directly on the form (those on a Panel or a Groupbox); I think that's probably the real issue/the crux of the biscuit. – B. Clay Shannon-B. Crow Raven Feb 18 '16 at 19:30
  • I don't get how this gets ALL textboxes. For me, it only gets textboxes that are direct children of the form/given control. You still need to loop through each control with this solution, right? – n00dles Jun 27 '17 at 14:24
  • @n00dles: the method calls itself recursively on all children of given type. Another option would be rewrite in and remove `OfType`. So just return all children aka flatten the hierarchy. Then call `OfType` to filter by given type. – abatishchev Jun 30 '17 at 04:12
  • I noticed the recursive call. I must have done something wrong then, coz it didn't return child controls. I found another similar method (but not an extension) that worked though. I don't yet understand lambda expressions, so it's hard for me to think it through. – n00dles Jun 30 '17 at 05:24
  • @n00dles: post an answer if you found a better (or at least a similar) solution! – abatishchev Jun 30 '17 at 18:12
7

You Could loop all the controls in the form asking one by one if it is a "Textbox" y ther return the complete List of them.

public List GetTextBoxes(){   
    var textBoxes = new List();   
        foreach (Control c in Controls){   
            if(c is TextBox){   
                textBoxes.add(c);   
        }   
    }   
return textBoxes;   
}
JAiro
  • 5,914
  • 2
  • 22
  • 21
  • what if there might be additional text box which name happens to be fingering other than datatexrboc? will that count all text hides including one we don't need to count? – HelpNeeder Nov 01 '11 at 23:28
5

Since this post seems to resurrect itself from time to time and since the solutions above do not find controls inside of controls, such as in a groupbox, this will find them. Just add your control type:

public static IList<T> GetAllControls<T>(Control control) where T : Control
{
    var lst = new List<T>();
    foreach (Control item in control.Controls)
    {
        var ctr = item as T;
        if (ctr != null)
            lst.Add(ctr);
        else
            lst.AddRange(GetAllControls<T>(item));
    }
    return lst;
}

And its use:

var listBoxes = GetAllControls<ListBox>(this);
foreach (ListBox lst in listBoxes)
{
    //Do Something
}
jordanz
  • 367
  • 4
  • 12
Zath
  • 547
  • 2
  • 10
  • 25
  • 1
    This worked for me (the top answer didn't). I put my method in a static class and had to set the method signature to " ... GetAllControls(this Control control) ..." for it to work. The "this" would allow an instance of a control to call this extension method. – jkulpe Apr 07 '20 at 21:09
4

You can loop through the textboxes in your form in a fairly simple manner:

Func<ControlCollection, List<TextBox>> SearchTextBoxes = null;
SearchTextBoxes = coll => {
    List<TextBox> textBoxes = new List<TextBox>();

    foreach (Control c in coll) {
        TextBox box = c as TextBox;
        if (box != null)
           textBoxes.Add(box);
        if (c.Controls.Count > 0)
           textBoxes.AddRange(SearchTextBoxes(c.Controls));
    }

    return textBoxes;
};

var tbs = SearchTextBoxes(this.Controls).OrderBy(tb => tb.Name);

Edit: Changed according to new requirements. Not nearly as elegant as the LINQ-solution, of course :)

Christian
  • 4,261
  • 22
  • 24
  • That will miss any controls that are not located directly on the form (such as in a panel, tab page or similar). – Fredrik Mörk Feb 01 '11 at 13:27
  • I know I can loop through the controls but this screen contains 37 panels with the DateTextBoxes as well as another control. So in order for me to get the data I have to loop through the panel controls and then the controls in the panel to get an array. Is there a way for me to do this without using an array? Just assign the DateTextBoxes the data it needs? – Taryn Feb 01 '11 at 13:29
  • @bluefeet: See my answer to find how to loop through nested controls – abatishchev Feb 01 '11 at 13:33
  • Well, having them in panels changes things "a bit". You need to go recursive about it. Or even better, use the answer that abatishchev provided. It's pretty neat :) – Christian Feb 01 '11 at 13:34
2

Iterate through controls within form and check name of the control if matched then set Text property as you require.

int i = 0;
foreach (Control contrl in this.Controls) {
    if (contrl.Name == ("DateTextBox" + i.ToString())) {
        contrl.Text = "requiredtexttobeset";
    }
    i = i + 1;
}
Ismail
  • 634
  • 1
  • 5
  • 11
1
        //THE EASY WAY! Always post easy solutions. It's the best way.
        //This code is used to loop through all textboxes on a form for data validation. 
        //If an empty textbox is found, Set the error provider for the appropriate textbox.
        foreach (var control in Controls)
        {
            if (control is TextBox)
            {
                //Box the control into a textbox. Not really needed, but do it anyway
                var textbox = (TextBox)control;

                if (String.IsNullOrWhiteSpace(textbox.Text))
                {
                    //Set the errorProvider for data validation
                    errorProvider1.SetError(textbox, "Data Required!");
                    textbox.Text = String.Empty; //Clear out the whitespace if necessary
                    //blnError = true;
                }
            }
        }
puffgroovy
  • 240
  • 2
  • 5
1

You can simply do this mate...

foreach (TextBox txt in this.Panel.Controls.OfType<TextBox>())
            {
                txt.Text="some value you assign";
            }

If your text boxes are on the form directly and not on a Panel then you can replace this.Panel.Controls with this.Controls. That should be short and clear enough for you.

indago
  • 2,041
  • 3
  • 29
  • 48
1

If you want to do without 'foreach' (If you have specific boxes to adjust/address)

int numControls = Page.Form.Controls.Count;

    for (int i = 0; i < numControls; i++)
    {
        if (Page.Form.Controls[i] is TextBox)
        {
            TextBox currBox = Page.Form.Controls[i] as TextBox;
            currbox.Text = currbox.TabIndex.ToString();
        }
    }
Mark
  • 11
  • 1
0

Other answers just not cutting it for you?
I found this as an answer to a similar question on SO, but I can't find the thread now. It recursively loops through ALL controls of a given type which are located within a control. So includes children of children of children of... etc. My example changes the ForeColor of each TextBox to Hot Pink!

public IEnumerable<Control> GetAllControlsOfType(Control control, Type type)
{
    var controls = control.Controls.Cast<Control>();

    return controls.SelectMany(ctrl => GetAllControlsOfType(ctrl, type))
                              .Concat(controls)
                              .Where(c => c.GetType() == type);
}

Implementation:

IEnumerable<Control> allTxtBxs = GetAllControlsOfType(this, typeof(TextBox));
foreach (TextBox txtBx in allTxtBxs)
{
    txtBx.ForeColor = Color.HotPink;
}

Quite similar to abatishchev's answer(which, for me, only returned first-level child controls), but different enough to merit it's own answer I think.

n00dles
  • 255
  • 3
  • 18
0

After the InitialiseComponents() call, add the textboxes to a collection member variable on the form. You can then iterate through them in order later on.

Neil Barnwell
  • 41,080
  • 29
  • 148
  • 220
0

Since you already know the name of control, therefore you can search the control by its name.

See Get a Windows Forms control by name in C#

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Ramesh Soni
  • 15,867
  • 28
  • 93
  • 113
0

You can create a Dictionary of TextBox, int like the following

Dictionary<TextBox, int> textBoxes = new Dictionary<TextBox, int>();

foreach (TextBox control in Controls.OfType<TextBox>())
    textBoxes[control] = Convert.ToInt32(control.Name.Substring(11));

Now.. to loop through them..

foreach (var item in textBoxes.Select(p => new { textBox = p.Key, no = p.Value}))
     item.textBox.Text = item.no.ToString(); // whatever you want...

Good luck!

Homam
  • 23,263
  • 32
  • 111
  • 187