49

I have a complex asp.net form,having even 50 to 60 fields in one form like there is Multiview, inside MultiView I have a GridView, and inside GridView I have several CheckBoxes.

Currently I am using chaining of the FindControl() method and retrieving the child ID.

Now, my question is that is there any other way/solution to find the nested control in ASP.NET.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
santosh singh
  • 27,666
  • 26
  • 83
  • 129
  • 1
    What do you mean with chaining in this context? FindControl does only find controls inside its NamingContainer, therefore if you would use `Page.FindControl` you wouldn't find controls inside of a GridView but only controls that belong to the page's NamingContainer. There is no recursive check for finding nested controls. – Tim Schmelter Feb 10 '11 at 10:22

9 Answers9

73

If you're looking for a specific type of control you could use a recursive loop like this one - http://weblogs.asp.net/eporter/archive/2007/02/24/asp-net-findcontrol-recursive-with-generics.aspx

Here's an example I made that returns all controls of the given type

/// <summary>
/// Finds all controls of type T stores them in FoundControls
/// </summary>
/// <typeparam name="T"></typeparam>
private class ControlFinder<T> where T : Control 
{
    private readonly List<T> _foundControls = new List<T>();
    public IEnumerable<T> FoundControls
    {
        get { return _foundControls; }
    }    

    public void FindChildControlsRecursive(Control control)
    {
        foreach (Control childControl in control.Controls)
        {
            if (childControl.GetType() == typeof(T))
            {
                _foundControls.Add((T)childControl);
            }
            else
            {
                FindChildControlsRecursive(childControl);
            }
        }
    }
}
jimmystormig
  • 10,672
  • 1
  • 29
  • 28
  • I see this a lot in C# code. Why return IEnumerable in FoundControls Get property, why _foundControls is always a List<> in this class? I mean, I understand that List implements IEnumerable, but what's the gain? There must be some, since as I've said I see this pattern frequently. Thanks –  Jul 10 '13 at 18:01
  • 2
    Great code, many thanks. I had an attempt at writing this kind of thing myself and got an ugly spaghetti ball. This is so much better. @MassStrike, if you use the most generalised type, your code is more portable. It is a great habit to get into, which is why you see it everywhere. – Steve Hibbert Jul 29 '14 at 10:15
  • 4
    Warning: The given solution didn't work for me right off the bat, because it never accused the same type. _I think_ that if the childControl is a user control, GetType() is not enough. **childControl.GetType().BaseType** did work for me. Hope it helps others. neverthless, thanks @Jimmy for the solution :) – StinkyCat Jul 31 '14 at 12:52
  • Thanks so much StinkyCat for your comment! Yes, doing .BaseType made a world of difference for user controls. – John Suit Sep 03 '14 at 13:29
19

Late as usual. If anyone is still interested in this there are a number of related SO questions and answers. My version of recursive extension method for resolving this:

public static IEnumerable<T> FindControlsOfType<T>(this Control parent)
                                                        where T : Control
{
    foreach (Control child in parent.Controls)
    {
        if (child is T)
        {
            yield return (T)child;
        }
        else if (child.Controls.Count > 0)
        {
            foreach (T grandChild in child.FindControlsOfType<T>())
            {
                yield return grandChild;
            }
        }
    }
}
Community
  • 1
  • 1
David Clarke
  • 12,888
  • 9
  • 86
  • 116
  • 1
    @Gaolai Peng how is it not working? I use this routine in a number of places and haven't had a problem with it. – David Clarke Feb 02 '12 at 22:00
  • it seems that this method doesn't find controls of type T in the decendants of grandChild. It only stops at grandChild. Am I right? – Twisted Whisper Nov 02 '12 at 09:33
  • No it calls itself recursively to traverse the tree of controls. Ref child.FindControlsOfType() – David Clarke Nov 02 '12 at 19:24
  • 3
    It's important to note that this method must be in a static class, because it creates an extension method for type Control. Otherwise you will get this compile error: "Extension method must be defined in a non-generic static class." – Ryan Kyle Jan 31 '15 at 02:37
  • I know this is old but hoping someone still will see this. Can this be used to find gridviews that are dynamically created that are embedded in panels? If yes, how would you call this method? – Mike Apr 27 '16 at 13:40
  • It should work, it recursively traverses the tree of controls so if the `GridView` exists it should find it. It's an extension method so needs a `Control` to start with e.g. `parentControl.FindControlsOfType()` (not tested). – David Clarke Apr 27 '16 at 21:02
13

All the highlighted solutions are using recursion (which is performance costly). Here is cleaner way without recursion:

public T GetControlByType<T>(Control root, Func<T, bool> predicate = null) where T : Control 
{
    if (root == null) {
        throw new ArgumentNullException("root");
    }

    var stack = new Stack<Control>(new Control[] { root });

    while (stack.Count > 0) {
        var control = stack.Pop();
        T match = control as T;

        if (match != null && (predicate == null || predicate(match))) {
            return match;
        }

        foreach (Control childControl in control.Controls) {
           stack.Push(childControl);
        }
    }

    return default(T);
}
Mikael Dúi Bolinder
  • 2,080
  • 2
  • 19
  • 44
