views:

1907

answers:

2

If I wanted to find checked check boxes on an ASP.NET page I could use the following Linq.

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

That works fine if the checkboxes are nested in the current control collection, but I'd like to know how to extend the search by drilling down into the control collections of the top-level controls.

The question was asked here:

Finding controls that use a certain interface in ASP.NET

And received non-Linq answers, I already have my own version of a recursive control search on type and ID as extension methods, but just wondered how easy this is to do in Linq?

+10  A: 

Take the type/ID checking out of the recursion, so just have a "give me all the controls, recursively" method, e.g.

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

That's somewhat inefficient (in terms of creating lots of iterators) but I doubt that you'll have a very deep tree.

You can then write your original query as:

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

(EDIT: Changed AllControls to GetAllControls and use it properly as a method.)

Jon Skeet
That works, but if you were using third party controls and your own server controls as we do in my current workplace, you can get a good few levels of control nesting. I suppose an ASP.NET page is just an example of a more general problem, that of drilling down through nested collections in Linq.
PhilGriffin
I have used just this technique before. (If you want to get really sweet, you could do this as one expression with the Y Combinator!) BTW shouldn't AllControls be used as a method?
Justice
@Justice: Oops, thanks - will fix :)
Jon Skeet
@Phil: Yes, it may go down a few levels and be a little bit inefficient - but do you have any reason to believe it'll be a *significant* bottleneck? Do the simplest thing that works first, and then optimise if it's really a problem. (e.g. a single database call is likely to overwhelm this cost.)
Jon Skeet
@Jon, I've had a solution to the problem for years in the kind of recursive searches that are in the link in my question. Just trying to make my day more interesting reinventing the wheel, it is a Friday after all :) Thanks anyway.
PhilGriffin
Similar to new question: http://stackoverflow.com/questions/3419159/how-to-get-all-child-controls-of-a-winform-of-a-specific-type-button-textbox/3419209#3419209 -- I would be interested in knowing the correct way to use the `Descendants` LINQ function for recursive control selection. (Perhaps updating this question/answer also?)
JYelton
A: 

My suggestion to make the AllControls recursive is:

    public static IEnumerable<Control> 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;
        } 
    }

the second foreach looks weird but this is the only way i know to "flatten" the recursive call.