Comprobar my previous SO answer.
Básicamente, la idea es envolver la recursividad de iteración a través de la colección de controles usando:
private void GetControlList<T>(ControlCollection controlCollection, List<T> resultCollection)
where T : Control
{
foreach (Control control in controlCollection)
{
//if (control.GetType() == typeof(T))
if (control is T) // This is cleaner
resultCollection.Add((T)control);
if (control.HasControls())
GetControlList(control.Controls, resultCollection);
}
}
y utilizarlo:
List<DropDownList> allControls = new List<DropDownList>();
GetControlList<DropDownList>(Page.Controls, allControls)
foreach (var childControl in allControls)
{
// call for all controls of the page
}
[Editado 26/11/2013] : aquí hay una forma más elegante de alcanzar este objetivo. Escribí dos métodos de extensiones que pueden recorrer el árbol de control en ambas direcciones. Los métodos se escriben de una manera más LINQ, ya que produce un enumerable:
/// <summary>
/// Provide utilities methods related to <see cref="Control"/> objects
/// </summary>
public static class ControlUtilities
{
/// <summary>
/// Find the first ancestor of the selected control in the control tree
/// </summary>
/// <typeparam name="TControl">Type of the ancestor to look for</typeparam>
/// <param name="control">The control to look for its ancestors</param>
/// <returns>The first ancestor of the specified type, or null if no ancestor is found.</returns>
public static TControl FindAncestor<TControl>(this Control control) where TControl : Control
{
if (control == null) throw new ArgumentNullException("control");
Control parent = control;
do
{
parent = parent.Parent;
var candidate = parent as TControl;
if (candidate != null)
{
return candidate;
}
} while (parent != null);
return null;
}
/// <summary>
/// Finds all descendants of a certain type of the specified control.
/// </summary>
/// <typeparam name="TControl">The type of descendant controls to look for.</typeparam>
/// <param name="parent">The parent control where to look into.</param>
/// <returns>All corresponding descendants</returns>
public static IEnumerable<TControl> FindDescendants<TControl>(this Control parent) where TControl : Control
{
if (parent == null) throw new ArgumentNullException("control");
if (parent.HasControls())
{
foreach (Control childControl in parent.Controls)
{
var candidate = childControl as TControl;
if (candidate != null) yield return candidate;
foreach (var nextLevel in FindDescendants<TControl>(childControl))
{
yield return nextLevel;
}
}
}
}
}
Gracias a la palabra clave this
, estos métodos son métodos de extensiones y pueden simplificar el código.
Por ejemplo, para encontrar todos DropDownList
en la página, sólo tiene que llamar:
var allDropDowns = this.Page.FindControl<DropDownList>();
Debido a la utilización de la palabra clave yield
, y debido a LINQ es lo suficientemente inteligente como para aplazar la ejecución de la enumeración, se puede llamar a (por ejemplo):
var allDropDowns = this.Page.FindDescendants<DropDownList>();
var firstDropDownWithCustomClass = allDropDowns.First(
ddl=>ddl.CssClass == "customclass"
);
la enumeración se detendrá tan pronto como el predicado en el método First
está satisfecho. El árbol de control completo no será caminado.
La respuesta más completa y funciona como un amuleto de aclamaciones. – Anicho
http://msdn.microsoft.com/en-us/library/yt340bh4.aspx Buen artículo sobre msdn está bien. – Anicho
Me gusta. Es bueno que ya no necesite usar una palabra clave yield, lo que hizo que la función recursiva siempre se ejecutara aunque declaré que Visual Studio la pisó. – JonathanWolfson