Рекурсивный поиск управления с помощью LINQ

Если бы я хотел найти отмеченные флажки на странице ASP.NET, я мог бы использовать следующий запрос LINQ.

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

Это отлично работает, если флажки вложены в текущую коллекцию элементов управления, но я хотел бы знать, как расширить поиск, углубившись в коллекции элементов управления верхнего уровня.

Здесь был задан вопрос:

Поиск элементов управления, использующих определенный интерфейс в ASP.NET

И получил ответы, отличные от LINQ, у меня уже есть собственная версия рекурсивного поиска по типу и идентификатору в качестве методов расширения, но мне просто интересно, насколько легко это сделать в LINQ?


person PhilGriffin    schedule 31.10.2008    source источник


Ответы (3)


Возьмите проверку типа / идентификатора из рекурсии, поэтому просто используйте метод «дать мне все элементы управления рекурсивно», например

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

Это несколько неэффективно (с точки зрения создания большого количества итераторов), но я сомневаюсь, что у вас будет очень дерево.

Затем вы можете написать свой исходный запрос как:

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

(РЕДАКТИРОВАТЬ: изменен AllControls на GetAllControls и правильно использовать его как метод.)

person Jon Skeet    schedule 31.10.2008
comment
Это работает, но если вы использовали сторонние элементы управления и свои собственные серверные элементы управления, как мы на моем текущем рабочем месте, вы можете получить несколько хороших уровней вложенности элементов управления. Я полагаю, что страница ASP.NET - это просто пример более общей проблемы, проблемы детализации вложенных коллекций в Linq. - person PhilGriffin; 31.10.2008
comment
Раньше я использовал именно эту технику. (Если вы хотите получить действительно сладкое, вы можете сделать это как одно выражение с помощью Y Combinator!) Кстати, не следует ли использовать AllControls как метод? - person yfeldblum; 31.10.2008
comment
@Phil: Да, он может упасть на несколько уровней и быть немного неэффективным - но есть ли у вас основания полагать, что это станет значительным узким местом? Сначала сделайте самое простое, что работает, а затем оптимизируйте, если это действительно проблема. (например, один вызов базы данных может превысить эту стоимость.) - person Jon Skeet; 31.10.2008
comment
@Jon, у меня уже много лет есть решение проблемы с помощью рекурсивного поиска, который есть в ссылке в моем вопросе. Просто пытаюсь сделать свой день более интересным, изобретая велосипед, ведь сегодня пятница :) Все равно спасибо. - person PhilGriffin; 31.10.2008
comment
Аналогично новому вопросу: stackoverflow.com/questions/3419159/. Мне было бы интересно узнать правильный способ используйте функцию Descendants LINQ для выбора рекурсивного элемента управления. (Возможно, обновите и этот вопрос / ответ?) - person JYelton; 06.08.2010

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

Теперь, основываясь на приведенной выше функции, мы можем сделать что-то вроде этого:

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

Мое предложение сделать AllControls рекурсивным:

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

Второй foreach выглядит странно, но это единственный известный мне способ «сгладить» рекурсивный вызов.

person Community    schedule 31.10.2008