Поиск рекурсивного контроля с помощью LINQ
Если бы я хотел найти флажки на странице ASP.NET, я мог бы использовать следующий запрос LINQ.
var checkBoxes = this.Controls
.OfType<CheckBox>()
.TakeWhile<CheckBox>(cb => cb.Checked);
Это прекрасно работает, если флажки вложены в текущую коллекцию элементов управления, но я хотел бы знать, как расширить поиск, углубившись в наборы элементов управления элементов верхнего уровня.
Вопрос был задан здесь:
Поиск элементов управления, которые используют определенный интерфейс в ASP.NET
Получив ответы, не относящиеся к LINQ, у меня уже есть собственная версия рекурсивного поиска по типу и идентификатору в качестве методов расширения, но я просто удивился, насколько это легко сделать в LINQ?
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 и использовать его как метод.)
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;
}
Мое предложение сделать 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
выглядит странно, но это единственный способ, которым я знаю, чтобы "сгладить" рекурсивный вызов.