Reputation: 3451
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?
Upvotes: 24
Views: 75059
Reputation: 11
Here is a static method that iterates through a specified container or iterates through all containers in a form. That depends on what is specified in the cParentControl
and bChildContainers
. In the properties window look for Tag and give each control a Tag name, so you can reference each control by its
Tag name instead of its control name.
public static Control GetAllControls(Control cParentControl, bool bChildContainers) {
foreach(Control cChildControl in cParentControl.Controls.OfType<Control>().ToList()) {
//=> This line is what iterates through all child containers
//
if(bChildContainers is true) { GetAllControls(cChildControl, bChildContainers); }
if(cChildControl is TextBox tTextBox) {
if(tTextBox != null && tTextBox.Tag != null) {
if(tTextBox.Tag.Equals("TxtTextBox1")) {
tTextBox.TextChanged += (s, e) => { /* TextChanged code here for the textbox event */ };
}
if(tTextBox.Tag.Equals("TxtTextBox2")) {
tTextBox.TextChanged += (s, e) => { /* TextChanged code here for the textbox event */ };
}
// And so on ...
}
}
}
return cParentControl;
}
To get textboxes in a specified container "GroupBox, Panel, etc."
//=> Gets all TextBoxes in a spcified container e.g. GrpGroupBox1.
//
e.g. GetAllControls(GrpGroupBox1, false);
//=> If GrpGroupBox1 has child containers, then bChildContainers will
// be set to true.
e.g. GetAllControls(GrpGroupBox1, true);
//=> Gets all TextBoxes in the form and all child containers:
//
e.g. GetAllControls(this, true);
Hope this helps.
Upvotes: 1
Reputation:
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:
Solution:
i.e:
foreach (Control control in myContainer.Controls)
{
if (control is TextBox) { /* Do Something */ }
}
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);
}
}
}
Upvotes: 1
Reputation: 2416
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;
}
Upvotes: 0
Reputation: 112269
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;
}
});
Upvotes: 40
Reputation: 2086
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!
Upvotes: 8
Reputation: 86
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.
Upvotes: 3
Reputation: 4895
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;
}
}
Upvotes: 1
Reputation: 4188
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);
Upvotes: 2
Reputation: 203802
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;
Upvotes: 22
Reputation: 27336
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 :)
Upvotes: 1