Iterating through TextBoxes in asp.net - why is this not working? - c#

I have 2 methods I tried to iterate through all my textboxes in an asp.net page. The first is working, but the second one is not returning anything. Could someone please explain to me why the second one is not working?
This works ok:
List<string> list = new List<string>();
foreach (Control c in Page.Controls)
{
foreach (Control childc in c.Controls)
{
if (childc is TextBox)
{
list.Add(((TextBox)childc).Text);
}
}
}
and the "not working" code:
List<string> list = new List<string>();
foreach (Control control in Controls)
{
TextBox textBox = control as TextBox;
if (textBox != null)
{
list.Add(textBox.Text);
}
}

Your first example is doing one level of recursion, so you're getting TextBoxes that are more than one control deep in the control tree. The second example only gets top-level TextBoxes (which you likely have few or none).
The key here is that the Controls collection is not every control on the page - rather, it is only the immediate child controls of the current control (and a Page is a type of Control). Those controls may in turn have child controls of their own. To learn more about this, read about the ASP.NET Control Tree here and about NamingContainers here. To truly get every TextBox anywhere on the page, you need a recursive method, like this:
public static IEnumerable<T> FindControls<T>(this Control control, bool recurse) where T : Control
{
List<T> found = new List<T>();
Action<Control> search = null;
search = ctrl =>
{
foreach (Control child in ctrl.Controls)
{
if (typeof(T).IsAssignableFrom(child.GetType()))
{
found.Add((T)child);
}
if (recurse)
{
search(child);
}
}
};
search(control);
return found;
}
Which is used as an extension method, like so:
var allTextBoxes = this.Page.FindControls<TextBox>(true);

You need to recurse. The controls are in a tree structure - Page.Controls is not a flattened list of all controls on the page. You'd need to do something like the following to get all values of TextBoxes:
void GetTextBoxValues(Control c, List<string> strings)
{
TextBox t = c as TextBox;
if (t != null)
strings.Add(t.Text);
foreach(Control child in c.Controls)
GetTextBoxValues(child, strings);
}
...
List<string> strings = new List<string>();
GetTextBoxValues(Page, strings);

you can try this piece of code to get list of all TextBoxes
public partial class _Default : System.Web.UI.Page
{
public List<TextBox> ListOfTextBoxes = new List<TextBox>();
protected void Page_Load(object sender, EventArgs e)
{
// after execution this line
FindTextBoxes(Page, ListOfTextBoxes);
//ListOfTextBoxes will be populated with all text boxes with in the page.
}
private void FindTextBoxes(Control Parent, List<TextBox> ListOfTextBoxes)
{
foreach (Control c in Parent.Controls) {
// if c is a parent control like panel
if (c.HasControls())
{
// search all control inside the panel
FindTextBoxes(c, ListOfTextBoxes);
}
else {
if (c is TextBox)
{
// if c is type of textbox then put it into the list
ListOfTextBoxes.Add(c as TextBox);
}
}
}
}
}

Related

Readonly properties

hi all here i try to make method which can loop on the form and convert any text boxenter image description here from Read only=true to be Read only = false but its not working
public static void unread only(Form frm)
{
foreach (Control item in frm.Controls)
{
if (item is TextBox)
{
// item.ReadOnly = false;
}
}
}
In your code, the compiler thinks that the object you have found is a Control, and does not know what type of control it is. You need to tell it what sort of control it is, and you can do this by casting it to a textbox:
((TextBox)item).ReadOnly = false;
However, there are better ways of doing this. Your code will only look at Top-level controls, and if you have container controls on your form, it will not recursively search those to find other textboxes. A recursive method to do this is as follows:
public static IEnumerable<T> GetControlsOfType<T>(Control root)
where T : Control
{
var t = root as T;
if (t != null)
yield return t;
var container = root as ContainerControl;
if (container != null)
foreach (Control c in container.Controls)
foreach (var i in GetControlsOfType<T>(c))
yield return i;
}
This is some code I got from here. It allows you to do something like this:
foreach (var textBox in GetControlsOfType<TextBox>(theForm))
{
textBox.ReadOnly = false;
}
Primarily your problem is that you are not casting the control to TextBox so that you have access to the property you want to set.
foreach (Control item in frm.Controls) {
if (item is TextBox) {
var textBox = item as TextBox;
textBox.ReadOnly = false;
}
}
That said, controls can contain child control, so you would need to crawl the entire form looking for embedded text boxes.
This will check the form and its controls for text boxes and set them to read only
public static void SetReadOnly(Control frm) {
var controls = new Queue<Control>();
controls.Enqueue(frm);
while (controls.Count > 0) {
var control = controls.Dequeue();
if (control is TextBox) {
var txtBox = control as TextBox;
txtBox.ReadOnly = true;
}
if (control.HasChildren) {
foreach (var child in control.Controls.OfType<Control>()) {
controls.Enqueue(child);
}
}
}
}
The code is pretty self explanatory but you should walk through the flow to understand what it is doing.
You need to be recursive. A Form Control can contain other Controls.

