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

VBA ListBox en Excel — Rellénalo, lee la selección y la trampa de MultiSelect

|

VBA ListBox en Excel — Rellénalo, lee la selección y la trampa de MultiSelect

TL;DR — Un ListBox muestra una lista y recuerda lo que el usuario eligió. Rellénalo de una vez con ListBox1.List = Range("A2:A50").Value. Lee una única elección con .ListIndex (la fila con base 0, o -1 si no hay nada) y .Value (el texto elegido). La trampa: en cuanto activas .MultiSelect, .Value se vuelve Null y deja de decirte nada — tienes que recorrer .Selected(i) para reunir cada elección.

' ListBox de seleccion unica: leer la unica opcion
Private Sub cmdOK_Click()
    If ListBox1.ListIndex = -1 Then
        MsgBox "Please pick a name first."
        Exit Sub
    End If
    MsgBox "You picked " & ListBox1.Value      ' .Value = el texto seleccionado
End Sub

Un ListBox es un control de un UserForm, y lo primero que hay que dejar claro es la idea que hay detrás de cada control: un control es un objeto vivo que lees a través de sus propiedades, no el valor en sí. Los elementos viven en .List; la elección del usuario vive en una propiedad aparte — .ListIndex o .Selected — nunca en el cuadro como un todo. Casi toda pregunta del tipo «mi ListBox no devuelve nada» consiste en leer la propiedad equivocada.

Lo que aprenderás

  • El modelo mental que une ListBox, ComboBox y CheckBox
  • Dos formas de rellenar un ListBox — .List = Range.Value de una vez frente a .AddItem en un bucle
  • Cómo leer una única selección con .ListIndex y .Value, y qué significa -1
  • La trampa número uno: .Value se vuelve Null con MultiSelect, así que debes recorrer .Selected(i)
  • Cómo vaciar una lista, mostrar más de una columna y enlazarla a una hoja con .RowSource
  • Cuándo un ListBox es el control adecuado y cuándo encaja mejor un ComboBox

El modelo mental: el cuadro guarda la lista, una propiedad guarda la elección

Piensa en un ListBox como una ventana a una lista de elementos. Dos cosas distintas viven en dos lugares distintos:

ListBox1.List           ' los ELEMENTOS - lo que se muestra en el cuadro
ListBox1.ListIndex      ' la SELECCION  - que fila esta elegida (base 0, -1 = ninguna)
ListBox1.Value          ' el TEXTO elegido - solo tiene sentido en seleccion unica
ListBox1.Selected(i)    ' True/False para la fila i - la unica lectura fiable con MultiSelect

Esa separación es todo el control. Cuando fijas .List, estás cargando los elementos. Cuando el usuario hace clic en una fila, VBA no cambia .List — cambia .ListIndex y conmuta .Selected. Así que «¿qué eligió el usuario?» nunca se responde mirando la lista; se responde leyendo las propiedades de selección. Ten eso claro y el resto es detalle.

Rellenar un ListBox: una sola asignación gana al bucle

Verás dos formas de rellenar una lista. La primera a la que recurrir asigna una columna entera de golpe:

Private Sub UserForm_Initialize()
    ' Rellenar desde una columna de la hoja en una sola asignacion - rapido y simple
    ListBox1.List = Sheet1.Range("A2:A50").Value
End Sub

.List acepta una matriz 2D, y Range.Value es una matriz 2D (mira VBA Range), así que una línea carga cincuenta filas. Usa .AddItem solo cuando los elementos se calculan uno a uno y no tienes una matriz a mano:

Dim i As Long
For i = 1 To 5
    ListBox1.AddItem "Quarter " & i    ' anadir elementos que construyes sobre la marcha
Next i

.AddItem dentro de un bucle grande es el camino lento — cada llamada es un viaje de ida y vuelta aparte al control. Si los datos ya están en un rango o en una matriz, asigna .List una sola vez. Para vaciar el cuadro antes de rellenarlo de nuevo, llama a ListBox1.Clear.

Leer una única selección: ListIndex y Value

Para un ListBox corriente de selección única, dos propiedades lo responden todo. .ListIndex es el número de fila con base 0 de la selección — y es -1 cuando no hay nada seleccionado, que es la comprobación que siempre deberías hacer primero. .Value es el texto de la fila seleccionada:

If ListBox1.ListIndex = -1 Then
    MsgBox "Nothing selected."          ' -1 es la senal de 'aun sin seleccion'
Else
    MsgBox "Row " & ListBox1.ListIndex & ": " & ListBox1.Value
End If

Comprobar .ListIndex = -1 antes de leer .Value es la diferencia entre un formulario que guía al usuario y otro que actúa sobre una selección vacía. Es la misma disciplina que proteger el resultado de un Find antes de usarlo.

La trampa número uno: MultiSelect deja .Value inservible

Aquí está el error que hace a la gente dar vueltas. Un ListBox tiene una propiedad MultiSelect. Déjala en su valor por defecto (fmMultiSelectSingle) y .Value funciona. Ponla en fmMultiSelectMulti o fmMultiSelectExtended para que el usuario pueda marcar varias filas — y .Value se vuelve Null. Ya no refleja la selección en absoluto, porque «el valor» no significa nada cuando hay tres filas elegidas.

Con MultiSelect, la única lectura fiable es recorrer cada fila y probar .Selected(i):

