Better way to find control in ASP.NET
Asked Answered
P

9

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.

Prostyle answered 10/2, 2011 at 10:13 Comment(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.Hypomania
C
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);
            }
        }
    }
}
Chrissie answered 10/2, 2011 at 10:19 Comment(4)
I see this a lot in C# code. Why return IEnumerable<T> 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. ThanksUtta
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.Wrennie
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 :)Crossways
Thanks so much StinkyCat for your comment! Yes, doing .BaseType made a world of difference for user controls.Genteelism
C
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;
            }
        }
    }
}
Conjugation answered 8/12, 2011 at 3:11 Comment(6)
@Gaolai Peng how is it not working? I use this routine in a number of places and haven't had a problem with it.Conjugation
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?Arjuna
No it calls itself recursively to traverse the tree of controls. Ref child.FindControlsOfType<T>()Conjugation
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."Mazy
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?Bible
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<GridView>() (not tested).Conjugation
L
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);
}
Lone answered 4/9, 2015 at 13:19 Comment(1)
It did cost me some time to understand what you did there but now ... it's beautiful! I'll try to memorize that ... Thanks!Perrie
H
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.

Hypomania answered 10/2, 2011 at 10:57 Comment(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.Chrissie
D
2

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;
}
Daveta answered 11/3, 2020 at 15:33 Comment(0)
K
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;
                }

            }
        }
    }
Kowatch answered 7/4, 2015 at 18:54 Comment(0)
S
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");
Skilken answered 20/8, 2015 at 15:0 Comment(0)
S
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.

Straightjacket answered 13/11, 2015 at 19:45 Comment(0)
L
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");
      }
}
Laundes answered 21/2, 2017 at 0:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.