5

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?

1

5 Answers 5

12

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); 
Sign up to request clarification or add additional context in comments.

10 Comments

Wow, that is an extreme, overly complicated way to find a control with a tab index of 9. Kudos for effort though.
Your method only finds a control with a tab index of 9. Mine is an extension method that effectively enables LINQ to WinForms. It's the same approach used by LINQ to XML. And it's hardly complicated, it's simple recursion like many of the other answers. The only difference being I'm filtering on type which makes the predicate easier to write.
+1 for checking on the type and the yield return will also ensure that no more controls that need to be evaluated will be evaluated. Could also easily be applied to many other conditions without modifying the extension method and could check on properties specific to the type (like IsChecked) as well! @Ed Swangren what if you don't want to check on TabIndex anymore? A query (or predicate) is not a bad idea.
@Ed: the elegance of this solution appears when, in a near future, the need to find a control that has no child controls appears. You can reuse the code, just call it with a different comparison, instead of extending the code base with a new, nearly identical method for that new case.
@EdS. Good luck on always writing a different piece of code that only does what it needs to that day. With Josh's code, you write it once only, test it once only, and use it whenever you need to find any control on any form.
|
4

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

Comments

2
void iterateControls(Control ctrl) { foreach(Control c in ctrl.Controls) { iterateControls(c); } } 

Comments

2

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

Comments

0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.