Ondrej Svejdar
  • 21,349
  • 5
  • 54
  • 89
  • It did cost me some time to understand what you did there but now ... it's beautiful! I'll try to memorize that ... Thanks! – Ben Aug 19 '16 at 12:20
6

FindControl does not search within nested controls recursively. It does only find controls that's NamigContainer is the Control on that you are calling FindControl.

Theres a reason that ASP.Net does not look into your nested controls recursively by default:

  • Performance
  • Avoiding errors
  • Reusability

Consider you want to encapsulate your GridViews, Formviews, UserControls etc. inside of other UserControls for reusability reasons. If you would have implemented all logic in your page and accessed these controls with recursive loops, it'll very difficult to refactor that. If you have implemented your logic and access methods via the event-handlers(f.e. RowDataBound of GridView), it'll be much simpler and less error-prone.

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • 1
    In the case of reusability the UserControls could expose a method that calls the recursive method on itself and the ease of use this approach offers far outweighs any performance issues. Sure if there were thousands of controls but thats not the case. Just ask your client if the perfect design ads value to their business. Keep it simple is all I'm saying. – jimmystormig Feb 10 '11 at 13:04
1

Action Management On Controls

Create below class in base class. Class To get all controls:

public static class ControlExtensions
{
    public static IEnumerable<T> GetAllControlsOfType<T>(this Control parent) where T : Control
    {
        var result = new List<T>();
        foreach (Control control in parent.Controls)
        {
            if (control is T)
            {
                result.Add((T)control);
            }
            if (control.HasControls())
            {
                result.AddRange(control.GetAllControlsOfType<T>());
            }
        }
        return result;
    }
}

From Database: Get All Actions IDs (like divAction1,divAction2 ....) dynamic in DATASET (DTActions) allow on specific User.

In Aspx: in HTML Put Action(button,anchor etc) in div or span and give them id like

<div id="divAction1" visible="false" runat="server" clientidmode="Static">   
                <a id="anchorAction" runat="server">Submit
                        </a>                      
                 </div>

IN CS: Use this function on your page:

private void ShowHideActions()
    {

        var controls = Page.GetAllControlsOfType<HtmlGenericControl>();

        foreach (DataRow dr in DTActions.Rows)
        {          

            foreach (Control cont in controls)
            {

                if (cont.ClientID == "divAction" + dr["ActionID"].ToString())
                {
                    cont.Visible = true;
                }

            }
        }
    }
atif
  • 11
  • 1
1

Recursively find all controls matching the specified predicate (do not include root Control):

    public static IEnumerable<Control> FindControlsRecursive(this Control control, Func<Control, bool> predicate)
    {
        var results = new List<Control>();

        foreach (Control child in control.Controls)
        {
            if (predicate(child))
            {
                results.Add(child);
            }
            results.AddRange(child.FindControlsRecursive(predicate));
        }

        return results;
    }

Usage:

myControl.FindControlsRecursive(c => c.ID == "findThisID");
DavidWainwright
  • 2,895
  • 1
  • 27
  • 30
1

I decided to just build controls dictionaries. Harder to maintain, might run faster than the recursive FindControl().

protected void Page_Load(object sender, EventArgs e)
{
  this.BuildControlDics();
}

private void BuildControlDics()
{
  _Divs = new Dictionary<MyEnum, HtmlContainerControl>();
  _Divs.Add(MyEnum.One, this.divOne);
  _Divs.Add(MyEnum.Two, this.divTwo);
  _Divs.Add(MyEnum.Three, this.divThree);

}

And before I get down-thumbs for not answering the OP's question...

Q: Now, my question is that is there any other way/solution to find the nested control in ASP.NET? A: Yes, avoid the need to search for them in the first place. Why search for things you already know are there? Better to build a system allowing reference of known objects.

B H
  • 1,730
  • 18
  • 24
1

https://blog.codinghorror.com/recursive-pagefindcontrol/

Page.FindControl("DataList1:_ctl0:TextBox3");

OR

private Control FindControlRecursive(Control root, string id)
{
    if (root.ID == id)
    {
        return root;
    }
    foreach (Control c in root.Controls)
    {
        Control t = FindControlRecursive(c, id);
        if (t != null)
        {
            return t;
        }
    }
    return null;
}
estinamir
  • 435
  • 5
  • 11
0

The following example defines a Button1_Click event handler. When invoked, this handler uses the FindControl method to locate a control with an ID property of TextBox2 on the containing page. If the control is found, its parent is determined using the Parent property and the parent control's ID is written to the page. If TextBox2 is not found, "Control Not Found" is written to the page.

private void Button1_Click(object sender, EventArgs MyEventArgs)
{
      // Find control on page.
      Control myControl1 = FindControl("TextBox2");
      if(myControl1!=null)
      {
         // Get control's parent.
         Control myControl2 = myControl1.Parent;
         Response.Write("Parent of the text box is : " + myControl2.ID);
      }
      else
      {
         Response.Write("Control not found");
      }
}
Sudhakar Rao
  • 177
  • 1
  • 8