39

I have been pondering how I can get all controls on a page and then perform a task on them in this related question:

How to Search Through a C# DropDownList Programmatically

I need code that can scan the page, get all DropDownList Controls and return them in a list.

I'm currently having to edit each individual control, I would rather be able to dynamically loop over each control to preform my task.

Community
  • 1
  • 1
Anicho
  • 2,647
  • 11
  • 48
  • 76

8 Answers8

63

Check my previous SO answer.

Basically, the idea is to wrap the recursion of iterating through the controls collection using :

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

and to use it :

List<DropDownList> allControls = new List<DropDownList>();
GetControlList<DropDownList>(Page.Controls, allControls )
foreach (var childControl in allControls )
{
//     call for all controls of the page
}

[Edited 11/26/2013]: here is a more elegant way to reach this goal. I wrote two extensions methods that can walk the control tree in both directions. The methods are written in a more Linq way as it produces an 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;
                }
            }
        }
    }
}

Thanks to the this keyword, these methods are extensions methods and can simplify the code.

For example, to find all DropDownList in the page, you can simply call:

var allDropDowns = this.Page.FindControl<DropDownList>();

Because of the use of the yield keyword, and because Linq is smart enough to defer execution of the enumeration, you can call (for example):

var allDropDowns = this.Page.FindDescendants<DropDownList>();
var firstDropDownWithCustomClass = allDropDowns.First(
    ddl=>ddl.CssClass == "customclass"
    );

The enumeration will stop as soon as the predicate in the First method is satisfied. The whole control tree won't be walked.

Community
  • 1
  • 1
Steve B
  • 36,818
  • 21
  • 101
  • 174
  • 1
    Most comprehensive answer and it works like a charm cheers buddy. – Anicho Sep 09 '11 at 15:03
  • http://msdn.microsoft.com/en-us/library/yt340bh4.aspx Good article on msdn is well. – Anicho Sep 12 '11 at 17:45
  • I like it. It's good that I no longer need to use a yield keyword, which made it so the recursive function always ran even though I declared Visual Studio to step over it. – JonathanWolfson Jan 31 '13 at 18:31
  • Would be better if you added example of actually using this extension method. E.g., `foreach (var tbox in Page.GetAllControls()) { tbox.Text = box.Text.Trim(); }` in a direct answer to the original question. – Gary Walker Aug 16 '14 at 01:11
  • Could I please have a sample ZIP file project showing how the above works? I've tried to paste the code into App_Code as a separate class and I get errors like "this" saying "type expected". Also an example of how to iterate through ANY or all controls on a page? I am after everything, not just dropdownlists. Thanks – Fandango68 Sep 01 '15 at 01:47
  • 1
    @Fernando68: you should ask a specific question in SO... the code I showed is from a larger project... I can't publish it – Steve B Sep 01 '15 at 07:40
  • @SteveB Can't we do this with Recursive methods? – Ali.Rashidi Jan 29 '17 at 11:35
  • @Ali.Rashidi,you should probably be able to this using recursion. FindDescendants **is** recursive. FindAncestor may be recursive, but I guess it's more efficient without – Steve B Jan 29 '17 at 15:02
16
foreach (DropDownList dr in this.Page.Form.Controls.OfType<DropDownList>())
{

}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
marko
  • 10,684
  • 17
  • 71
  • 92
  • Yeah! You need to iterate each controls Controls collection - recursion. But these extension methods I think is really useful, because they make your code more compact - more work in fewer lines. – marko Sep 11 '11 at 07:02
  • I fail to see how this answer would ever work correctly for any page with controls containing other controls. As @JamesJohnson pointed out, to correctly find all DropDownList's you would need to recursively search through ALL controls(as many of the other answers have done), not just top level controls. In my implementation, this answer did not return all the controls present on my page. Am I missing anything with this? – David Rogers Apr 19 '16 at 19:58
9

Here is a recursive version that returns a control collection of the requested type instead of utilizing another argument:

using System.Collections.Generic;
using System.Web.UI;
// ...
public static List<T> GetControls<T>(ControlCollection Controls)
where T : Control {
  List<T> results = new List<T>();
  foreach (Control c in Controls) {
    if (c is T) results.Add((T)c);
    if (c.HasControls()) results.AddRange(GetControls<T>(c.Controls));
  }
  return results;
}

Insert into your class (static optional).

Greg Searle
  • 459
  • 5
  • 3
6

Had this very question and while I found Steve B's answer useful, I wanted an extension method, so re-factored it:

    public static IEnumerable<T> GetControlList<T>(this ControlCollection controlCollection) where T : Control
    {
        foreach (Control control in controlCollection)
        {
            if (control is T)
            {
                yield return (T)control;
            }

            if (control.HasControls())
            {
                foreach (T childControl in control.Controls.GetControlList<T>())
                {
                    yield return childControl;
                }
            }
        }
    }
Andrew
  • 789
  • 7
  • 13
bicbmx
  • 817
  • 10
  • 8
  • I get intelli-sense error on the "this" saying "Type expected". I am simply adding this to a class in app_code folder. – Fandango68 Sep 01 '15 at 01:52
1

Looping through controls on a page isn't hard - you just have to look within each control for more controls.

You could do something like

foreach(var control in Page)
{
    if(control is DropDownList)
    {
        //Do whatever
    }
    else
    {
        //Call this function again to search for controls within this control
    }
}
Mark Williams
  • 583
  • 7
  • 18
  • 2
    That's what the else is for...//Call this function again to search for controls within this control...that's calling for something recurisve... – Mark Williams Sep 09 '11 at 13:46
1

You can use recursive logic to get all of the controls, like this:

private void PopulateSelectList(Control parentCtrl, List<DropDownList> selectList)
{
    foreach (Control ctrl in parentCtrl.Controls)
    {
        if (ctrl is DropDownList)
        {
            selectList.Add(((DropDownList)ctrl);
            continue;
        }
        FindAllControls(ctrl, selectList);
    }
}
James Johnson
  • 45,496
  • 8
  • 73
  • 110
0

This works if you use the form components from system.web.ui however this does not work when you use them from system.web.mvc apparently so i came up with the following work around.

for (Int32 idx = 0; idx < formCollection.Count; idx += 1)
                    {
                    String Name = formCollection.Keys[idx];
                    String value = formCollection[idx];

                    if (Name.Substring(0, 3).ToLower() == "chk")

                        {
                        Response.Write(Name + " is a checkbox <br/>");
                        }
                    else if (Name.Substring(0, 5).ToLower() == "txtar")
                        {
                        Response.Write(Name + " is a text area <br/>");
                        }
                    else if (Name.Substring(0, 2).ToLower() == "rd")
                        {
                        Response.Write(Name + " is a RadioButton <br/>");
                        }

                    }

This works for me however i found out that radio button if not selected is null so doesnt return anything which is ok i dont have to write anything to the database if it is null

0
        var dropDownLists = new List<DropDownList>();
        foreach (var control in this.Controls)
        {
            if (control is DropDownList)
            {
                dropDownLists.Add( (DropDownList)control );
            }
        }
dudeNumber4
  • 4,217
  • 7
  • 40
  • 57