🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

VBA Controls en Excel — recorre la colección Controls y referencia controles por su nombre

|

VBA Controls en Excel — recorre la colección Controls y referencia controles por su nombre

TL;DR — Todo control de un UserForm vive en un solo sitio: la colección Controls del formulario. Llega a ella como Me.Controls, recórrela con For Each y 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 con TypeName o TypeOf, 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 Each y actuar solo sobre los que quieres
  • Filtrar por tipo con TypeName y TypeOf para 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