Foreach control in form including children [duplicate] - c#

I need to get all controls on a form that are of type x. I'm pretty sure I saw that code once in the past that used something like this:
dim ctrls() as Control
ctrls = Me.Controls(GetType(TextBox))
I know I can iterate over all controls getting children using a recursive function, but
is there something easier or more straightforward, maybe like the following?
Dim Ctrls = From ctrl In Me.Controls Where ctrl.GetType Is Textbox

Here's another option for you. I tested it by creating a sample application, I then put a GroupBox and a GroupBox inside the initial GroupBox. Inside the nested GroupBox I put 3 TextBox controls and a button. This is the code I used (even includes the recursion you were looking for)
public IEnumerable<Control> GetAll(Control control,Type type)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => GetAll(ctrl,type))
.Concat(controls)
.Where(c => c.GetType() == type);
}
To test it in the form load event I wanted a count of all controls inside the initial GroupBox
private void Form1_Load(object sender, EventArgs e)
{
var c = GetAll(this,typeof(TextBox));
MessageBox.Show("Total Controls: " + c.Count());
}
And it returned the proper count each time, so I think this will work perfectly for what you're looking for :)

In C# (since you tagged it as such) you could use a LINQ expression like this:
List<Control> c = Controls.OfType<TextBox>().Cast<Control>().ToList();
Edit for recursion:
In this example, you first create the list of controls and then call a method to populate it. Since the method is recursive, it doesn't return the list, it just updates it.
List<Control> ControlList = new List<Control>();
private void GetAllControls(Control container)
{
foreach (Control c in container.Controls)
{
GetAllControls(c);
if (c is TextBox) ControlList.Add(c);
}
}
It may be possible to do this in one LINQ statement using the Descendants function, though I am not as familiar with it. See this page for more information on that.
Edit 2 to return a collection:
As #ProfK suggested, a method that simply returns the desired controls is probably better practice. To illustrate this I have modified the code as follows:
private IEnumerable<Control> GetAllTextBoxControls(Control container)
{
List<Control> controlList = new List<Control>();
foreach (Control c in container.Controls)
{
controlList.AddRange(GetAllTextBoxControls(c));
if (c is TextBox)
controlList.Add(c);
}
return controlList;
}

This is an improved version of the recursive GetAllControls() that actually works on private vars:
private void Test()
{
List<Control> allTextboxes = GetAllControls(this);
}
private List<Control> GetAllControls(Control container, List<Control> list)
{
foreach (Control c in container.Controls)
{
if (c is TextBox) list.Add(c);
if (c.Controls.Count > 0)
list = GetAllControls(c, list);
}
return list;
}
private List<Control> GetAllControls(Control container)
{
return GetAllControls(container, new List<Control>());
}

I combined a bunch of the previous ideas into one extension method. The benefits here are that you get the correctly typed enumerable back, plus inheritance is handled correctly by OfType().
public static IEnumerable<T> FindAllChildrenByType<T>(this Control control)
{
IEnumerable<Control> controls = control.Controls.Cast<Control>();
return controls
.OfType<T>()
.Concat<T>(controls.SelectMany<Control, T>(ctrl => FindAllChildrenByType<T>(ctrl)));
}

You can use a LINQ query to do this. This will query everything on the form that is type TextBox
var c = from controls in this.Controls.OfType<TextBox>()
select controls;

It might be the ancient technique, but it works like charm. I used recursion to change the color of all labels of the control. It works great.
internal static void changeControlColour(Control f, Color color)
{
foreach (Control c in f.Controls)
{
// MessageBox.Show(c.GetType().ToString());
if (c.HasChildren)
{
changeControlColour(c, color);
}
else
if (c is Label)
{
Label lll = (Label)c;
lll.ForeColor = color;
}
}
}

I'd like to amend PsychoCoders answer: as the user wants to get all controls of a certain type we could use generics in the following way:
public IEnumerable<T> FindControls<T>(Control control) where T : Control
{
// we can't cast here because some controls in here will most likely not be <T>
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => FindControls<T>(ctrl))
.Concat(controls)
.Where(c => c.GetType() == typeof(T)).Cast<T>();
}
This way, we can call the function as follows:
private void Form1_Load(object sender, EventArgs e)
{
var c = FindControls<TextBox>(this);
MessageBox.Show("Total Controls: " + c.Count());
}

