Búsqueda de control recursivo con LINQ

Si quisiera encontrar casillas marcadas en una página ASP.NET, podría usar la siguiente consulta LINQ.

var checkBoxes = this.Controls .OfType() .TakeWhile(cb => cb.Checked); 

Eso funciona bien si las casillas de verificación están anidadas en la colección de control actual, pero me gustaría saber cómo ampliar la búsqueda profundizando en las colecciones de control de los controles de nivel superior.

La pregunta fue hecha aquí:

Encontrar controles que utilizan una determinada interfaz en ASP.NET

Y al recibir respuestas que no son de LINQ, ya tengo mi propia versión de una búsqueda de control recursivo sobre el tipo y la ID como métodos de extensión, pero me preguntaba qué fácil es hacerlo en LINQ.

Realice la comprobación de tipo / ID fuera de la recursión, de modo que simplemente tenga el método “dígame todos los controles recursivamente”, por ejemplo

 public static IEnumerable GetAllControls(this Control parent) { foreach (Control control in parent.Controls) { yield return control; foreach(Control descendant in control.GetAllControls()) { yield return descendant; } } } 

Eso es algo ineficiente (en términos de crear muchos iteradores) pero dudo que tengas un árbol muy profundo.

Luego puede escribir su consulta original como:

 var checkBoxes = this.GetAllControls() .OfType() .TakeWhile(cb => cb.Checked); 

(EDITAR: Cambió AllControls a GetAllControls y lo usa correctamente como método).

 public static IEnumerable AllControls(this Control container) { //Get all controls var controls = container.Controls.Cast(); //Get all children var children = controls.Select(c => c.AllControls()); //combine controls and children var firstGen = controls.Concat(children.SelectMany(b => b)); return firstGen; } 

Ahora, basándonos en la función anterior, podemos hacer algo como esto:

 public static Control FindControl(this Control container, string Id) { var child = container.AllControls().FirstOrDefault(c => c.ID == Id); return child; } 

Mi sugerencia para hacer que AllControls recursivo es:

  public static IEnumerable AllControls(this Control parent) { foreach (Control control in parent.Controls) { yield return control; } foreach (Control control in parent.Controls) { foreach (Control cc in AllControls(control)) yield return cc; } } 

El segundo foreach ve raro, pero esta es la única forma que conozco de “aplanar” la llamada recursiva.