Recursive control search with LINQ

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

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 I just wondered how easy this is to do in LINQ?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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.)

Method 2

public static IEnumerable<Control> AllControls(this Control container)
{
    //Get all controls
    var controls = container.Controls.Cast<Control>();

    //Get all children
    var children = controls.Select(c => c.AllControls());

    //combine controls and children
    var firstGen = controls.Concat(children.SelectMany(b => b));

    return firstGen;
}

Now based on the above function, we can do something like this:

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

Method 3

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.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x