A clean and easy solution (C#):
static class Utilities {
public static List<T> GetAllControls<T>(this Control container) where T : Control {
List<T> controls = new List<T>();
if (container.Controls.Count > 0) {
controls.AddRange(container.Controls.OfType<T>());
foreach (Control c in container.Controls) {
controls.AddRange(c.GetAllControls<T>());
}
}
return controls;
}
}
Get all textboxes:
List<TextBox> textboxes = myControl.GetAllControls<TextBox>();

Don't forget that you can also have a TextBox within other controls other than container controls too. You can even add a TextBox to a PictureBox.
So you also need to check if
someControl.HasChildren = True
in any recursive function.
This is the result I had from a layout to test this code:
TextBox13 Parent = Panel5
TextBox12 Parent = Panel5
TextBox9 Parent = Panel2
TextBox8 Parent = Panel2
TextBox16 Parent = Panel6
TextBox15 Parent = Panel6
TextBox14 Parent = Panel6
TextBox10 Parent = Panel3
TextBox11 Parent = Panel4
TextBox7 Parent = Panel1
TextBox6 Parent = Panel1
TextBox5 Parent = Panel1
TextBox4 Parent = Form1
TextBox3 Parent = Form1
TextBox2 Parent = Form1
TextBox1 Parent = Form1
tbTest Parent = myPicBox
Try this with one Button and one RichTextBox on a form.
Option Strict On
Option Explicit On
Option Infer Off
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim pb As New PictureBox
pb.Name = "myPicBox"
pb.BackColor = Color.Goldenrod
pb.Size = New Size(100, 100)
pb.Location = New Point(0, 0)
Dim tb As New TextBox
tb.Name = "tbTest"
pb.Controls.Add(tb)
Me.Controls.Add(pb)
Dim textBoxList As New List(Of Control)
textBoxList = GetAllControls(Of TextBox)(Me)
Dim sb As New System.Text.StringBuilder
For index As Integer = 0 To textBoxList.Count - 1
sb.Append(textBoxList.Item(index).Name & " Parent = " & textBoxList.Item(index).Parent.Name & System.Environment.NewLine)
Next
RichTextBox1.Text = sb.ToString
End Sub
Private Function GetAllControls(Of T)(ByVal searchWithin As Control) As List(Of Control)
Dim returnList As New List(Of Control)
If searchWithin.HasChildren = True Then
For Each ctrl As Control In searchWithin.Controls
If TypeOf ctrl Is T Then
returnList.Add(ctrl)
End If
returnList.AddRange(GetAllControls(Of T)(ctrl))
Next
ElseIf searchWithin.HasChildren = False Then
For Each ctrl As Control In searchWithin.Controls
If TypeOf ctrl Is T Then
returnList.Add(ctrl)
End If
returnList.AddRange(GetAllControls(Of T)(ctrl))
Next
End If
Return returnList
End Function
End Class

You can use the below Code
public static class ExtensionMethods
{
public static IEnumerable<T> GetAll<T>(this Control control)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => ctrl.GetAll<T>())
.Concat(controls.OfType<T>());
}
}

Here is my Extension method. It's very efficient and it's lazy.
Usage:
var checkBoxes = tableLayoutPanel1.FindChildControlsOfType<CheckBox>();
foreach (var checkBox in checkBoxes)
{
checkBox.Checked = false;
}
The code is:
public static IEnumerable<TControl> FindChildControlsOfType<TControl>(this Control control) where TControl : Control
{
foreach (var childControl in control.Controls.Cast<Control>())
{
if (childControl.GetType() == typeof(TControl))
{
yield return (TControl)childControl;
}
else
{
foreach (var next in FindChildControlsOfType<TControl>(childControl))
{
yield return next;
}
}
}
}

Using reflection:
// Return a list with all the private fields with the same type
List<T> GetAllControlsWithTypeFromControl<T>(Control parentControl)
{
List<T> retValue = new List<T>();
System.Reflection.FieldInfo[] fields = parentControl.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
foreach (System.Reflection.FieldInfo field in fields)
{
if (field.FieldType == typeof(T))
retValue.Add((T)field.GetValue(parentControl));
}
}
List<TextBox> ctrls = GetAllControlsWithTypeFromControl<TextBox>(this);