Save string values via textboxes form a specific tabpage to a List<string> MyData

I am developing an application using Windows Form and C#. How can I add the Text of all textboxes on the current tabpage to a List<string> Mydata.
Here is my code below:
foreach(Control c in Controls){
if((c is TextBox) && (c.Text !="")){
if(c.Tag.ToString() == "500"){
StaticClass.Mydata.Items.Add(c.Text);
}
}
}
NB: Mydata (List) is public on a static class.
I have a form with a tabcontrol which contains about 6 tabpages. The other tabpages have textboxes, too, but I want to retrieve only those in my example on tabindex 5.
Thanks.
If I understand you correctly you need to get the text from all TextBoxes within a TabPage, but only if the Tag property is equal to the string "500".
Your code is basically right, but I've modified it slightly (removed some redundant parenthesis etc.).
I'm assuming that your TabPage is named tabPage1, indexOfTabPage is the index of the TabPage and the results are placed in a local List.
You can just add the strings to a static list instead if you like.
int indexOfTabPage = 5;
List<string> myData = new List<string>();
foreach(Control c in this.tabControl1.TabPages[indexOfTabPage].Controls)
{
if(c is TextBox && !string.IsNullOrEmpty(c.Text) && c.Tag != null && c.Tag.ToString() == "500")
{
myData.Add(c.Text);
}
}
private void GetTextBoxValueFromCurrentTabPage()
{
List<string> lstTextBoxData = new List<string>();
foreach (var textBox in tabControl1.TabPages[tabControl1.SelectedIndex].Controls.OfType<TextBox>())
{
lstTextBoxData.Add(textBox.Text.Trim());
}
}
private void GetTextBoxValueFromAllTabPages()
{
List<string> lstTextBoxData = new List<string>();
foreach (TabPage tabPage in tabControl1.TabPages)
{
foreach (var textBox in tabPage.Controls.OfType<TextBox>())
{
lstTextBoxData.Add(textBox.Text.Trim());
}
}
}

Loop through all controls of a Form, even those in GroupBoxes

