TL;DR — Todo control de un UserForm vive en un solo sitio: la colección
Controlsdel formulario. Llega a ella comoMe.Controls, recórrela conFor Eachy dirígete a cualquier control por su nombre como cadena —Me.Controls("txtName"). Esa colección es lo que mantiene corto un formulario de veinte campos: un bucle en vez de veinte líneas con nombre. Filtra a los controles que quieres conTypeNameoTypeOf, porque la colección también guarda etiquetas, frames y botones.
Private Sub cmdClear_Click()
Dim ctl As MSForms.Control
' Vaciar cada cuadro de texto del formulario en un solo bucle:
For Each ctl In Me.Controls
If TypeOf ctl Is MSForms.TextBox Then ctl.Value = ""
Next ctl
End Sub
Llegas a un control concreto escribiendo su nombre: txtName.Value, lstItems.Clear. Eso funciona
cuando conoces el control mientras escribes el código. La colección Controls es para cuando no lo
conoces — quieres todos los cuadros de texto, o un control cuyo nombre construiste en tiempo de
ejecución. Este es el eje de la familia de controles del UserForm, y las tres guías de aquí la
completan. La colección Controls es cómo te diriges a los controles en bloque; un
TextBox siempre devuelve un String, así que un número escrito es texto hasta
que lo conviertes; y un Frame es el contenedor que da a un conjunto de botones
de opción su propio grupo. Domina la colección, la cadena y el contenedor, y los controles
individuales — ListBox, ComboBox,
CheckBox — se leen todos igual: un control es un objeto al que llegas a
través del formulario y con el que hablas mediante sus propiedades.
Lo que aprenderás
- Qué es
Me.Controls, y por qué un formulario es una colección y no una pantalla - Recorrer todos los controles con
For Eachy actuar solo sobre los que quieres - Filtrar por tipo con
TypeNameyTypeOfpara saltarte etiquetas y frames - Llegar a un control por su nombre como cadena, para formularios construidos desde datos
- Añadir controles a un formulario mientras se ejecuta
- El juicio a hacer: cuándo un bucle gana a veinte referencias con nombre
El modelo mental: un formulario es una colección de objetos control
Un UserForm parece una pantalla, pero para tu código es una colección. Me.Controls es esa
colección — cada cuadro de texto, etiqueta, botón, frame y lista que soltaste en el formulario,
guardados como objetos que puedes contar y recorrer. Me.Controls.Count te dice cuántos hay;
Me.Controls(0) es el primero (la colección tiene base 0). Me es el formulario donde se ejecuta
el código, así que dentro del propio módulo de un UserForm, Me.Controls y Controls significan lo
mismo.
MsgBox "This form has " & Me.Controls.Count & " controls"
Una vez ves el formulario como una colección, las partes repetitivas del código de formulario
desaparecen. Dejas de escribir txt1, txt2, txt3 y empiezas a escribir un bucle.
Recorrer todos los controles con For Each
For Each recorre la colección. La variable de control se declara MSForms.Control — el tipo
genérico que comparten todos los controles:
Dim ctl As MSForms.Control
For Each ctl In Me.Controls
Debug.Print ctl.Name
Next ctl
Este es el patrón detrás de "vaciar el formulario", "recoger cada valor", "desactivar todo mientras
trabajo". Pero la colección contiene todos los controles — incluidos los que no querías — así que
un bucle sin filtro que escribe ctl.Value se estrella en la primera etiqueta, porque una etiqueta
no tiene Value. Ese es el siguiente problema.
Filtrar por tipo con TypeName y TypeOf
No todos los controles tienen la propiedad que buscas. Un Label no tiene .Value; un
CommandButton no tiene .Text. Toca el equivocado y obtienes error de ejecución 438,
« Object doesn't support this property or method ». Así que filtra primero. TypeName(ctl)
devuelve el tipo como cadena; TypeOf ctl Is lo comprueba directamente:
For Each ctl In Me.Controls
If TypeOf ctl Is MSForms.TextBox Then
ctl.Value = "" ' seguro: solo los cuadros de texto llegan a esta linea
End If
Next ctl
Usa TypeOf ... Is dentro de un If — es la prueba idiomática. Recurre a TypeName(ctl) cuando
quieras el nombre del tipo en un mensaje o un Select Case sobre varios tipos. La regla: nunca
llames a una propiedad dentro de un For Each sobre Controls sin antes demostrar que el control es
de un tipo que la tiene.
Llegar a un control por su nombre
Aquí es donde la colección da sus frutos. Puedes indexarla con una cadena de nombre, y esa cadena puede construirse en tiempo de ejecución:
Dim i As Long
For i = 1 To 5
Me.Controls("txtDay" & i).Value = "" ' txtDay1 ... txtDay5
Next i
Un formulario con campos llamados de txtDay1 a txtDay5 necesita un bucle, no cinco líneas. Así
funcionan los formularios basados en datos: los nombres siguen una convención, y el código los
construye. La trampa es la misma que la de Application.Run y CallByName en la familia de
reflexión — el nombre es una cadena, así que el compilador no puede comprobarlo. Una errata como
"txtDya1" no es un subrayado rojo; es un error de ejecución cuando se ejecuta la línea. Construye
el nombre a partir de un patrón conocido, y protégelo con On Error si
alguna parte viene de datos.
Añadir controles a un formulario mientras se ejecuta
La colección no es fija. Controls.Add crea un control mientras el formulario está abierto, que es
cómo construyes un formulario cuyo tamaño depende de los datos — una fila de campos por registro:
Dim box As MSForms.TextBox
Set box = Me.Controls.Add("Forms.TextBox.1", "txtDynamic", True)
box.Top = 10: box.Left = 10: box.Width = 120
El primer argumento es la clase de control, el segundo su nombre, el tercero si es visible. Los
controles añadidos dinámicamente no tienen eventos de código propios a menos que los conectes con
una clase WithEvents — una técnica real, pero a la que recurrir solo cuando un diseño fijo de
verdad no puede hacer el trabajo.
El juicio a hacer: cuándo un bucle gana a veinte referencias con nombre
La colección Controls es potente, y eso hace fácil abusar de ella. La regla honesta: si tienes un
puñado de controles que conoces por nombre, txtName.Value es más claro que
Me.Controls("txtName").Value, y el compilador lo comprueba. El bucle se gana su sitio cuando los
controles son muchos y uniformes — veinte campos de día, una cuadrícula de entradas, cada cuadro
de texto del formulario — o cuando el nombre lo decide de verdad un dato. Un bucle sobre tres
controles es presumir; un bucle sobre veinte es la única forma sensata de escribirlo. Elige la
colección cuando elimina repetición real, no para demostrar que puedes.
Cómo te ayuda ExcelMaster
Los fallos aquí son silenciosos y tardíos: un For Each que se estrella en la única etiqueta que
olvidaste, un nombre de control construido desde datos que ya no coincide, una propiedad llamada
sobre el tipo equivocado. Todos aparecen como el mismo error 438 o "no encontrado", en tiempo de
ejecución, en un formulario que parecía terminado.
ExcelMaster te deja describir
el comportamiento del formulario — "vaciar cada cuadro de texto", "leer todas las entradas en una
fila", "construir cinco campos de día a partir de una lista" — y escribe el bucle Controls con el
filtro de tipo y la protección de nombre ya puestos. Tú conservas el formulario y el código, y te
saltas la caza en tiempo de ejecución.
Preguntas frecuentes
¿Qué es la colección Controls en VBA?
Es la colección de todos los controles de un UserForm, a la que llegas como Me.Controls. Cada
elemento es un objeto control — un cuadro de texto, una etiqueta, un botón, un frame o una lista.
Puedes contarlos con .Count, recorrerlos con For Each, y llegar a uno por índice o por su nombre
como cadena. Es la forma estándar de actuar sobre muchos controles a la vez en lugar de nombrar cada
uno.
¿Cómo recorro todos los controles de un UserForm?
Declara una variable As MSForms.Control y usa For Each ctl In Me.Controls. Dentro del bucle,
comprueba el tipo con TypeOf ctl Is MSForms.TextBox antes de tocar una propiedad, porque la
colección también guarda etiquetas y botones que no la tienen y lanzarían el error 438.
¿Cómo referencio un control por un nombre guardado en una cadena?
Indexa la colección con la cadena: Me.Controls("txtName"). El nombre puede construirse en tiempo
de ejecución, como Me.Controls("txtDay" & i). Como el nombre es una cadena, una errata no se
detecta hasta que se ejecuta la línea — lanza un error de "no encontrado" — así que construye el
nombre a partir de un patrón conocido y protégelo.
¿Por qué mi bucle For Each sobre Controls lanza el error 438?
Porque llamaste a una propiedad que el control actual no tiene — casi siempre .Value o .Text
sobre un Label, Frame o CommandButton. La colección Controls guarda todos los tipos, así que
filtra con TypeOf ctl Is MSForms.TextBox (o el tipo que quieras) antes de leer o escribir esa
propiedad.
¿Puedo añadir controles a un formulario mientras se ejecuta?
Sí. Me.Controls.Add("Forms.TextBox.1", "txtNew", True) crea uno en tiempo de ejecución, que es
cómo construyes un formulario ajustado al tamaño de los datos. Configura después su Top, Left y
Width. Los controles añadidos dinámicamente no tienen controladores de evento a menos que los
conectes con una clase WithEvents.
Probado en
Probado en: Excel 365 (Windows 11), VBA 7.1 — última verificación el 28/09/2026.
Guías relacionadas: VBA TextBox · VBA Frame · VBA UserForm · VBA ListBox · VBA ComboBox · VBA CheckBox · VBA On Error
