Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprende Trabajando con controles de formulario | Interacción con el Usuario
Excel VBA para Automatización Empresarial

Trabajando con controles de formulario

Desliza para mostrar el menú

TextBoxes y ComboBoxes cubren la mayoría de las entradas de datos, pero algunos otros controles expresan ciertos tipos de elecciones de manera más natural, y leer sus valores correctamente tiene algunos detalles importantes que conviene conocer antes de confiar en ellos.

Figura 5.5

CheckBoxes

La propiedad Value de un CheckBox es un valor Booleano simple: True cuando está seleccionado, False cuando no lo está:

If chkWelcomeEmail.Value = True Then
    MsgBox "A welcome email would be sent here."
End If

Cada CheckBox en un formulario es completamente independiente de los demás; seleccionar chkWelcomeEmail no afecta a chkNewsletter, lo cual es exactamente el comportamiento deseado cuando las opciones no son mutuamente excluyentes.

OptionButtons

Los OptionButtons funcionan de manera opuesta: dentro del mismo grupo, seleccionar uno deselecciona automáticamente todos los demás, exactamente lo que necesitan las opciones de Estado de la captura de pantalla, ya que un cliente no puede ser Activo e Inactivo al mismo tiempo. Para saber cuál está seleccionado, hay que comprobar el valor de cada uno individualmente:

Dim status As String
If optActive.Value = True Then
    status = "Active"
ElseIf optInactive.Value = True Then
    status = "Inactive"
ElseIf optProspect.Value = True Then
    status = "Prospect"
End If

La exclusividad mutua es automática y está gestionada completamente por VBA; nunca es necesario escribir código para desmarcar los otros dos cuando uno es seleccionado. Sin embargo, la agrupación es importante: los OptionButtons solo se excluyen entre sí dentro del mismo contenedor (el propio formulario o un control Frame colocado en él), por lo que dos grupos separados de OptionButtons en el mismo formulario —por ejemplo, Estado y Prioridad— deben estar dentro de dos Frames diferentes, o VBA trataría a todos como un solo grupo mutuamente excluyente.

ListBoxes

Un ListBox muestra varias opciones a la vez sin necesidad de desplegarse, como la lista de City en la Figura 5.5. Se llena de la misma manera que un ComboBox, con .AddItem, y se lee la selección mediante .Value o .ListIndex:

Private Sub UserForm_Initialize()
    lstCities.AddItem "Chicago"
    lstCities.AddItem "Boston"
    lstCities.AddItem "Austin"
End Sub
 
Private Sub cmdAssign_Click()
    If lstCities.ListIndex = -1 Then
        MsgBox "Please select a city first."
        Exit Sub
    End If
    MsgBox "Assigning to: " & lstCities.Value
End Sub

ListIndex es la posición del elemento seleccionado, contando desde 0 para el primer elemento, y es -1 cuando aún no se ha seleccionado nada, lo cual es la comprobación que utiliza cmdAssign_Click para detectar si el usuario hace clic en el botón sin elegir una ciudad primero. .Value proporciona directamente el texto seleccionado, que suele ser más útil que la posición numérica que da .ListIndex.

Formularios Multi-Page

Un control MultiPage (las pestañas Details / Preferences en la parte superior de la Figura 5.5) agrupa controles relacionados en páginas separadas dentro del mismo formulario físico, útil cuando un formulario tiene demasiados campos para caber cómodamente en una sola pantalla. Cada página se identifica por su posición, y solo los controles de la página visible son relevantes para el usuario, aunque todos siguen existiendo y pueden leerse desde el código sin importar qué página esté mostrando:

Private Sub MultiPage1_Change()
    If MultiPage1.Value = 1 Then
        MsgBox "Now viewing Preferences"
    End If
End Sub

MultiPage1.Value sigue la misma numeración desde 0 que ListIndex: la primera página es 0, la segunda (Preferences, en la Figura 5.5) es 1, y el evento Change se activa automáticamente cada vez que el usuario hace clic en una pestaña diferente.

Tarea

  1. Agregar un Frame que contenga tres OptionButtons (optActive, optInactive, optProspect) a frmAddCustomer, reemplazando el anterior ComboBox cboStatus, y escribir el bloque If...ElseIf mostrado arriba para leer la selección.
  2. Agregar dos CheckBoxes independientes (chkWelcomeEmail, chkNewsletter) y confirmar en las pruebas que marcar uno nunca afecta al otro.
  3. Agregar un ListBox (lstCities) que se llene en UserForm_Initialize, y escribir una validación que muestre un error si el usuario intenta continuar sin seleccionar nada (ListIndex = -1).
  4. Desafío: agrupar los campos Company Name, Contact y Email en una página de MultiPage llamada "Details", y los CheckBoxes y OptionButtons en una segunda página llamada "Preferences".
Pista
expand arrow

1. Reemplazo de cboStatus por un Frame con OptionButtons

  • Elimina primero cboStatus y su etiqueta del formulario — lo estás reemplazando, no agregando junto a él.
  • Arrastra un control Frame al formulario, luego arrastra los tres OptionButtons dentro de los límites del Frame — este contenedor es lo que los hace mutuamente excluyentes como un solo grupo.
  • Renombra cada OptionButton individualmente en la ventana de Propiedades; el Frame puede mantener un Caption sencillo como "Status" y no necesita un nombre para código a menos que planees referenciarlo directamente.
  • Leer la selección significa comprobar el .Value de cada OptionButton individualmente con If...ElseIf — no existe una propiedad única que indique "cuál del grupo está seleccionado".
  • Como cboStatus.Value ya no existe, cmdSave_Click necesita una variable status construida a partir de los OptionButtons, que se escribirá en newRow en su lugar.

