Pasa por todos los controles de un formulario, incluso aquellos en las cajas de grupo

Me gustaría agregar un evento a todos los cuadros de texto en mi formulario:

foreach (Control C in this.Controls) { if (C.GetType() == typeof(System.Windows.Forms.TextBox)) { C.TextChanged += new EventHandler(C_TextChanged); } } 

El problema es que están almacenados en varias cajas de grupo y mi ciclo no las ve. Podría recorrer los controles de cada grupo individualmente pero ¿es posible hacerlo todo de una manera simple en un ciclo?

La colección Controls de formularios y controles de contenedor contiene solo los elementos secundarios inmediatos. Para obtener todos los controles, debe atravesar el árbol de controles y aplicar esta operación recursivamente

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

Nota: La forma deriva (indirectamente) de Control también y todos los controles tienen una colección de Controls . Entonces puede llamar al método de esta manera en su forma:

 AddTextChangedHandler(this); 

Una solución más general sería crear un método de extensión que aplique una acción recursivamente a todos los controles. En una clase estática (por ejemplo, WinFormsExtensions ) agregue este método:

 public static void ForAllControls(this Control parent, Action action) { foreach (Control c in parent.Controls) { action(c); ForAllControls(c, action); } } 

El espacio de nombres de las clases estáticas debe ser “visible”, es decir, agregue una statement de using apropiada si está en otro espacio de nombres.

Entonces puedes llamarlo así, donde this es la forma; también puede reemplazar this por un formulario o variable de control cuyos controles nesteds deben verse afectados:

 this.ForAllControls(c => { if (c.GetType() == typeof(TextBox)) { c.TextChanged += C_TextChanged; } }); 

Algunas herramientas sencillas y de uso general hacen que este problema sea muy sencillo. Podemos crear un método simple que atraviese todo el árbol de control, devolviendo una secuencia de todos sus hijos, todos sus hijos, etc., cubriendo todos los controles, no solo a una profundidad fija. Podríamos usar la recursión, pero al evitar la recursión funcionará mejor.

 public static IEnumerable GetAllChildren(this Control root) { var stack = new Stack(); stack.Push(root); while (stack.Any()) { var next = stack.Pop(); foreach (Control child in next.Controls) stack.Push(child); yield return next; } } 

Usando esto podemos obtener todos los niños, filtrar aquellos del tipo que necesitamos, y luego adjuntar el controlador con mucha facilidad:

 foreach(var textbox in GetAllChildren().OfType()) textbox.TextChanged += C_TextChanged; 

Prueba esto

 AllSubControls(this).OfType().ToList() .ForEach(o => o.TextChanged += C_TextChanged); 

donde AllSubControls es

 private static IEnumerable AllSubControls(Control control) => Enumerable.Repeat(control, 1) .Union(control.Controls.OfType() .SelectMany(AllSubControls) ); 

LINQ es genial!

No he visto a nadie usando linq y / o rendimiento así que aquí va:

 public static class UtilitiesX { public static IEnumerable GetEntireControlsTree(this Control rootControl) { yield return rootControl; foreach (var childControl in rootControl.Controls.Cast().SelectMany(x => x.GetEntireControlsTree())) { yield return childControl; } } public static void ForEach(this IEnumerable en, Action action) { foreach (var obj in en) action(obj); } } 

Puede usarlo para el deseo de su corazón:

 someControl.GetEntireControlsTree().OfType().ForEach(x => x.Click += someHandler); 

Como ya dijiste, tendrás que ir más allá de solo recorrer cada elemento de tu forma. Esto, lamentablemente, implica el uso de un bucle nested.

En el primer ciclo, recorre cada elemento. SI el elemento es de tipo GroupBox, entonces sabrá que deberá pasar por cada elemento dentro del grupo, antes de continuar; de lo contrario, agrega el evento como siempre.

Parece que tienes una comprensión decente de C #, así que no te daré ningún código; puramente para asegurar que desarrolles todos los conceptos importantes que están involucrados en la resolución de problemas 🙂

solo puede recorrer formularios abiertos en formularios de Windows mediante la recostackción de formularios, por ejemplo, para establecer la posición de inicio de Windows para todos los formularios abiertos:

 public static void setStartPosition() { FormCollection fc = Application.OpenForms; foreach(Form f in fc) { f.StartPosition = FormStartPosition.CenterScreen; } } 

Sé que este es un tema anterior, pero diría que el fragmento de código de http://backstreet.ch/coding/code-snippets/mit-c-rekursiv-durch-form-controls-loopen/ es una solución inteligente para este problema.

Utiliza un método de extensión para ControlCollection.

 public static void ApplyToAll(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); } } } 

Ahora, para asignar un evento a todos los controles del cuadro de texto, puede escribir un enunciado como (donde ‘este’ es el formulario):

 this.Controls.ApplyToAll("", control => { control.TextChanged += SomeEvent }); 

Opcionalmente puede filtrar los controles por sus tags.