Recursive FindControl<T>

Need to find a control anywhere on the page or in a template?  Inspired by Steve Smith's implementation, here is the code to find it recursively.  This example assumes the code is located in a custom base page.

 

  1 public T FindControl<T>(string id) where T : Control
  2 {
  3     return FindControl<T>(Page, id);
  4 }
  5 
  6 public static T FindControl<T>(Control startingControl, string id) where T : Control
  7 {
  8     // this is null by default
  9     T found = default(T);
 10 
 11     int controlCount = startingControl.Controls.Count;
 12 
 13     if (controlCount > 0)
 14     {
 15         for (int i = 0; i < controlCount; i++)
 16         {
 17             Control activeControl = startingControl.Controls[i];
 18             if (activeControl is T)
 19             {
 20                 found = startingControl.Controls[i] as T;
 21                 if (string.Compare(id, found.ID, true) == 0) break;
 22                 else found = null;
 23             }
 24             else
 25             {
 26                 found = FindControl<T>(activeControl, id);
 27                 if (found != null) break;
 28             }
 29         }
 30     }
 31     return found;
 32 }       

14 Comments

  • What, no link love for me?

  • Woot! Thanks, and nice job on the VB version, too!

  • Steve Smith is a Link Shore (-s +w)

  • Usefull class but why post code with annoying line numbers??? Cut and paste requires each line to be edited!

  • Aaron, I did try out your code and it works just fine. Thanks for the update!

  • Is there a VB Version as well?

  • i made a vb version

    Partial Class Default4
    Inherits System.Web.UI.Page

    Public Overloads Function FindControl(Of T As Control)(ByVal id As String) As T
    Return FindChildControl(Of T)(Page, id)
    End Function

    Public Overloads Function FindControl(Of T As Control)(ByVal startingControl As Control, ByVal id As String) As T
    Return ControlFinder.FindControl(Of T)(startingControl, id)
    End Function

    Public Function FindChildControl(Of T As Control)(ByVal startingControl As Control, ByVal id As String) As T
    Return ControlFinder.FindChildControl(Of T)(startingControl, id)
    End Function

    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
    Me.FindControl(Of RadioButton)(Page, "RadioButton1").Checked = True
    End Sub

    End Class

    Public NotInheritable Class ControlFinder

    Private Sub New()
    MyBase.New()

    End Sub

    Public Shared Function FindControl(Of T As Control)(ByVal startingControl As Control, ByVal id As String) As T
    Dim found As T = TryCast(startingControl.FindControl(id), T)
    If found Is Nothing Then
    found = FindChildControl(Of T)(startingControl, id)
    End If

    Return found
    End Function

    Public Shared Function FindChildControl(Of T As Control)(ByVal startingControl As Control, ByVal id As String) As T
    Dim found As T = Nothing
    For Each activeControl As Control In startingControl.Controls
    found = TryCast(activeControl, T)
    If found Is Nothing OrElse (String.Compare(id, found.ID, True) 0) Then
    found = FindChildControl(Of T)(activeControl, id)
    End If
    If found IsNot Nothing Then
    Exit For
    End If
    Next

    Return found
    End Function

    End Class

  • Aside from clarity, I like that for loop much better this way:

    15 for (int i = 0; i < controlCount; i++)
    16 {
    17 Control activeControl = startingControl.Controls[i];
    18 if ( string.Compare( id , activeControl.Controls [ i ] , true ) == 0 )
    19 return activeControl.Controls [ i ] as T;
    20 else
    21 return FindControl(activeControl, id);
    22 }

    Notice, how it avoids the two types casts your version always incurrs even if ID does not match. And there is no need for that redundant if before the loop.

    Always run FxCop on your code.

  • I agree with Tanveer this implementation has excessive casting. Also, there is a reason that this hasn't been added to ASP.net because it is way to expensive of an operation. But if you must use it why not make it an extension method on the page & control class?

  • Works good with one drawback,
    If I want to find panel inside panel, it will not find it because if will fail on the first panel, or am I wrong?

  • else block should be changed to if(found == null) or all children are not processed

  • Protected Function GetChildControl(ByVal oParentControl As Control, ByVal sChildID As String) As Control

    Try
    Dim oChildControl As Control = Nothing

    For i As Integer = 0 To oParentControl.Controls.Count - 1
    If oParentControl.Controls(i).ID = sChildID Then
    Return oParentControl.Controls(i)
    Else
    oChildControl = GetChildControl(oParentControl.Controls(i), sChildID)

    If oChildControl IsNot Nothing Then Return oChildControl

    End If
    Next

    Return oChildControl

    Catch ex As Exception
    Throw ex
    End Try

  • That really helped ! Thanks !

  • Nice post! My own FindControlRecursive helped me out many times. Especially, when I deal with MasterPages. For example, one of such usage is shown in my post here – http://dotnetfollower.com/wordpress/2010/12/sharepoint-add-onchange-attribute-to-dropdownchoicefield/.
    Thank you!

Comments have been disabled for this content.