Pesquisa de controle recursiva com LINQ

Se eu quisesse encontrar checkboxs de seleção marcadas em uma página ASP.NET eu poderia usar a seguinte consulta LINQ.

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

Isso funciona bem se as checkboxs de seleção estiverem aninhadas na coleção de controle atual, mas gostaria de saber como estender a pesquisa explorando as collections de controle dos controles de nível superior.

A pergunta foi feita aqui:

Encontrando controles que usam uma determinada interface no ASP.NET

E recebi respostas não-LINQ, eu já tenho minha própria versão de uma busca de controle recursiva no tipo e ID como methods de extensão, mas eu só queria saber o quão fácil isso é fazer no LINQ?

Pegue a verificação de tipo / ID fora da recursion, portanto, basta ter um método “me dê todos os controles, recursivamente”, por exemplo

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

Isso é um pouco ineficiente (em termos de criar muitos iteradores), mas duvido que você tenha uma tree muito profunda.

Você pode então escrever sua consulta original como:

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

(EDIT: AllControls alterado para GetAllControls e usá-lo corretamente como um 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; } 

Agora, com base na function acima, podemos fazer algo assim:

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

Minha sugestão para tornar os AllControls recursivos é:

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

O segundo foreach parece estranho, mas essa é a única maneira que conheço de “achatar” a chamada recursiva.