Here is my extension method for Control, using LINQ, as an adaptation of #PsychoCoder version:
It takes a list of type instead that allows you to not need multiple calls of GetAll to get what you want. I currently use it as an overload version.
public static IEnumerable<Control> GetAll(this Control control, IEnumerable<Type> filteringTypes)
{
var ctrls = control.Controls.Cast<Control>();
return ctrls.SelectMany(ctrl => GetAll(ctrl, filteringTypes))
.Concat(ctrls)
.Where(ctl => filteringTypes.Any(t => ctl.GetType() == t));
}
Usage:
// The types you want to select
var typeToBeSelected = new List<Type>
{
typeof(TextBox)
, typeof(MaskedTextBox)
, typeof(Button)
};
// Only one call
var allControls = MyControlThatContainsOtherControls.GetAll(typeToBeSelected);
// Do something with it
foreach(var ctrl in allControls)
{
ctrl.Enabled = true;
}

Here is the Solution.
https://stackoverflow.com/a/19224936/1147352
I have written this piece of code and selected only the panels, you can add more switches or ifs. in it

public List<Control> GetAllChildControls(Control Root, Type FilterType = null)
{
List<Control> AllChilds = new List<Control>();
foreach (Control ctl in Root.Controls) {
if (FilterType != null) {
if (ctl.GetType == FilterType) {
AllChilds.Add(ctl);
}
} else {
AllChilds.Add(ctl);
}
if (ctl.HasChildren) {
GetAllChildControls(ctl, FilterType);
}
}
return AllChilds;
}

IEnumerable<Control> Ctrls = from Control ctrl in Me.Controls where ctrl is TextBox | ctrl is GroupBox select ctr;
Lambda Expressions
IEnumerable<Control> Ctrls = Me.Controls.Cast<Control>().Where(c => c is Button | c is GroupBox);

Create Method
public static IEnumerable<Control> GetControlsOfType<T>(Control control)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => GetControlsOfType<T>(ctrl)).Concat(controls).Where(c => c is T);
}
And use it Like
Var controls= GetControlsOfType<TextBox>(this);//You can replace this with your control

I'm saldy using VB so, I wrote an extension method. That retrieve all children and sub children of a control
Imports System.Runtime.CompilerServices
Module ControlExt
<Extension()>
Public Function GetAllChildren(Of T As Control)(parentControl As Control) As IEnumerable(Of T)
Dim controls = parentControl.Controls.Cast(Of Control)
Return controls.SelectMany(Of Control)(Function(ctrl) _
GetAllChildren(Of T)(ctrl)) _
.Concat(controls) _
.Where(Function(ctrl) ctrl.GetType() = GetType(T)) _
.Cast(Of T)
End Function
End Module
Then you can use it like, where "btnList" is a control
btnList.GetAllChildren(Of HtmlInputRadioButton).FirstOrDefault(Function(rb) rb.Checked)
In this case, it will select the selected radio button.

I modified from #PsychoCoder.
All controls could be found now (include nested).
public static IEnumerable<T> GetChildrens<T>(Control control)
{
var type = typeof (T);
var allControls = GetAllChildrens(control);
return allControls.Where(c => c.GetType() == type).Cast<T>();
}
private static IEnumerable<Control> GetAllChildrens(Control control)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(c => GetAllChildrens(c))
.Concat(controls);
}

This may work:
Public Function getControls(Of T)() As List(Of T)
Dim st As New Stack(Of Control)
Dim ctl As Control
Dim li As New List(Of T)
st.Push(Me)
While st.Count > 0
ctl = st.Pop
For Each c In ctl.Controls
st.Push(CType(c, Control))
If c.GetType Is GetType(T) Then
li.Add(CType(c, T))
End If
Next
End While
Return li
End Function
I think the function to get all controls you are talking about is only available to WPF.

