# VBForums CodeBank > CodeBank - Visual Basic .NET >  Visit Every Control on a Form (includes nested controls, no recursion)

## jmcilhinney

C# version here.

This code will visit every control on a form, regardless of how deeply it's nested.  You could have a TextBox in a Panel in a Splitter in a GroupBox in a TabPage in a TabControl in a Panel and it will still be found.  No recursion is needed on your part, as this will simply follow the tab order from start to finish.  It also doesn't matter if a control has it's TabStop set to False, as it must still have a TabIndex.
VB Code:
Dim ctl As Control = Me.GetNextControl(Me, True) 'Get the first control in the tab order.
 Do Until ctl Is Nothing
    'Use ctl here.
     ctl = Me.GetNextControl(ctl, True) 'Get the next control in the tab order.
Loop
No doubt the GetNextControl method uses recursion internally, but that's not our concern.  This will keep your code nice and neat and you'd need a very, very large number of controls before you'd notice a difference in performance.

----------


## xoggoth

Hey! That's neat!

----------


## Naigewron

It seems it might not be able to see controls inside a ToolStripContainer, but I'll report back with further findings.

----------


## jmcilhinney

You're correct, because the four ToolStripPanels and the ToolStripContentPanel are accessed via specific properties rather than the Controls collection.  I think you'll find that the SplitContainer is in a similar situation.

----------


## michaelrawi

jmcilhinney, what about this:


vb Code:
For Each c As Control In Me.Controls
    'Do something here
Next

Which is more efficient? I always use this if I want to find any control in my form. But I guess, For each method didn't order by tab control tough ...

----------


## jmcilhinney

> jmcilhinney, what about this:
> 
> 
> vb Code:
> For Each c As Control In Me.Controls
>     'Do something here
> Next
> 
> Which is more efficient? I always use this if I want to find any control in my form. But I guess, For each method didn't order by tab control tough ...


Your code will visit the controls by z-order as opposed to tab order, but that's by-the-by.  The point of my code is that it will visit nested controls too, while yours will only visit those controls parented directly by the form.  Your code will not visit any controls that are nested within Panels, GroupBoxes, TabControls, etc.

----------


## vbdotnetboy

this will definitely come in handy... as always thanks

----------


## Clanguage

> This code will visit every control on a form, regardless of how deeply it's nested.  You could have a TextBox in a Panel in a Splitter in a GroupBox in a TabPage in a TabControl in a Panel and it will still be found.  No recursion is need on your part, as this will simply follow the tab order from start to finish.  It also doesn't matter if a control has it's TabStop set to False, as it must still have a TabIndex.
> VB Code:
> Dim ctl As Control = Me.GetNextControl(Me, True) 'Get the first control in the tab order.
>          Do Until ctl Is Nothing
>             'Use ctl here.
>              ctl = Me.GetNextControl(ctl, True) 'Get the next control in the tab order.
>         Loop
> No doubt the GetNextControl method uses recursion internally, but that's not our concern.  This will keep your code nice and neat and you'd need a very, very large number of controls before you'd notice a difference in performance.


Cool tip jmcilhinney,
When I think that all this time I have been using nested For Each to retrieve inner controls. :Cry:  Well no more from now on.

----------


## maged

Greate Piece of code, no more recursive calls 


thx man

----------


## boops boops

Very useful. In VS2008 it works for ToolStripContainers too. I have ToolStrips in ToolStripContainers in TabPages in a TabControl on a Form... no problem!

boops

----------


## davoodrm_666

this code can't find Menustrip and Submenus

----------


## JuggaloBrotha

That's because menu's aren't tab-able, even when you set the tab order, the menu's aren't given a number.

----------


## FourBlades

Thanks jm, I used it today.
Thanks for posting that.

----------


## hacker786

But what to do if we have to use only some controls???

it ll do action in all the controls :|

----------


## jmcilhinney

> But what to do if we have to use only some controls???
> 
> it ll do action in all the controls :|


