Recursive control search with LINQ

Jeśli chciałem znaleźć zaznaczone pola wyboru na ASP.NET page I could use the following LINQ query.

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

To działa dobrze, jeśli pola wyboru są zagnieżdżone w bieżącej kolekcji kontroli, ale chciałbym wiedzieć, jak Rozszerzyć wyszukiwanie, wiercąc w dół do kolekcji kontroli najwyższego poziomu.

Pytanie zostało zadane tutaj:

Znajdowanie kontrolek, które używają określonego interfejsu w ASP.NET

I otrzymałem odpowiedzi nie LINQ, już mam moja własna wersja rekurencyjnego wyszukiwania kontrolnego na type i ID jako metody rozszerzenia, ale zastanawiałem się, jak łatwo to zrobić w LINQ?

Author: Community, 2008-10-31

3 answers

Weź Typ / ID z rekurencji, więc po prostu miej metodę "daj mi wszystkie kontrolki, rekurencyjnie", np.

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

To trochę nieefektywne (jeśli chodzi o tworzenie wielu iteratorów), ale wątpię, że będziesz miał bardzo głębokie drzewo.

Możesz następnie napisać swoje oryginalne zapytanie jako:

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

(EDIT: zmieniono Ustawienia AllControls na GetAllControls i używaj ich jako metody.)

 43
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-31 16:06:52
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;
}

Teraz na podstawie powyższej funkcji możemy zrobić coś takiego:

public static Control FindControl(this Control container, string Id)
{
    var child = container.AllControls().FirstOrDefault(c => c.ID == Id);
    return child;
}
 2
Author: Arthur Dzhelali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-28 15:55:04

Moja sugestia, aby AllControls rekurencyjny jest:

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

Drugi foreach wygląda dziwnie, ale jest to jedyny sposób, jaki znam, aby "spłaścić" wywołanie rekurencyjne.

 0
Author: Peter Mortensen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-28 15:53:23