Here is a tested and working generic solution:
I have a large number UpDownNumeric controls, some in the main form, some in groupboxes within the form.
I want only the one last selected control to change back-color to green, for which I first set all others to white, using this method: (can also expand to grandchildren)
public void setAllUpDnBackColorWhite()
{
//To set the numericUpDown background color of the selected control to white:
//and then the last selected control will change to green.
foreach (Control cont in this.Controls)
{
if (cont.HasChildren)
{
foreach (Control contChild in cont.Controls)
if (contChild.GetType() == typeof(NumericUpDown))
contChild.BackColor = Color.White;
}
if (cont.GetType() == typeof(NumericUpDown))
cont.BackColor = Color.White;
}
}

You can try this if you want :)
private void ClearControls(Control.ControlCollection c)
{
foreach (Control control in c)
{
if (control.HasChildren)
{
ClearControls(control.Controls);
}
else
{
if (control is TextBox)
{
TextBox txt = (TextBox)control;
txt.Clear();
}
if (control is ComboBox)
{
ComboBox cmb = (ComboBox)control;
if (cmb.Items.Count > 0)
cmb.SelectedIndex = -1;
}
if (control is CheckBox)
{
CheckBox chk = (CheckBox)control;
chk.Checked = false;
}
if (control is RadioButton)
{
RadioButton rdo = (RadioButton)control;
rdo.Checked = false;
}
if (control is ListBox)
{
ListBox listBox = (ListBox)control;
listBox.ClearSelected();
}
}
}
}
private void btnClear_Click(object sender, EventArgs e)
{
ClearControls((ControlCollection)this.Controls);
}

Although several other users have posted adequate solutions, I'd like to post a more general approach that may be more useful.
This is largely based on JYelton's response.
public static IEnumerable<Control> AllControls(
this Control control,
Func<Control, Boolean> filter = null)
{
if (control == null)
throw new ArgumentNullException("control");
if (filter == null)
filter = (c => true);
var list = new List<Control>();
foreach (Control c in control.Controls) {
list.AddRange(AllControls(c, filter));
if (filter(c))
list.Add(c);
}
return list;
}

public static IEnumerable<T> GetAllControls<T>(this Control control) where T : Control
{
foreach (Control c in control.Controls)
{
if (c is T)
yield return (T)c;
foreach (T c1 in c.GetAllControls<T>())
yield return c1;
}
}

public IEnumerable<T> GetAll<T>(Control control) where T : Control
{
var type = typeof(T);
var controls = control.Controls.Cast<Control>().ToArray();
foreach (var c in controls.SelectMany(GetAll<T>).Concat(controls))
if (c.GetType() == type) yield return (T)c;
}