I'd like to add an event to all TextBoxes on my Form:
foreach (Control C in this.Controls)
{
if (C.GetType() == typeof(System.Windows.Forms.TextBox))
{
C.TextChanged += new EventHandler(C_TextChanged);
}
}
The problem is that they are stored in several GroupBoxes and my loop doesn't see them. I could loop through controls of each GroupBox individually but is it possible to do it all in a simple way in one loop?
The Controls collection of Forms and container controls contains only the immediate children. In order to get all the controls, you need to traverse the controls tree and to apply this operation recursively
private void AddTextChangedHandler(Control parent)
{
foreach (Control c in parent.Controls)
{
if (c.GetType() == typeof(TextBox)) {
c.TextChanged += new EventHandler(C_TextChanged);
} else {
AddTextChangedHandler(c);
}
}
}
Note: The form derives (indirectly) from Control as well and all controls have a Controls collection. So you can call the method like this in your form:
AddTextChangedHandler(this);
A more general solution would be to create an extension method that applies an action recursively to all controls. In a static class (e.g. WinFormsExtensions) add this method:
public static void ForAllControls(this Control parent, Action<Control> action)
{
foreach (Control c in parent.Controls) {
action(c);
ForAllControls(c, action);
}
}
The static classes namespace must be "visible", i.e., add an appropriate using declaration if it is in another namespace.
Then you can call it like this, where this is the form; you can also replace this by a form or control variable whose nested controls have to be affected:
this.ForAllControls(c =>
{
if (c.GetType() == typeof(TextBox)) {
c.TextChanged += C_TextChanged;
}
});
A few simple, general purpose tools make this problem very straightforward. We can create a simple method that will traverse an entire control's tree, returning a sequence of all of it's children, all of their children, and so on, covering all controls, not just to a fixed depth. We could use recursion, but by avoiding recursion it will perform better.
public static IEnumerable<Control> GetAllChildren(this Control root)
{
var stack = new Stack<Control>();
stack.Push(root);
while (stack.Any())
{
var next = stack.Pop();
foreach (Control child in next.Controls)
stack.Push(child);
yield return next;
}
}
Using this we can get all of the children, filter out those of the type we need, and then attach the handler very easily:
foreach(var textbox in GetAllChildren().OfType<Textbox>())
textbox.TextChanged += C_TextChanged;
Try this
AllSubControls(this).OfType<TextBox>().ToList()
.ForEach(o => o.TextChanged += C_TextChanged);
where AllSubControls is
private static IEnumerable<Control> AllSubControls(Control control)
=> Enumerable.Repeat(control, 1)
.Union(control.Controls.OfType<Control>()
.SelectMany(AllSubControls)
);
LINQ is great!
Haven't seen anyone using linq and/or yield so here goes:
public static class UtilitiesX {
public static IEnumerable<Control> GetEntireControlsTree(this Control rootControl)
{
yield return rootControl;
foreach (var childControl in rootControl.Controls.Cast<Control>().SelectMany(x => x.GetEntireControlsTree()))
{
yield return childControl;
}
}
public static void ForEach<T>(this IEnumerable<T> en, Action<T> action)
{
foreach (var obj in en) action(obj);
}
}
You may then use it to your heart's desire:
someControl.GetEntireControlsTree().OfType<TextBox>().ForEach(x => x.Click += someHandler);
As you have stated, you will have to go deeper than just cycling over each element in your form. This, unfortunately, implies the use of a nested loop.
In the first loop, cycle through each element. IF the element is of type GroupBox, then you know you'll need to cycle through each element inside the groupbox, before continuing; else add the event as normal.
You seem to have a decent grasp of C# so I won't give you any code; purely to ensure you develop all the important concepts that are involved in problem solving :)
you can only loop through open forms in windows forms using form collection for example to set windows start position for all open forms:
public static void setStartPosition()
{
FormCollection fc = Application.OpenForms;
foreach(Form f in fc)
{
f.StartPosition = FormStartPosition.CenterScreen;
}
}
I know that this is an older topic, but would say the code snippet from http://backstreet.ch/coding/code-snippets/mit-c-rekursiv-durch-form-controls-loopen/ is a clever solution for this problem.
It uses an extension method for ControlCollection.
public static void ApplyToAll<T>(this Control.ControlCollection controlCollection, string tagFilter, Action action)
{
foreach (Control control in controlCollection)
{
if (!string.IsNullOrEmpty(tagFilter))
{
if (control.Tag == null)
{
control.Tag = "";
}
if (!string.IsNullOrEmpty(tagFilter) && control.Tag.ToString() == tagFilter && control is T)
{
action(control);
}
}
else
{
if (control is T)
{
action(control);
}
}
if (control.Controls != null && control.Controls.Count > 0)
{
ApplyToAll(control.Controls, tagFilter, action);
}
}
}
Now, to assign an event to all the TextBox controls you can write a statement like (where 'this' is the form):
this.Controls.ApplyToAll<TextBox>("", control =>
{
control.TextChanged += SomeEvent
});
Optionally you can filter the controls by their tags.
Since the Question regarding "Adding an Event to your TextBoxes"; was already answered; I'm providing some explanation and adding an iteration alternative using a for loop instead.
Problem:
Being Unable to Get Controls Inside a Container.
Solution:
In order to retrieve the Controls inside a Container you have to specify the Container that Contains the Controls you wish to access to.
Therefore your loop must check the Controls inside a Container.
Otherwise your loop will not find the Controls inside a Container.
i.e:
foreach (Control control in myContainer.Controls)
{
if (control is TextBox) { /* Do Something */ }
}
In case you have several Containers:
Initially iterate the Containers.
Then iterate over the controls inside the container (the container found in the initial iteration).
Pseudo Code Example on How to use a for Loop Instead:
/// <summary> Iterate Controls Inside a Container using a for Loop. </summary>
public void IterateOverControlsIncontainer()
{
// Iterate Controls Inside a Container (i.e: a Panel Container)
for (int i = 0; i < myContainer.Controls.Count; i++)
{
// Get Container Control by Current Iteration Index
// Note:
// You don't need to dispose or set a variable to null.
// The ".NET" GabageCollector (GC); will clear up any unreferenced classes when a method ends in it's own time.
Control control = myContainer.Controls[i];
// Perform your Comparison
if (control is TextBox)
{
// Control Iteration Test.
// Shall Display a MessageBox for Each Matching Control in Specified Container.
MessageBox.Show("Control Name: " + control.Name);
}
}
}
Updated answer:
I needed to disable all the controls in a form, including groupboxes. This code worked:
private void AlterControlsEnable(bool ControlEnabled)
{
foreach (Control i in Controls)
i.Enabled = ControlEnabled;
}

C# winform, Can I access Controls Inside a GroupBox like this: myGroupBox.InnerTextBox.Text "someText";?