2. Agregar dos CheckBoxes independientes

  • Estos se colocan directamente en el formulario (no dentro del Frame) — los CheckBoxes fuera de un contenedor compartido nunca se afectan entre sí, sin importar el agrupamiento.
  • Para "confirmar en pruebas", simplemente marca uno, luego el otro, y verifica que ninguno desmarca el primero.

3. Agregar y validar un ListBox

  • Llénalo de la misma manera que se llenaba cboCity — llamadas repetidas a .AddItem en UserForm_Initialize.
  • ListIndex = -1 significa específicamente aún no se ha hecho clic en nada — esa es la condición exacta que debes comprobar antes de permitir guardar.
  • Esta validación debe estar en el mismo bloque de validación que las otras comprobaciones de campos obligatorios, agregando al mismo string errors.

4. Desafío MultiPage

  • Arrastra un control MultiPage al formulario — viene con dos páginas por defecto, que es exactamente lo que necesitas.
  • Renombra cada página mediante clic derecho → Page Order, o seleccionando la pestaña de la página y usando el campo Caption en la ventana de Propiedades (Caption controla el texto visible de la pestaña; hay un campo (Name) separado si quieres referenciar una página por nombre en el código).
  • Mover controles existentes a una página significa arrastrarlos físicamente al área visible de esa página en la vista de diseño — las referencias de código a ellos no cambian, ya que el nombre del control no depende de en qué página esté ubicado.
Solución
expand arrow
Private Sub UserForm_Initialize()
    cboCity.AddItem "Chicago"
    cboCity.AddItem "Boston"
    cboCity.AddItem "Austin"
    cboCity.AddItem "Seattle"
    cboCity.AddItem "Denver"

    lstCities.AddItem "Chicago"
    lstCities.AddItem "Boston"
    lstCities.AddItem "Austin"
    lstCities.AddItem "Seattle"
    lstCities.AddItem "Denver"
End Sub

Private Sub cmdCancel_Click()
    Unload Me
End Sub

Private Sub cmdSave_Click()
    Dim tbl As ListObject
    Dim newRow As ListRow
    Dim errors As String
    Dim status As String

    ' --- Reset field colors ---
    txtCompanyName.BackColor = vbWhite
    txtContact.BackColor = vbWhite
    txtEmail.BackColor = vbWhite

    ' --- Required field checks ---
    If Trim(txtCompanyName.Value) = "" Then
        errors = errors & "- Company Name is required" & vbNewLine
        txtCompanyName.BackColor = RGB(255, 199, 206)
    End If

    If Trim(txtContact.Value) = "" Then
        errors = errors & "- Contact is required" & vbNewLine
        txtContact.BackColor = RGB(255, 199, 206)
    End If

    If Trim(txtEmail.Value) = "" Then
        errors = errors & "- Email is required" & vbNewLine
        txtEmail.BackColor = RGB(255, 199, 206)
    End If

    ' --- Status via OptionButtons ---
    If optActive.Value = True Then
        status = "Active"
    ElseIf optInactive.Value = True Then
        status = "Inactive"
    ElseIf optProspect.Value = True Then
        status = "Prospect"
    Else
        errors = errors & "- Please select a Status" & vbNewLine
    End If

    ' --- ListBox check ---
    If lstCities.ListIndex = -1 Then
        errors = errors & "- Please select a city from the list" & vbNewLine
    End If

    If errors <> "" Then
        MsgBox "Please fix the following:" & vbNewLine & errors, vbExclamation
        Exit Sub
    End If

    ' --- All checks passed — safe to save now ---
    Set tbl = ThisWorkbook.Worksheets("Customers").ListObjects("tblCustomers")
    Set newRow = tbl.ListRows.Add

    newRow.Range(1, 1).Value = "C0" & (tbl.ListRows.Count + 100)
    newRow.Range(1, 2).Value = txtCompanyName.Value
    newRow.Range(1, 3).Value = txtContact.Value
    newRow.Range(1, 4).Value = txtEmail.Value
    newRow.Range(1, 5).Value = cboCity.Value
    newRow.Range(1, 6).Value = status

    If chkWelcomeEmail.Value = True Then
        MsgBox "A welcome email would be sent to " & txtEmail.Value & "."
    End If

    MsgBox txtCompanyName.Value & " was added successfully."
    Unload Me
End Sub

Notas de prueba:

  • El punto 2 (CheckBoxes) no tiene validación dedicada — simplemente haz clic en chkWelcomeEmail y chkNewsletter de forma independiente y confirma que cada uno se activa sin afectar al otro.
  • El punto 4 (MultiPage) es solo un cambio de diseño — no se requieren cambios de código más allá de lo que ya está aquí, ya que cada control mantiene su propio nombre sin importar en qué página se coloque.
  • Observa que cboCity.Value todavía se usa para el guardado real (newRow.Range(1, 5)), mientras que lstCities existe únicamente como un control independiente validado por separado según la tarea — ambos no están conectados a menos que decidas ampliarlo por tu cuenta.
¿Todo estuvo claro?

¿Cómo podemos mejorarlo?

¡Gracias por tus comentarios!

Sección 5. Capítulo 4

Pregunte a AI

expand

Pregunte a AI

ChatGPT

Pregunte lo que quiera o pruebe una de las preguntas sugeridas para comenzar nuestra charla

Sección 5. Capítulo 4
some-alt