For anyone looking for a VB version of Adam's C# code written as an extension of the Control class:
''' <summary>Collects child controls of the specified type or base type within the passed control.</summary>
''' <typeparam name="T">The type of child controls to include. Restricted to objects of type Control.</typeparam>
''' <param name="Parent">Required. The parent form control.</param>
''' <returns>An object of type IEnumerable(Of T) containing the control collection.</returns>
''' <remarks>This method recursively calls itself passing child controls as the parent control.</remarks>
<Extension()>
Public Function [GetControls](Of T As Control)(
ByVal Parent As Control) As IEnumerable(Of T)
Dim oControls As IEnumerable(Of Control) = Parent.Controls.Cast(Of Control)()
Return oControls.SelectMany(Function(c) GetControls(Of T)(c)).Concat(oControls.Where(Function(c) c.GetType() Is GetType(T) Or c.GetType().BaseType Is GetType(T))
End Function
NOTE: I've added BaseType matching for any derived custom controls. You can remove this or even make it an optional parameter if you wish.
Usage
Dim oButtons As IEnumerable(Of Button) = Me.GetControls(Of Button)()

VISUAL BASIC VB.NET
For some of us, who refuse to port 230,000+ lines of code to c# here is my contribution, if only an specific type required, just add a 'where' as needed.
Private Shared Function getAll(control As Control) As IEnumerable(Of Control)
Return control.Controls.Cast(Of Control) _
.SelectMany(Function(f) getAll(f).Concat(control.Controls.Cast(Of Control)))
End Function

Simply:
For Each ctrl In Me.Controls.OfType(Of Button)()
ctrl.Text = "Hello World!"
Next

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.

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");

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

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);
}
}
}
}
}

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.

What is the best way to clear all controls on a form C#?

I do remember seeing someone ask something along these lines a while ago but I did a search and couldn't find anything.
I'm trying to come up with the cleanest way to clear all the controls on a form back to their defaults (e.g., clear textboxes, uncheck checkboxes).
How would you go about this?
What I have come up with so far is something like this:
public static class extenstions
{
private static Dictionary<Type, Action<Control>> controldefaults = new Dictionary<Type, Action<Control>>() {
{typeof(TextBox), c => ((TextBox)c).Clear()},
{typeof(CheckBox), c => ((CheckBox)c).Checked = false},
{typeof(ListBox), c => ((ListBox)c).Items.Clear()},
{typeof(RadioButton), c => ((RadioButton)c).Checked = false},
{typeof(GroupBox), c => ((GroupBox)c).Controls.ClearControls()},
{typeof(Panel), c => ((Panel)c).Controls.ClearControls()}
};
private static void FindAndInvoke(Type type, Control control)
{
if (controldefaults.ContainsKey(type)) {
controldefaults[type].Invoke(control);
}
}
public static void ClearControls(this Control.ControlCollection controls)
{
foreach (Control control in controls)
{
FindAndInvoke(control.GetType(), control);
}
}
public static void ClearControls<T>(this Control.ControlCollection controls) where T : class
{
if (!controldefaults.ContainsKey(typeof(T))) return;
foreach (Control control in controls)
{
if (control.GetType().Equals(typeof(T)))
{
FindAndInvoke(typeof(T), control);
}
}
}
}
Now you can just call the extension method ClearControls like this:
private void button1_Click(object sender, EventArgs e)
{
this.Controls.ClearControls();
}
EDIT: I have just added a generic ClearControls method that will clear all the controls of that type, which can be called like this:
this.Controls.ClearControls<TextBox>();
At the moment it will only handle top level controls and won't dig down through groupboxes and panels.
I know its an old question but just my 2 cents in. This is a helper class I use for form clearing.
using System;
using System.Windows.Forms;
namespace FormClearing
{
class Helper
{
public static void ClearFormControls(Form form)
{
foreach (Control control in form.Controls)
{
if (control is TextBox)
{
TextBox txtbox = (TextBox)control;
txtbox.Text = string.Empty;
}
else if(control is CheckBox)
{
CheckBox chkbox = (CheckBox)control;
chkbox.Checked = false;
}
else if (control is RadioButton)
{
RadioButton rdbtn = (RadioButton)control;
rdbtn.Checked = false;
}
else if (control is DateTimePicker)
{
DateTimePicker dtp = (DateTimePicker)control;
dtp.Value = DateTime.Now;
}
}
}
}
}
And I call the method from any form like this passing a form object as a parameter.
Helper.ClearFormControls(this);
You can extend it for other types of controls. You just have to cast it.
You can loop for control
foreach (Control ctrl in this)
{
if(ctrl is TextBox)
(ctrl as TextBox).Clear();
}
I voted for Nathan's solution, but wanted to add a bit more than a comment can handle.
His is actually very good, but I think the best solution would involve sub-classing each of the control types you might be adding before adding them to the GUI. Have them all implement an interface "Clearable" or something like that (I'm a java programmer, but the concept should be there), then iterate over it as a collection of "Clearable" objects, calling the only method .clear() on each
This is how GUIs really should be done in an OO system. This will make your code easy to extend in the future--almost too easy, you'll be shocked.
Edit: (per Nathan's comment about not changing existing controls)
Perhaps you could create "Container" classes that reference your control (one for each type of control). In a loop like the one you set up in your answer, you could instantiate the correct container, place the real control inside the container and store the container in a collection.
That way you are back to iterating over a collection.
This would be a good simple solution that isn't much more complex than the one you suggested, but infinitely more expandable.
The above solutions seem to ignore nested controls.
A recursive function may be required such as:
public void ClearControl(Control control)
{
TextBox tb = control as TextBox;
if (tb != null)
{
tb.Text = String.Empty;
}
// repeat for combobox, listbox, checkbox and any other controls you want to clear
if (control.HasChildren)
{
foreach(Control child in control.Controls)
{
ClearControl(child)
}
}
}
You don't want to just clear the Text property without checking the controls type.
Implementing an interface, such as IClearable (as suggested by Bill K), on a set of derived controls would cut down the length of this function, but require more work on each control.
Here is the same thing that I proposed in my first answer but in VB, until we get VB10 this is the best we can do in VB because it doesn't support non returning functions in lambdas:
VB Solution:
Public Module Extension
Private Sub ClearTextBox(ByVal T As TextBox)
T.Clear()
End Sub
Private Sub ClearCheckBox(ByVal T As CheckBox)
T.Checked = False
End Sub
Private Sub ClearListBox(ByVal T As ListBox)
T.Items.Clear()
End Sub
Private Sub ClearGroupbox(ByVal T As GroupBox)
T.Controls.ClearControls()
End Sub
<Runtime.CompilerServices.Extension()> _
Public Sub ClearControls(ByVal Controls As ControlCollection)
For Each Control In Controls
If ControlDefaults.ContainsKey(Control.GetType()) Then
ControlDefaults(Control.GetType()).Invoke(Control)
End If
Next
End Sub
Private _ControlDefaults As Dictionary(Of Type, Action(Of Control))
Private ReadOnly Property ControlDefaults() As Dictionary(Of Type, Action(Of Control))
Get
If (_ControlDefaults Is Nothing) Then
_ControlDefaults = New Dictionary(Of Type, Action(Of Control))
_ControlDefaults.Add(GetType(TextBox), AddressOf ClearTextBox)
_ControlDefaults.Add(GetType(CheckBox), AddressOf ClearCheckBox)
_ControlDefaults.Add(GetType(ListBox), AddressOf ClearListBox)
_ControlDefaults.Add(GetType(GroupBox), AddressOf ClearGroupbox)
End If
Return _ControlDefaults
End Get
End Property
End Module
Calling:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Me.Controls.ClearControls()
End Sub
I'm just posting this here so that people can see how to do the same thing in VB.
private void FormReset()
{
ViewState.Clear();
Response.Redirect(Request.Url.AbsoluteUri.ToString());
}
Below are methods I use to clear text from a type of control that implements ITextBox.
I noticed in the example default boolean values are set. I'm sure you can modify it to set default values of boolean components.
Pass the Clear method a control type (TextBox, Label... etc) and a control collection, and it will clear all text from controls that implement ITextBox.
Something like this:
//Clears the textboxes
WebControlUtilities.ClearControls<TextBox>(myPanel.Controls);
The Clear method is meant for a Page or Masterpage. The control collection type may vary. ie. Form, ContentPlaceHolder.. etc
/// <summary>
/// Clears Text from Controls...ie TextBox, Label, anything that implements ITextBox
/// </summary>
/// <typeparam name="T">Collection Type, ie. ContentPlaceHolder..</typeparam>
/// <typeparam name="C">ie TextBox, Label, anything that implements ITextBox</typeparam>
/// <param name="controls"></param>
public static void Clear<T, C>(ControlCollection controls)
where C : ITextControl
where T : Control
{
IEnumerable<T> placeHolders = controls.OfType<T>();
List<T> holders = placeHolders.ToList();
foreach (T holder in holders)
{
IEnumerable<C> enumBoxes = holder.Controls.OfType<C>();
List<C> boxes = enumBoxes.ToList();
foreach (C box in boxes)
{
box.Text = string.Empty;
}
}
}
/// <summary>
/// Clears the text from control.
/// </summary>
/// <typeparam name="C"></typeparam>
/// <param name="controls">The controls.</param>
public static void ClearControls<C>(ControlCollection controls) where C : ITextControl
{
IEnumerable<C> enumBoxes = controls.OfType<C>();
List<C> boxes = enumBoxes.ToList();
foreach (C box in boxes)
{
box.Text = string.Empty;
}
}
I am using this method for clear the controls on windows form. This is sample for textbox, numericupdown and datetimepicker.
public void Clear()
{
foreach (var item in this.Controls)
{
if (item is TextBox)
{
TextBox text = (TextBox)item;
text.Clear();
//text.Text = "";
//text.Text=String.Empty;
//text.Text = null;
}
else if (item is NumericUpDown)
{
NumericUpDown numeric = (NumericUpDown)item;
numeric.Value = 0;
}
else if (item is DateTimePicker)
{
DateTimePicker dateTimePicker = (DateTimePicker)item;
dateTimePicker.Value = DateTime.Now;
}
}
//Focus the cursor where you want to
txtBookName.Focus();
}

Categories