Dim i As Long, chosen As String
For i = 0 To ListBox1.ListCount - 1     ' las filas van de 0 .. ListCount - 1
    If ListBox1.Selected(i) Then
        chosen = chosen & ListBox1.List(i) & vbCrLf
    End If
Next i
MsgBox "You chose:" & vbCrLf & chosen

Dos detalles hacen que este bucle funcione o falle. Va de 0 a .ListCount - 1, porque las filas tienen base 0 igual que los límites de una matriz — un desajuste de uno aquí descarta en silencio la última fila. Y lee el texto del elemento con .List(i), no con .Value, porque .Value es Null en este modo. Si construiste un formulario, activaste la selección múltiple más tarde y tu código dejó de ver las selecciones, esta es la razón.

Más de una columna, y enlazar a una hoja

Dos propiedades cubren los casos habituales de «necesito algo más que una lista simple». .ColumnCount y .List (una matriz 2D) dejan que un ListBox muestre varias columnas — un ID y un nombre uno al lado del otro — mientras que .BoundColumn decide qué columna devuelve .Value. Y .RowSource enlaza el cuadro directamente a un rango de la hoja para que siga las celdas sin ningún código de relleno:

ListBox1.ColumnCount = 2
ListBox1.RowSource = "Sheet1!A2:B50"    ' dos columnas, enlazadas en vivo a la hoja
ListBox1.BoundColumn = 1                ' .Value devuelve la columna 1 (el ID)

.RowSource es cómodo pero acopla el formulario a una disposición concreta de la hoja; asignar .List desde una matriz mantiene el formulario independiente de dónde viven los datos. Para un formulario de entrada de datos que sobreviva a un solo libro, prefiere la asignación de la matriz.

¿ListBox o ComboBox? Elige por cómo debe sentirse la elección

Un ListBox y un ComboBox se leen casi idénticamente, así que elige por comportamiento, no por sintaxis. Recurre a un ListBox cuando las opciones deban estar visibles de un vistazo o cuando el usuario pueda elegir más de una — la selección múltiple es un truco exclusivo del ListBox. Recurre a un ComboBox cuando el espacio escasee, la respuesta sea un único valor y una lista desplegable que se pliega en una línea quede más ordenada. Si te descubres queriendo una casilla por fila, lo que en realidad quieres es un ListBox de selección múltiple.

Cómo te ayuda ExcelMaster

Los dos errores de esta página cuestan tiempo de verdad: leer .Value en un ListBox de selección múltiple (y obtener Null), y actuar sobre una selección sin comprobar antes .ListIndex = -1. Ambos son fáciles de escribir y fallan en silencio.

ExcelMaster te deja describir el formulario con palabras llanas — «una lista de clientes en la que el usuario puede elegir varios y luego escribir los elegidos en una hoja» — y cablea el UserForm, rellena el ListBox desde tus datos y lee la selección de la forma correcta (recorriendo .Selected(i) cuando la selección múltiple está activa, protegiendo .ListIndex cuando no lo está). Tú conservas el libro y el código.

Preguntas frecuentes

¿Cómo relleno un ListBox desde un rango en VBA?

Asigna el valor del rango a .List en una sola sentencia: ListBox1.List = Sheet1.Range("A2:A50").Value. Como Range.Value es una matriz 2D y .List acepta una, esto carga la columna entera de una vez. Usa .AddItem solo para elementos que construyes individualmente en un bucle, y llama a .Clear primero si estás rellenando un cuadro que ya tiene filas.

¿Cómo obtengo el elemento seleccionado de un ListBox de VBA?

Para un ListBox de selección única, lee .Value para el texto o .ListIndex para el número de fila con base 0; .ListIndex es -1 cuando no hay nada seleccionado, así que compruébalo primero. Para un ListBox de selección múltiple, .Value es Null — recorre For i = 0 To ListBox1.ListCount - 1 y reúne cada fila donde .Selected(i) sea True.

¿Por qué mi ListBox .Value está vacío o es Null?

Porque MultiSelect está activado. Cuando .MultiSelect es fmMultiSelectMulti o fmMultiSelectExtended, la propiedad .Value siempre es Null — no puede representar varias filas seleccionadas. Lee la selección recorriendo .Selected(i) de 0 a .ListCount - 1 en su lugar, y lee cada elemento con .List(i).

¿Cómo vacío todos los elementos de un ListBox?

Llama a ListBox1.Clear. Eso elimina cada fila. Si el cuadro está enlazado con .RowSource, pon primero .RowSource = "", porque una lista enlazada se reconstruye a sí misma desde el rango. Para borrar solo la selección actual sin quitar elementos, pon .ListIndex = -1 (selección única) o recorre .Selected(i) = False (selección múltiple).

¿Cuál es la diferencia entre un ListBox y un ComboBox?

Un ListBox muestra varias filas a la vez y puede permitir selecciones múltiples; un ComboBox se pliega en una única línea, muestra una lista desplegable y permite una sola elección (y, opcionalmente, escritura libre). Usa un ListBox cuando las opciones deban estar visibles o sean de elección múltiple, y un ComboBox cuando el espacio escasee y baste con una respuesta.

Probado en

Probado en: Excel 365 (Windows 11), VBA 7.1 — última verificación el 25/09/2026.

Guías relacionadas: VBA ComboBox · VBA CheckBox · VBA UserForm · VBA Range · VBA For Loop