@Tim Lentine's suggestion is very good and directly answers your question, seems to me.
But I would probably never write a sub that operates on the entire controls collection of a form. The reason is that it's actually quite inefficient to do so. But whether or not it's appropriate depends on how often and when you're walking that collection.
If you're walking it once in the form's OnLoad event (you wouldn't want to do it in the OnOpen because the data-bound properties of the controls aren't guaranteed to be fully initialized at that point -- you can still operate on format properties, though -- but everything's ready to go by the time the OnLoad event fires), that's no big deal, and passing that collection to an outside subroutine would be appropriate.
But if you're walking it for every record (say to hide/reveal controls, or to initialize criteria fields in an unbound query-by-form interface), then you will noticeably improve your form's performance by using one or more custom collections having much smaller numbers of items to loop through than are in any normal form's controls collection. Then you could rewrite Tim's code to use a custom collection, or, for that matter, one could still use the Object variable above and pass it a custom collection as well (and still remain able to pass it a form's controls collection).
Basically, what you do is initialize the collection in the form's OnLoad event. I normally write a private subroutine to do that so I can re-initialize should a code reset happen:
Private Sub SetupCollections()
If mcolCriteria.Count = 0 Then
Call PopulateCollections(Me, mcolCriteria, "Criteria")
End If
End Sub
Public Sub PopulateCollections(frm As Form, pcol As Collection, strTag As String)
Dim ctl As Control
For Each ctl In frm.Controls
If ctl.Tag = strTag Then
pcol.Add ctl, ctl.Name
End If
Next ctl
Set ctl = Nothing
End Sub
In this case, my method for determining which controls get added to the collection is to set the Tag property of those controls. You could also do something like:
Public Sub PopulateCollections(frm As Form, pcol As Collection, intControlType As AcControlType)
Dim ctl As Control
For Each ctl In frm.Controls
If ctl.ControlType = intControlType
pcol.Add ctl, ctl.Name
End If
Next ctl
Set ctl = Nothing
End Sub
To use this, you could, for instance, create a collection of Nullable controls like this:
If mcolControlsNullable.Count = 0 Then
Call PopulateCollections(Me, mcolControlsNullable, acTextBox)
Call PopulateCollections(Me, mcolControlsNullable, acComboBox)
Call PopulateCollections(Me, mcolControlsNullable, acListBox)
End If
For Boolean controls:
If mcolControlsBoolean.Count = 0 Then
Call PopulateCollections(Me, mcolControlsBoolean, acCheckBox)
End If
For option groups or other controls having a default value:
If mcolControlsWithDefaults.Count = 0 Then
Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acTextBox)
Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acComboBox)
Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acListBox)
Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acCheckBox)
Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acOptionGroup)
End If
Public Sub PopulateCollectionsWithDefaults(frm As Form, pcol As Collection)
Dim ctl As Control
For Each ctl In frm.Controls
If Len(ctl.DefaultValue) > 0 Then
pcol.Add ctl, ctl.Name
End If
Next ctl
Set ctl = Nothing
End Sub
Private Sub SetControlValuesFromDefaults(pcol As Collection)
For Each ctl in pcol
ctl = ctl.DefaultValue
Next ctl
End Sub
And for the other collections:
Public Sub SetControlValues(pcol As Collection, varValue As Variant)
For Each ctl in pcol
ctl = varValue
Next ctl
End Sub
With this more complex set of collections, you would need something like this to initially populate them them:
Private Sub SetupCollections()
If mcolControlsNullable.Count = 0 Then
Call PopulateCollections(Me, mcolControlsNullable, acTextBox)
Call PopulateCollections(Me, mcolControlsNullable, acComboBox)
Call PopulateCollections(Me, mcolControlsNullable, acListBox)
End If
If mcolControlsBoolean.Count = 0 Then
Call PopulateCollections(Me, mcolControlsBoolean, acCheckBox)
End If
If mcolControlsWithDefaults.Count = 0 Then
Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acTextBox)
Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acComboBox)
Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acListBox)
Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acCheckBox)
Call PopulateCollectionsWithDefaults(Me, mcolControlsWithDefaults, acOptionGroup)
End If
End Sub
...then you'd want a sub to initialize the control values:
Private Sub InitializeControls()
Call SetControlValues(mcolControlsNullable, Null)
Call SetControlValues(mcolControlsBoolean, False)
Call SetControlValuesFromDefaults(mcolControlsWithDefaults)
End Sub
...so that you could then set everything up in your form's OnLoad event:
Call SetupCollections()
Call InitializeControls()
Now, of course, there are less convoluted ways to do this. You might want to have your initialization routine walk the controls collection just once:
Private Sub SetupCollections()
Dim ctl As Control
For Each ctl in Me.Controls
If Len(ctl.DefaultValue) > 0 then
mcolControlsWithDefaults.Add ctl, ctl.Name
Else
Select Case ctl.ControlType
Case acTextBox, acComboBox, acListBox
mcolControlsNullable.Add ctl, ctl.Name
Case acCheckBox
mcolControlsBoolean.Add ctl, ctl.Name
End Select
End If
Next ctl
Set ctl = Nothing
End Sub
A way to eliminate the initialization routine would be to use custom properties to return the collections, using internal static variables that would be re-initialized as needed. However, this would mean multiple walks through the controls collection, so it's not as efficient:
Private Property Get colControlsNullable() As Collection
Static colNullable As Collection
If colNullable.Count = 0 Then
Call PopulateCollections(Me, mcolControlsNullable, acTextBox)
Call PopulateCollections(Me, mcolControlsNullable, acComboBox)
Call PopulateCollections(Me, mcolControlsNullable, acListBox)
End If
Set colControlsNullable = colNullable
End Property
Unfortunately, using the static variable, while nicely avoiding module-level variables, means that your inialization routine gets less efficient, since there's no way for an outside initialization routine to utilize these static variables to populate everything with one walk through the controls collection.
So, I don't use custom properties for these collections, even though I wish I could. On the other hand, if I have only one custom controls collection, I would do that.
Anyway, I've rambled on far to long and with way too much convolution, and probably all that air code is filled with errors...