I have a bunch of textboxes, about 150 to be exact. They are inside different tabs of a tab control, and are not in order by name on screen. They are named simply textBox1, textBox2, textBox3... I would like to be able to iterate them in order by name and not by how they appear on the form. How would I got about doing this?
public IEnumerable<Control> GetChildrenRecursive(Control parent)
{
var controls = new List<Control>();
foreach(Control child in parent.Controls)
controls.AddRange(GetChildrenRecursive(child));
controls.Add(parent); //fix
return controls;
}
TextBox[] textboxes = GetChildrenRecursive(this)
.OfType<TextBox>().OrderBy(i => i.Name).ToArray();
Related
I have a windows form which include some textbox and labels.In my program I set all of them unvisible and when I press button it makes all of the labels and textbox visible with the code below and it works perfect.
List<Label> lbls = this.Controls.OfType<Label>().ToList();
foreach (var lbl in lbls)
{
if (lbl.Name.StartsWith("label"))
{
lbl.Visible = true;
}
}
List<TextBox> txts = this.Controls.OfType<TextBox>().ToList();
foreach (var txt in txts)
{
if (txt.Name.StartsWith("textBox"))
{
txt.Visible = true;
}
}
But when I put all of my labels and textboxes into groupbox.My code doesn't work.How can I do this?
Note: My groupbox is also unvisible and when I press button.
groupBox1.visible =true;
This code works and groupbox panel seems, but the code of labels and textboxes doesn't work.
Because you are working with the immediate child of Form here
List<Label> lbls = this.Controls.OfType<Label>().ToList();
Notice this that means your current form. so when you have controls outside in form it works,
But when you put them inside group box it won't be the immediate child anymore.
so use
List<Label> lbls = groupBox1.Controls.OfType<Label>().ToList();
This will give you access to immediate children of the group box.
You're better off creating a recursive method of your own. Try implementing something like this:
private void MakeControlsInvisible(Control container, params Type[] controlTypes)
{
foreach (Control control in container.Controls)
{
if (controlTypes.Contains(control.GetType()))
{
control.Visible = false;
}
if (control.Controls.Count > 0)
{
MakeControlsInvisible(control, controlTypes);
}
}
}
And then using it on whatever container you wish:
MakeControlsInvisible(this, typeof(Label), typeof(TextBox)); // Will make all labels and textboxes inside the entire form invisible.
MakeControlsInvisible(groupBox1, typeof(Label), typeof(TextBox));// Will make all labels and textboxes inside groupBox1 invisible.
First, I read this Reset all the items in a form
It was a great help until I realised all my controls are inside a TabControl containing itself several tabs in which there are all the common controls i.e. textbox, datetimepicker, datagrigview, etc....
Then I tried MyTabControl.Controls.Clear() but this deleted all tabs in the form.
How can I implement this Reset all the items in a form in a TabControl ?
use:
foreach (Control c in GetAll(myTabControl))
{
ResetAllControls(c);
}
in which ResetAllControls is the method in your referenced link and
public static IEnumerable<Control> GetAll(Control control)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => GetAll(ctrl))
.Concat(controls);
}
from the accepted answer of this question.
I have a windows form that just exists to take input from user, for all intents and purposes it is just a label and a corresponding input box (textbox, checkbox, masket textbox etc).
I have programatically placed all the input fields in a TabIndex order that is optimal for cycling through them in a way that fits with their locations (tab down each column of inputs, then to the top of the next column).
The person that I am building this project for has stipulated that only like each textbox to come available one at a time, as the previous one has been filled out. This is a crude way of doing it with no validation, but essentially something like this...
if (String.IsNullOrEmpty(textbox1.Text))
{
textbox2.Enabled = true
}
So this is fine to do with two textboxes in this example, but the form has 28 different inputs on it, so an absurd series of if statements will only be a last resort.
My thoughts has been to put all the inputs in a List, ideally in the same order as is their TabIndexes. I tried to do this using a foreach loop...
List<Control> inputsList = new List<Control>();
public void initialiseControls()
{
//control position to insert control into list at specified index
int cntrlpos = 0;
//for every control in form
foreach (Control cntrl in this.Controls)
{
//not counting labels (not input fields)
if (!(cntrl is Label))
{
//set list position to equal control's TabIndex
cntrlpos = cntrl.TabIndex;
//insert the control into the list at the position reflecting TabIndex
inputsList.Insert(cntrlpos, cntrl); //<---- Error Occurs
//ASK TEXTBOX TO OUTPUT LIST POSITION AS TEST
//foreach (var txtbx in this.Controls.OfType<TextBox>())
//{
// txtbx.Text = Convert.ToString(cntrlpos);
//}
}
}
As soon as the function is called, an exception is thrown stating that "Index must be within the bounds of the list".
When I put a breakpoint into the code, it showed cntrlpos to equal 29, which is more than the 28 total input controls there are on the form.
I don't know where to go from here, if anyone can offer some advice on the code above to place the Controls into the list in the correct order (or point me in the direction of another method to do something like this), then I would really appreciate it.
Thanks,
Mark
To make your list, try this:
List<Control> inputList =
(from Control c in getAllControls(this)
where c.TabStop
orderby c.TabIndex
select c).ToList();
Define the method getAllControls elsewhere in your form class:
IEnumerable<Control> getAllControls(Control parent)
{
foreach (Control control in parent.Controls)
{
yield return control;
foreach (Control descendant in getAllControls(control))
yield return descendant;
}
}
(Taken and modified slightly from Recursive control search with Linq)
This will make it so that you get even nested controls (such as those in panels or groupboxes).
You can't just use the TabIndex as an index into your list, because even stuff like labels have tab indices, which will mess up your indices.
I think you've over complicated it...
Just use Control.GetNextControl:
Retrieves the next control forward or back in the tab order of child
controls.
For example, with just TextBoxes:
private void textBoxes_TextChanged(object sender, EventArgs e)
{
Control ctl = (Control)sender;
if (!String.IsNullOrEmpty(ctl.Text))
{
Control next = this.GetNextControl(ctl, true);
if (next != null)
{
next.Enabled = true;
}
}
}
Obviously you might need a slightly more complicated check for some other types of controls in a different handler, but you'd still just grab the next control to enable using GetNextControl().
Some inits done earlier in the code...
private List<System.Windows.Forms.TabPage> tab_pages = new List<System.Windows.Forms.TabPage>();
int tab_increment = 0;
Somewhere in the code, I create a bunch of tab pages in real-time.
for (i=0; i<5; i++)
{
tab_pages.Add( new System.Windows.Forms.TabPage() );
tab_pages[tab_increment].Location = new System.Drawing.Point(4, 22);
tab_pages[tab_increment].Name = 1 + tab_increment.ToString();
tab_pages[tab_increment].Size = new System.Drawing.Size(501, 281);
tab_pages[tab_increment].Text = tab_increment.ToString();
this.tabControl.Controls.Add(tab_pages[tab_increment]);
tab_increment += 1;
}
Now I would like to access elements that are these tab pages. Also let's pretend that I created different elements on each page (example, tabPage[0] a button, tabPage[1] a checkbox, etc), how do I access them knowing that everything was added dynamically?
Check this approach:
void Walk(Control control)
{
foreach (Control c in control.Controls)
{
//just walking through controls...
//...do something
//but remember, it could contain containers itself (say, groupbox or panel, etc.)...so, do a recursion
if (c.Controls.Count > 0)
Walk(c);
}
//or
foreach (Button btn in control.Controls.OfType<Button>())
{
//an example of how to walk through controls sub array of certain type
//this loop won't have a single iteration if this page contains no Buttons
//..so you can replace Button
//and have some certain code for different types of controls
}
}
And launch it for tabcontrol:
foreach (TabPage page in tabControl1.TabPages)
Walk(page);
I guess there is no special need to have separate collection of tabpages for one tabcontrol, as soon as it has TabPages property.
In the code above I used Enumerable.OfType Method to get a subcollection of controls of certain type.
As for your code, try this:
for (int i = 0; i < 5; i++)
{
this.tabControl.Controls.Add(new System.Windows.Forms.TabPage());
this.tabControl.TabPages[i].Text = i.ToString();
//...do whatever you need
//...
//besdies, I think, ther's no need in tab_increment...loop index works well enough
}
In order to add pages, I think that using
tabControl.TabPages.Add(new TabPage("Name"));
or in your case
this.tabControl.TabPages.Add(tab_pages[tab_increment]);
is more suitable.
In order to access them you could use
TabPage tp = tabControl.TabPages[i]; //where i is the index of your TabPage
and you can use TabPage.Controls.Add of the Controls property to add any Control on the TabPage like:
Button btn = new Button();
btn.Name = "Button name";
tp.Controls.Add(btn);
You can use the Controls property on the TabPage object. Each control in the collection is given to you as a Control, and it is up to you to cast them to the type that you want.
Finding all the controls in a form by Using "Reflection"
During the Run Time..
You can recursively iterate through Controls collection on a form or another control.
But you can't get all controls at runtime with reflection if they were added to a controls collection during the program run
List<Control> list = new List<Control>();
GetAllControl(this, list);
private void GetAllControl(Control c , List<Control> list)
{
foreach (Control control in c.Controls)
{
list.Add(control);
if (control.Controls.Count > 0)
GetAllControl(control , list);
}
}
You can do something like this
var controls = from control in this.Controls select control;
or if you want any specific control.
var textboxes = from textbox in this.Controls.OfType<TextBox>()
select textbox;