I have a GroupBox which has 3 TextBoxes and 3 Labels
the name of the group box is TextInfoGroupBox ..
I'm trying to access the textBoxes inside it, but I just don't seem to know how ..
I tried something like:
TextInfoGroupBox.innerTextbox;
TextInfoGroupBox.Controls.GetChildControl;
Both of these didn't pop up in the intellisence ..
how can i reach them, set and get data from them ?
You can access them just like any other control:
innerTextBox
The Visual Studio designer generates a field for each control you put in your form, regardless of nesting.
Use Controls collection for this purpose. You will need to know exactly what item in that collection is your TextBox. If there are only 3 textboxes in your groupbox you can use
groupBox.Controls[0], groupBox.Controls[1], groupBox.Controls[2]
to access your items or just use their respective names.
If you don't have direct access to innerTextBox for some reason, you can always go hunting:
TextBox myTextBox = null;
Control[] controls = TextInfoGroupBox.Controls.Find("InnerTextBoxName", true);
foreach (Control c in controls)
{
if (c is TextBox)
{
myTextBox = c as TextBox;
break;
}
}
At the end of this , if myTextBox is null, it couldn't be found (obviously). Hopefully you don't structure it so that there would be multiple entries.
You could also create some cute extension methods:
public static Control FindControl(this Control parent, string name)
{
if (parent == null || string.IsNullOrEmpty(name))
{
return null;
}
Control[] controls = parent.Controls.Find(name, true);
if (controls.Length > 0)
{
return controls[0];
}
else
{
return null;
}
}
public static T FindControl<T>(this Control parent, string name) where T : class
{
if (parent == null || string.IsNullOrEmpty(name))
{
return null;
}
Control[] controls = parent.Controls.Find(name, true);
foreach (Control c in controls)
{
if (c is T)
{
return c as T;
}
}
return null;
}
And you could simply call them as
Control c = TextInfoGroupBox.FindControl("MyTextBox");
TextBox tb = TextInfoGroupBox.FindControl<TextBox>("MytextBox");

How do I get all controls of a form in Windows Forms?

I have a Form named A.
A contains lots of different controls, including a main GroupBox. This GroupBox contains lots of tables and others GroupBoxes. I want to find a control which has e.g. tab index 9 in form A, but I don't know which GroupBox contains this control.
How can I do this?
With recursion...
public static IEnumerable<T> Descendants<T>( this Control control ) where T : class
{
foreach (Control child in control.Controls) {
T childOfT = child as T;
if (childOfT != null) {
yield return (T)childOfT;
}
if (child.HasChildren) {
foreach (T descendant in Descendants<T>(child)) {
yield return descendant;
}
}
}
}
You can use the above function like:
var checkBox = (from c in myForm.Descendants<CheckBox>()
where c.TabIndex == 9
select c).FirstOrDefault();
That will get the first CheckBox anywhere within the form that has a TabIndex of 9. You can obviously use whatever criteria you want.
If you aren't a fan of LINQ query syntax, the above could be re-written as:
var checkBox = myForm.Descendants<CheckBox>()
.FirstOrDefault(x=>x.TabIndex==9);
Recursively search through your form's Controls collection.
void FindAndSayHi(Control control)
{
foreach (Control c in control.Controls)
{
Find(c.Controls);
if (c.TabIndex == 9)
{
MessageBox.Show("Hi");
}
}
}
void iterateControls(Control ctrl)
{
foreach(Control c in ctrl.Controls)
{
iterateControls(c);
}
}
You can make a method like this:
public static Control GetControl(Control.ControlCollection controlCollection, Predicate<Control> match)
{
foreach (Control control in controlCollection)
{
if (match(control))
{
return control;
}
if (control.Controls.Count > 0)
{
Control result = GetControl(control.Controls, match);
if (result != null)
{
return result;
}
}
}
return null;
}
...that is used like this:
Control control = GetControl(this.Controls, ctl => ctl.TabIndex == 9);
Note however that TabIndex is a tricky case, since it starts at 0 within each container, so there may be several controls in the same form having the same TabIndex value.
Either way, the method above can be used for checking pretty much any property of the controls:
Control control = GetControl(this.Controls, ctl => ctl.Text == "Some text");
I hate recursion, so I always use a stack for this sort of thing. This assigns a common event handler to the CheckedChanged event of every RadioButton control in the current control hierarchy:
Stack<Control> controlStack = new Stack<Control>();
foreach (Control c in this.Controls)
{
controlStack.Push(c);
}
Control ctl;
while (controlStack.Count > 0 && (ctl = controlStack.Pop()) != null)
{
if (ctl is RadioButton)
{
(ctl as RadioButton).CheckedChanged += new EventHandler(rb_CheckedChanged);
}
foreach (Control child in ctl.Controls)
{
controlStack.Push(child);
}
}
You could easily retrofit Josh Einstein's extension method to work this way.

Categories