You can do this pretty easily using a recursive method. If you want to be fancy you can even use extension methods that takes some delegates as parameters for greater flexibility.
Implementing the method is as easy as dropping a new class in your App_Code folder:
Public Module ControlParser
Sub New()
End Sub
<System.Runtime.CompilerServices.Extension> _
Public Sub ForEachRecursive(ByVal ctrl As Control, ByVal callback As Action(Of Control), ByVal onMethodEnter As Action, ByVal onMethodLeave As Action)
onMethodEnter.Invoke()
If ctrl Is Nothing Then
Exit Sub
End If
callback(ctrl)
For Each curCtrl As Control In ctrl.Controls
ForEachRecursive(curCtrl, callback, onMethodEnter, onMethodLeave)
Next
onMethodLeave.Invoke()
End Sub
End Module
Utilizing this from inside your page to walk through all controls and print them out in a hierarchical view could be accomplished like so:
Private depth As Int32 = -1
Private sbOutput As New StringBuilder()
Private Const SPACES_PER_TAB As Int32 = 4
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
Me.ForEachRecursive(PrintControl, Function() System.Math.Max(System.Threading.Interlocked.Increment(depth),depth - 1), Function() System.Math.Max(System.Threading.Interlocked.Decrement(depth),depth + 1))
//Output is a panel on the page
output.Controls.Add(New LiteralControl(sbOutput.ToString()))
End Sub
Public Sub PrintControl(ByVal c As Control)
sbOutput.Append(GetTabs(depth) + c.ClientID & "<br />")
End Sub
Public Function GetTabs(ByVal tabs As Int32) As [String]
Return If(tabs < 1, String.Empty, New [String]("*"c, tabs * SPACES_PER_TAB).Replace("*", " "))
End Function
I appologize if the code looks funny, but I coded this in C# and used a converter. However, I was able to dump the actual VB code into my App_Code directory and confirm that it worked.
Hope this helps :)