My code allows you to visit every control on the form.  It does NOT require you to do the something to every one of those controls.  There's no reason you can test each control to see if it satisfies come criterion and then only perform your action if it does.  You'll notice the word "if" appears in that last sentence a couple of times.  That should give you a clue as to how you accomplish this.

----------


## hacker786

But i want to Reset all textbox from the form.

When i use this this ll reset all the controls.

thats u done 

Bt wht i can do for this.

Blank all the textbox on the form ?

----------


## techgnome

So... what's the problem? Inside the loop, check the control's type... if it is a text box, CType it and set its Text property to empty string.

-tg

----------


## jmcilhinney

> But i want to Reset all textbox from the form.
> 
> When i use this this ll reset all the controls.
> 
> thats u done 
> 
> Bt wht i can do for this.
> 
> Blank all the textbox on the form ?


It seems to me that I already told you: only reset the control IF it's a TextBox:
vb.net Code:
If TypeOf ctl Is TextBox Then
    ctl.ResetText()
End If

vb.net Code:
Dim txt As TextBox = TryCast(ctl, TextBox)
 If txt IsNot Nothing Then
    txt.ResetText()
End If

----------


## hacker786

Hmmm Okie 

Thank you i got the clue  :Smilie:  and i ll done this thanx man  :Smilie:

----------


## Wesley008

Thanks Jmci! Love it, use it all the time  :Smilie:

----------


## nelg87

Hi,
I'm trying to use this GetNextControl() function but I'm not sure how to do the declaration or definition for it.  I keep getting an error that the method is not found.  When I add the following definition:
Public Function GetNextControl(ctl as Control, forward as boolean) as Control
It says that I need an "End Function".
When I try to define it as a Declare statement it gives me an error also.

What is the correct function declaration I need to be able to use the GetNextControl() method in Visual Basic 6.0??

I'm trying to mimic the code that is posted at the beggining of this thread.

By the way I'm doing this because I inheret some visual basic application at work that now I have to maintain.  The main form has a ton of controls, some of them hidden.  I'm trying to make them all visible so I can see what they look like so I can understand the code.  If someone has another way of achieveing this then let me know.

Thanks in advance for your input :-).

----------


## techgnome

don't ... because you can't. Because there isn't an equivalent in VB6... it's new with  .NET. 

-tg

----------


## nelg87

Well that makes sense.  Thanks for the quick response.

So, any ideas how I can find a hidden control in a VB6 form that I have no idea where it is?

----------


## techgnome

nope... not with out thrid party add ons...

-tg

----------


## emartinho

Shouldn't this work? 

```
for each ctl in me.controls
ctl.visible = true
next ctl
```

Edited: working from memory, so adapt syntax as needed!  :Big Grin: 
-EM

----------


## techgnome

works at run time... but at designtime, not so much... I think that's what nelg87 was looking for... something to help at design time.

-tg

----------


## iliekater

Thanks jmcilhinney , this is useful !
May I ask a simple question : I noticed you wrote :
ctl.ResetText
is it the same as ctl.Text = "" ?

----------


## Edgemeal

> Thanks jmcilhinney , this is useful !
> May I ask a simple question : I noticed you wrote :
> ctl.ResetText
> is it the same as ctl.Text = "" ?


If you place your mouse cursor over the word ResetText the intellesence will popup and tell you about it, or you can highlight commands,methods etc and press F1 to get more help on them.

F1..MSDN...
Control.ResetText Method 
Resets the Text property to its default value.

----------


## iliekater

Oh , so this applies to all types of controls ! This could be useful , thanks !

----------


## blakemckenna

I tried this code in a little different fashion

'Calling Code


