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!
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.
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);
}
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;
}
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)
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.
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.
Here is a reference as to how web form controls are named...
来源:https://stackoverflow.com/questions/799655/asp-net-findcontrol-is-not-working-how-come