ASP.Net FindControl is not working - How come?
Asked Answered
A

7

30

I have used FindControl in the past, prior to .NET 2.0/3.0. It seems like now, for some reason, the ID's of my controls get a funky named assigned. For example I assigned an id "cbSelect" to a checkbox, but FindControl does not find it. When I view the HTML it was assigned ctl00_bodyPlaceHolder_ctl02_cbSelect.

I have not found one example of FindControl that mentions that. In fact everyone seems to just use find control like normal.

So, am I doing something wrong? Did .Net change? Can anyone shed some light onto this for me, it is really frustrating!

Archerfish answered 28/4, 2009 at 20:21 Comment(0)
C
29

You are probably using a MasterPage or user controls (ascx) and this is the reason the for client ids change. Imagine you have a control in the master page with the same id as one in the page. This would result in clashes. The id changes ensures all ClientID properties are unique on a page.

FindControl needs some special attention when working with MasterPages. Have a look at ASP.NET 2.0 MasterPages and FindControl(). The FindControl works inside a naming container. The MastePage and the page are different naming containers.

Cent answered 28/4, 2009 at 20:30 Comment(2)
The way Microsoft implemented this is such a joke, it should just work. Oh you have a masterpage? How about 500 nested master pages? The method should figure it out and do what's necessary to find the control, period.Maverick
Do you know you're using "should" and "Microsoft" in the same thought?Serbocroatian
N
10

You could write extender to find any control on page using recursion. This could be in some Util/Helper class.

 public static Control FindAnyControl(this Page page, string controlId)
    {
        return FindControlRecursive(controlId, page.Form);
    }

    public static Control FindAnyControl(this UserControl control, string controlId)
    {
        return FindControlRecursive(controlId, control);
    }

    public static Control FindControlRecursive(string controlId, Control parent)
    {
        foreach (Control control in parent.Controls)
        {
            Control result = FindControlRecursive(controlId, control);
            if (result != null)
            {
                return result;
            }
        }
        return parent.FindControl(controlId);
    }
Neuter answered 27/1, 2010 at 13:51 Comment(0)
J
8

I've had pretty good luck working around this problem in "most" cases with a simple extension method

You can call it on whatever higher-level container control you think best, including the Page itself if you want to scan the entire control hierarchy.

private static Control FindControlIterative(this Control control, string id)
{
    Control ctl = control;

    LinkedList<Control> controls = new LinkedList<Control>();

    while(ctl != null)
    {
        if(ctl.ID == id)
        {
            return ctl;
        }

        foreach(Control child in ctl.Controls)
        {
            if(child.ID == id)
            {
                return child;
            }

            if(child.HasControls())
            {
                controls.AddLast(child);
            }
        }

        ctl = controls.First.Value;
        controls.Remove(ctl);
    }

    return null;
}
Jeanejeanelle answered 28/4, 2009 at 20:56 Comment(0)
A
7

When searching for a control in a control collection, always use the id you assigned the control, not the one you see in the source post render. If FindControl() does not find the control you know exists, there is a good chance that you are not searching in the right branch of the control hierarchy. A recursive function has been successful for me.

Here is my example of what I use for VB.NET 3.5:

Function FindControlRecursive(ByVal ctrl As Control, ByVal id As String) As Control
    Dim c As Control = Nothing

    If ctrl.ID = id Then
        c = ctrl
    Else
        For Each childCtrl In ctrl.Controls
            Dim resCtrl As Control = FindControlRecursive(childCtrl, id)
            If resCtrl IsNot Nothing Then c = resCtrl
        Next
    End If

    Return c
End Function

Here is an example of how I would topically implement this function in my base page class:

Dim form HtmlForm = CType(FindControlRecursive(Me, "Form"), HtmlForm)
Architectonics answered 28/4, 2009 at 20:21 Comment(1)
+1 because without seeing yours I had already implemented it in C# with the same approach: public static System.Web.UI.Control FindControlRecursive(Control ctl, string id){ if (ctl == null) return null; if (ctl.ID == id) return ctl; if (ctl.Controls.Count > 0) foreach(Control sc in ctl.Controls) { Control fnd = FindControlRecursive(sc, id); if (fnd != null) return fnd; } return null; }`Lyso
B
3

This is the VB.NET code that worked for me:

<Extension()> _
Function FindChildControlById(ByVal controlToStartWith As Control, ByVal controlIdToFind As String) As Control
    If controlToStartWith Is Nothing Then Return Nothing
    If controlToStartWith.ID = controlIdToFind Then Return controlToStartWith
    For Each childControl As Control In controlToStartWith.Controls
        Dim resCtrl As Control = FindChildControlById(childControl, controlIdToFind)
        If resCtrl IsNot Nothing Then Return resCtrl
    Next childControl
    Return Nothing
End Function ' Function FindChildControlById(ByVal controlToStartWith As Control, ByVal controlIdToFind As String) As Control

Credit goes to George for the initial VB.NET code. I only modified it a teeny bit, with 2 functional change: mine doesn't error if/when null/Nothing is passed as the input control, and mine is implemented as an Extension. My other 3 minor changes don't affect the functionality, but to me, they were code simplifications. But I know it's very subjective.

So this method can be used with:

Dim c1 As Control = Page.FindChildControlById("aspControlID")

And if you want to convert it into a specific child class of a Control, like this:

Dim c1 As Control = Page.FindChildControlById("aspControlID")
Dim c As HyperLink = TryCast(c1, HyperLink)

Update: My function is now named 'FindChildControlById' (previously was 'FindMiControl'). I liked SpeedNet's suggestion better.

Ballflower answered 1/3, 2016 at 19:30 Comment(1)
This is perfect, better than the other solutions, thanks! I renamed to FindChildControlByID().Gastro
Z
1

When it's rendering the html, ASP.NET will prefix all the control IDs with the IDs of the naming containers (User Controls etc..) in a hierarchy going back all the way to the document root. This ensures that all the IDs are unique for post backs etc..

This does not effect using FindControl where you should use the ID in the original markup.

Zircon answered 28/4, 2009 at 20:31 Comment(0)
R
0

Here is a reference as to how web form controls are named...

Web Forms Control Identification

Rearward answered 28/4, 2009 at 20:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.