```
                    For Each tbp1 As TabPage In tabChannels.Controls
                        For Each ctl1 As Control In tbp1.Controls
                            If TypeOf ctl1 Is TabControl Then
                                ctl1.Enabled = True

                                For Each tbp2 As TabPage In ctl1.Controls
                                    Select Case tbp2.Name
                                        Case "tabParameter"
                                            EnableDisableControls(tbp2)
                                        Case "tabEquipment"
                                            EnableDisableControls(tbp2)
                                    End Select
                                Next
                            End If
                        Next
                    Next
```

'Called code


```
    Private Sub EnableDisableControls(ByVal c As TabPage)
        Try
            EH.ErrorMessage = String.Empty

            Do Until c Is Nothing
                c = GetNextControl(c, True)

                If TypeOf c Is TextBox Then
                    Dim txt As TextBox = DirectCast(c, TextBox)
                    txt.ReadOnly = True
                ElseIf TypeOf c Is ComboBox Then
                    Dim cmb As ComboBox = DirectCast(c, ComboBox)
                    cmb.Enabled = False
                End If
            Loop

        Catch ex As Exception
            EH.ErrorMessage = "frmCalibration_3/EnableDisableControls() - " & ex.Message & "...Contact Engineering!" & "~E"
        End Try
    End Sub
```

I have a TabControl on my Windows Form. This TabControl contains 8 TabPages with each TabPage containing a UserControl which in turn contains many different controls.

In my code, I loop through each TabPage of the TabControl. The above routine is called only twice based on the name of the TabPages. As I stepped through the lines of code, it seems to be getting every TabPage within the TabControl rather than every control within the TabPage. Not sure what I'm doing wrong.

----------


## jmcilhinney

@blakemckenna, the point of this code is to visit every control on a form.  It doesn't seem like that is what you actually want.  GetNextControl follows the Tab order and is not restricted to a specific container.  If what you want is to visit every control on a TabPage then, assuming that all children are on the TabPage directly and not inside other containmers, you should simply loop through the Controls collection of that TablePage.

----------


## blakemckenna

That's what I normally do but your code looked tighter and made more sense! I will go back to what I had.

Thanks jmc!

----------


## coolcurrent4u

> @blakemckenna, the point of this code is to visit every control on a form.  It doesn't seem like that is what you actually want.  GetNextControl follows the Tab order and is not restricted to a specific container.  If what you want is to visit every control on a TabPage then, assuming that all children are on the TabPage directly and not inside other containmers, you should simply loop through the Controls collection of that TablePage.




It doesnet work for toolstripmenu items or any non control

----------


## jmcilhinney

> It doesnet work for toolstripmenu items or any non control


That was basically addressed in post #4, i.e. this code will visit controls that are in a Controls collection of a control that is itself in a Controls collection, etc, all the way up to the form.  If there is a break in the control/Controls chain then I don't think that GetNextControl can access it.

----------


## coolcurrent4u

> That was basically addressed in post #4, i.e. this code will visit controls that are in a Controls collection of a control that is itself in a Controls collection, etc, all the way up to the form.  If there is a break in the control/Controls chain then I don't think that GetNextControl can access it.


Is there any way to make it work for Toostripmenu, Datagridview columns, Toolstripmenuitem  and ToolStripbutton?
because some of these are derived from Component Class

----------


## jmcilhinney

> Is there any way to make it work for Toostripmenu, Datagridview columns, Toolstripmenuitem  and ToolStripbutton?
> because some of these are derived from Component Class


This code relies on the GetNextControl method.  That method, as the name suggests, gets the next control.  Even then it relies on a direct hierarchy.  It's certainly not going to get something that isn't a control.  If you want to navigate a menu or a toolbar then you'd have to write code specific to that, which would almost certainly involve recursion..

----------


## vbfailure

Close Please

----------


## jmcilhinney

> Hello,
> 
> I have been looking for something like this, it seems ideal.
> 
> One thing I was curious about, does it actually loop through the controls in the order of the controls TabIndex? During a quick test it seems to loop through everything but in an order not following the TabIndex I have set on various controls.
> 
> Thanks!


The order of child controls in the parent's Controls collection matches the z-order.  You can see this in the designer by using the Document Outline window.

----------


## vbfailure

Close Please

----------


## jmcilhinney

If you want to visit the controls by TabIndex then simply sort them by TabIndex.

vb.net Code:
Private Sub ProcessControl(ctrl As Control)    'Use ctrl here.     For Each child In ctrl.Controls.Cast(Of Control)().OrderBy(Function(c) c.TabIndex)        'Here be recursion.  Arrrrrrr!        ProcessControl(child)    NextEnd Sub
You can kick that off by calling the method and passing the form as an argument.  If you don't want to process the form itself then change it to this:

vb.net Code:
Private Sub ProcessControls(ctrls As Control.ControlCollection)    For Each child In ctrls.Cast(Of Control)().OrderBy(Function(c) c.TabIndex)        'Use child here.         'Here be recursion.  Arrrrrrr!        ProcessControl(child)    NextEnd Sub
and pass the Controls property of the form to the first call.  You can do whatever is appropriate in your specific scenario where it says "Use ctrl here" or "Use child here".  Just be aware that sibling controls can have the same TabIndex and that code, as it is, ignores whether TabStop is set to False.

----------


## vbfailure

Close Please

----------


## jmcilhinney

That doesn't look like my code.  Also, this is off-topic for this thread, which is about NOT using recursion.  CodeBank threads, in particular, should not be cluttered up with tangential subjects.  Please create your own thread in the appropriate forum to ask about this issue.

----------


## vbfailure

Close Please

----------


## oliburg

I have made an API Proposal: Add Descendants property for Control for this. I you like it, please upvote it on github.com/dotnet/winforms.

----------


## jmcilhinney

> I have made an API Proposal: Add Descendants property for Control for this. I you like it, please upvote it on github.com/dotnet/winforms.


I don't really see the point when you can just write an extension method to do it, e.g.

vb.net Code:
Imports System.Runtime.CompilerServices
 Public Module ControlExtensions
     <Extension>
    Public Iterator Function GetControls(source As Control) As IEnumerable(Of Control)
        Dim ctl As Control = source.GetNextControl(source, True) 'Get the first control in the tab order.
         Do Until ctl Is Nothing
            Yield ctl
             ctl = source.GetNextControl(ctl, True) 'Get the next control in the tab order.
        Loop
    End Function
 End Module
That is implemented using the same principle as the code in post #1 of this thread but you could opt for various other implementations too, e.g.

vb.net Code:
Imports System.Runtime.CompilerServices
 Public Module ControlExtensions
     <Extension>
    Public Iterator Function GetControls(source As Control, Optional breadthFirst As Boolean = True) As IEnumerable(Of Control)
        If breadthFirst Then
            For Each child As Control In source.Controls
                Yield child
            Next
             For Each child As Control In source.Controls
                For Each descendent As Control In child.GetControls(breadthFirst)
                    Yield descendent
                Next
            Next
        Else 'depthFirst
            For Each child As Control In source.Controls
                For Each descendent As Control In child.GetControls(breadthFirst)
                    Yield descendent
                Next
                 Yield child
            Next
        End If
    End Function
 End Module

----------


## oliburg

> I don't really see the point when you can just write an extension method to do it, [...]


Sure you can. That's what I explain in this SO post.
It would just be handy to have it built-in.

----------


## vbdotnut

JM, I just wanted to let you know I've used this clever piece of code to thoroughly dispose user control classes in their near entirety. I like your style



```
If PanelMain.Controls.Count > 0 Then
    Dim UC As UserControl = DirectCast(PanelMain.Controls(0), UserControl)
    Dim ctl As Control = UC.GetNextControl(UC, True)
    Do Until ctl Is Nothing
        ctl.Dispose()
        ctl = UC.GetNextControl(ctl, True)
    Loop
    UC.Dispose()
    PanelMain.Controls.Clear()
End If
```

----------

