Capítulo 14: Controles Avançados: Listas Suspensas (ComboBox) e Painéis (ListBox)

🎯 Objetivo da Aula

Nesta aula, você aprenderá a enriquecer seus formulários gráficos corporativos dominando os controles avançados da biblioteca MSForms: a caixa de combinação suspensa (ComboBox), o painel de listagem tabular multicolunas (ListBox), as caixas de seleção (CheckBox) e os botões de opção exclusiva (OptionButton). Você compreenderá a diferença entre popular listas via método .AddItem e via atribuição direta de matrizes com a propriedade .List.

Ao final desta aula, você terá construído o Console Interativo de Despacho de Cargas da FastLog S.A.


🏢 Cenário Prático (FastLog S.A.)

Empresa: FastLog Transportes & Logística Integrada S.A.
Problema: No terminal de cargas, o assistente de expedição despachava dezenas de veículos por hora. O antigo formulário possuía apenas caixas de texto comuns; com isso, o operador frequentemente digitava o nome da filial com erro ortográfico ("Curitiba", "CWB", "Curitiba - PR"), o que quebrava os relatórios gerenciais da matriz. Além disso, o operador precisava alternar constantemente entre a tela do formulário e a planilha para conferir quais viagens já haviam sido expedidas no turno, gerando perda de tempo e erros de duplicidade de despacho.
Sua Missão: Desenvolver o formulário frmConsoleDespacho, contendo uma lista suspensa com auto-completar (ComboBox) para as filiais homologadas, botões de rádio para a modalidade de transporte (Frota Própria vs Terceirizado) e uma grade interna de 4 colunas (ListBox) que exibe na própria tela os despachos em tempo real.


🧠 Fundamentos: A Teoria Traduzida

1. Controles de Seleção Múltipla e Exclusiva

flowchart TD
    subgraph CONTROLES["Biblioteca de Controles Avançados"]
        CB["🔽 ComboBox (Lista Suspensa)<br/>Impede digitação livre; padroniza filiais"]
        LB["📑 ListBox Multicolunas<br/>Visualizador de tabelas e relatórios dentro da tela"]
        OB["🔘 OptionButtons em Grupo<br/>Escolha ÚNICA: Frota Própria OU Agregado"]
        CK["☑️ CheckBox<br/>Opções binárias independentes: Seguro Especial Sim/Não"]
    end

    style CONTROLES fill:#1a365d,stroke:#fff,stroke-width:2px,color:#fff
    style CB fill:#2980b9,stroke:#fff,stroke-width:2px,color:#fff
    style LB fill:#27ae60,stroke:#fff,stroke-width:2px,color:#fff
    style OB fill:#8e44ad,stroke:#fff,stroke-width:2px,color:#fff
    style CK fill:#d35400,stroke:#fff,stroke-width:2px,color:#fff

Existem duas maneiras fundamentais de alimentar uma lista suspensa:

  • Método .AddItem (Manual ou via laço):
    1
    2
    3
    
    cboFilial.AddItem "Matriz São Paulo (SP)"
    cboFilial.AddItem "Filial Rio de Janeiro (RJ)"
    cboFilial.AddItem "Filial Belo Horizonte (MG)"
  • Propriedade .List (Carga instantânea em bloco via matriz):
    1
    
    cboFilial.List = Worksheets("Parametros").Range("A2:A10").Value

3. Anatomia do ListBox Multicolunas

Para exibir uma grade tabular dentro do formulário, configuramos três propriedades essenciais do ListBox:

  • .ColumnCount = 4: Define a quantidade de colunas visíveis.
  • .ColumnWidths = "50 pt; 80 pt; 120 pt; 70 pt": Define a largura de cada coluna.
  • .List = varMatriz: Alimenta a grade inteira a partir de uma matriz de memória.

📖 Exemplo Guiado: Passo a Passo no Laboratório

No computador do laboratório, acesse o VBE com Alt + F11.

Passo 1: Inserção do Formulário de Despacho

  1. No menu superior, clique em Inserir ➔ UserForm.
  2. Na janela de propriedades (F4), configure o formulário:
    • (Name): frmConsoleDespacho
    • Caption: FastLog S.A. - Console Integrado de Expedição
    • Width: 520
    • Height: 420

🛠️ Prática Obrigatória 1: Desenho e Configuração dos Controles Avançados

Desenharemos a interface avançada com listas, caixas de seleção e botões de opção.

Passo a Passo no Computador:

  1. Caixa de Combinação (ComboBox):
    • Desenhe um rótulo com Caption = "Filial de Destino:" (Top: 20, Left: 20).
    • Desenhe um ComboBox ao lado: (Name) = cboDestino | Top: 18, Left: 140, Width: 180.
  2. Moldura (Frame) com Botões de Opção (OptionButtons):
    • Desenhe um Frame: (Name) = fraTipoFrota | Caption = "Modalidade de Frota" | Top: 55, Left: 20, Width: 300, Height: 50.
    • Dentro do Frame, desenhe dois OptionButtons:
      • optPropria: Caption = "Frota Própria" | Value = True
      • optTerceirizado: Caption = "Terceirizado Agregado"
  3. Caixa de Seleção (CheckBox):
    • Desenhe um CheckBox: (Name) = chkSeguroEspecial | Caption = "Carga com Cobertura de Seguro Especial (Ad Valorem)" | Top: 115, Left: 20, Width: 320.
  4. Campo de Valor do Frete (TextBox):
    • Rótulo: "Valor Frete (R$):" (Top: 145, Left: 20).
    • TextBox: (Name) = txtValorFrete | Top: 143, Left: 140, Width: 100.
  5. Botão de Despacho:
    • CommandButton: (Name) = cmdDespachar | Caption = "🚀 Despachar Carga" | Top: 140, Left: 260, Width: 140, Height: 30.
  6. Painel de Despachos Recentes (ListBox):
    • Rótulo: "HISTÓRICO RECENTE DE EXPEDIÇÕES:" | Top: 185, Left: 20, Font Bold = True.
    • ListBox: (Name) = lstHistorico | Top: 205, Left: 20, Width: 465, Height: 150.
    • Na janela de propriedades de lstHistorico:
      • ColumnCount = 4
      • ColumnWidths = 50 pt; 120 pt; 110 pt; 90 pt

🛠️ Prática Obrigatória 2: Programação dos Eventos e Sincronização da ListBox

Codificaremos o carregamento automático da lista suspensa, o disparo do despacho e a atualização imediata da grade de relatórios na tela.

Passo a Passo no Computador:

  1. Dê um duplo clique no fundo do formulário para abrir o código de UserForm_Initialize.

  2. Digite o procedimento de inicialização e a sub-rotina auxiliar de sincronização da ListBox:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    
    Option Explicit
    
    ' Evento disparado assim que o formulário é carregado
    Private Sub UserForm_Initialize()
        ' 1. Popula a ComboBox com as filiais homologadas
        cboDestino.Clear
        cboDestino.AddItem "São Paulo - Matriz (SP)"
        cboDestino.AddItem "Rio de Janeiro - Filial (RJ)"
        cboDestino.AddItem "Belo Horizonte - Filial (MG)"
        cboDestino.AddItem "Curitiba - Filial (PR)"
        cboDestino.AddItem "Salvador - Hub Nordeste (BA)"
        cboDestino.ListIndex = 0 ' Seleciona a primeira filial por padrão
    
        ' 2. Carrega a ListBox com os dados já existentes na planilha
        Call AtualizarGradeHistorico
    End Sub
    
    ' Sub-rotina auxiliar que lê a base e joga para a ListBox
    Private Sub AtualizarGradeHistorico()
        Dim ws As Worksheet
        Dim lngUltimaLinha As Long
        Dim varMatriz As Variant
    
        Set ws = ThisWorkbook.Worksheets(1)
        lngUltimaLinha = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    
        lstHistorico.Clear
    
        ' Se houver mais de uma linha (cabeçalho + dados)
        If lngUltimaLinha >= 2 Then
            varMatriz = ws.Range("A2:D" & lngUltimaLinha).Value
            ' Atribuição em bloco ultrarrápida para a ListBox!
            lstHistorico.List = varMatriz
        End If
    End Sub
    
    ' Evento de clique do botão Despachar
    Private Sub cmdDespachar_Click()
        Dim ws As Worksheet
        Dim lngLinha As Long
        Dim strDestino As String
        Dim strModalidade As String
        Dim curValor As Currency
        Dim strSeguro As String
    
        Set ws = ThisWorkbook.Worksheets(1)
    
        ' Validações
        If cboDestino.Text = "" Then
            MsgBox "Selecione a filial de destino!", vbExclamation, "Expedição"
            Exit Sub
        End If
    
        If Not IsNumeric(txtValorFrete.Text) Or Val(txtValorFrete.Text) <= 0 Then
            MsgBox "Digite um valor de frete válido!", vbExclamation, "Expedição"
            txtValorFrete.SetFocus
            Exit Sub
        End If
    
        ' Coleta dos dados
        strDestino = cboDestino.Text
        curValor = CCur(txtValorFrete.Text)
    
        If optPropria.Value = True Then
            strModalidade = "FROTA PRÓPRIA"
        Else
            strModalidade = "TERCEIRIZADO"
        End If
    
        If chkSeguroEspecial.Value = True Then
            strSeguro = "COM SEGURO AD VALOREM"
        Else
            strSeguro = "SEGURO PADRÃO"
        End If
    
        ' Gravação na Planilha
        lngLinha = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row + 1
        ws.Cells(lngLinha, 1).Value = "DSP-" & (lngLinha - 1)
        ws.Cells(lngLinha, 2).Value = strDestino
        ws.Cells(lngLinha, 3).Value = strModalidade
        ws.Cells(lngLinha, 4).Value = FormatCurrency(curValor)
        ws.Cells(lngLinha, 5).Value = strSeguro
        ws.Cells(lngLinha, 6).Value = Now
    
        ws.Columns("A:F").AutoFit
    
        ' Sincroniza a ListBox na tela instantaneamente!
        Call AtualizarGradeHistorico
    
        ' Limpa campos e notifica
        txtValorFrete.Text = ""
        txtValorFrete.SetFocus
        MsgBox "Despacho " & ws.Cells(lngLinha, 1).Value & " realizado com sucesso!", vbInformation, "FastLog Concluído"
    End Sub
  3. Criação da Macro de Chamada no Módulo Padrão:

    • Em modProcessoExpedicao, crie:
      1
      2
      3
      
      Sub AbrirConsoleDespacho()
          frmConsoleDespacho.Show
      End Sub
  4. Preparação da Planilha e Teste:

    • Na Planilha 1, certifique-se de que a linha 1 possui os cabeçalhos:
      • A1: ID_Despacho | B1: Destino | C1: Modalidade | D1: Valor_Frete | E1: Seguro | F1: Data_Hora
    • Execute a macro AbrirConsoleDespacho (Alt + F8).
    • A janela gráfica surgirá com a lista de filiais pré-carregada na ComboBox.
    • Selecione "Curitiba - Filial (PR)", marque "Terceirizado Agregado", digite o frete 4500 e clique em 🚀 Despachar Carga.
    • Veja que no mesmo milissegundo a nova viagem aparece listada no painel ListBox dentro do próprio formulário!

✅ Resultado Esperado (Prática 2):

O formulário opera como um dashboard executivo interativo em tempo real, eliminando qualquer necessidade de fechar a janela para olhar a planilha.


📤 Instruções de Entrega (Microsoft Teams)

  1. Salve o arquivo como: Atividade_14_VBA_SeuNome_SeuSobrenome.xlsm.
  2. Acesse o Microsoft Teams na sua turma.
  3. Abra a tarefa: “VBA - Cap 14: ComboBox e ListBox Multicolunas”.
  4. Anexe a pasta de trabalho e confirme a entrega.

💡 Checkpoint de Gestão & TI

No gerenciamento de transporte rodoviário (TMS - Transportation Management System), o uso de listas restritas (ComboBox) elimina erros de roteirização. Se um operador digitar manualmente “RJ” e o ERP esperar “Rio de Janeiro”, a emissão da nota fiscal eletrônica trava no Ministério da Fazenda. Além disso, a exibição de históricos em ListBox reduz duplicidade de lançamentos em até 85%, aumentando a confiança dos gestores nas operações diárias.


🔥 Desafio de Fixação: Totalizador de Fretes na Tela

Adicione um rótulo de rodapé no formulário chamado lblTotalDespachado:

  1. Toda vez que a rotina AtualizarGradeHistorico for executada, percorra as linhas da ListBox somando a coluna 4 (Valor do Frete):
    1
    2
    3
    4
    5
    6
    
    Dim i As Long, curTotal As Currency
    curTotal = 0
    For i = 0 To lstHistorico.ListCount - 1
        curTotal = curTotal + CCur(lstHistorico.List(i, 3))
    Next i
    lblTotalDespachado.Caption = "Total Expedido no Turno: " & FormatCurrency(curTotal)
  2. Teste adicionando novos fretes e veja o total financeiro acumulado atualizar sozinho no formulário!

🔑 Revelar Gabarito Oficial de Laboratório (Clique para expandir)
💡 Incentivo Pedagógico: Tente resolver as práticas e o desafio de fixação por conta própria antes de abrir o gabarito. Seus erros e tentativas durante o laboratório são a chave para a consolidação real do aprendizado!

Código Consolidado (frmConsoleDespacho):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
Option Explicit

Private Sub UserForm_Initialize()
    cboDestino.Clear
    cboDestino.AddItem "São Paulo - Matriz (SP)"
    cboDestino.AddItem "Rio de Janeiro - Filial (RJ)"
    cboDestino.AddItem "Belo Horizonte - Filial (MG)"
    cboDestino.AddItem "Curitiba - Filial (PR)"
    cboDestino.AddItem "Salvador - Hub Nordeste (BA)"
    cboDestino.ListIndex = 0
    
    Call AtualizarGradeHistorico
End Sub

Private Sub AtualizarGradeHistorico()
    Dim ws As Worksheet
    Dim lngUltimaLinha As Long
    Dim varMatriz As Variant
    Dim i As Long
    Dim curTotal As Currency
    
    Set ws = ThisWorkbook.Worksheets(1)
    lngUltimaLinha = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    
    lstHistorico.Clear
    
    If lngUltimaLinha >= 2 Then
        varMatriz = ws.Range("A2:D" & lngUltimaLinha).Value
        lstHistorico.List = varMatriz
        
        ' Totalizador do Desafio de Fixação
        curTotal = 0
        For i = 0 To lstHistorico.ListCount - 1
            curTotal = curTotal + CCur(lstHistorico.List(i, 3))
        Next i
        lblTotalDespachado.Caption = "Total Expedido: " & FormatCurrency(curTotal)
    Else
        lblTotalDespachado.Caption = "Nenhum despacho registrado."
    End If
End Sub

Private Sub cmdDespachar_Click()
    Dim ws As Worksheet
    Dim lngLinha As Long
    Dim strDestino As String
    Dim strModalidade As String
    Dim curValor As Currency
    Dim strSeguro As String
    
    Set ws = ThisWorkbook.Worksheets(1)
    
    If cboDestino.Text = "" Then Exit Sub
    If Not IsNumeric(txtValorFrete.Text) Then Exit Sub
    
    strDestino = cboDestino.Text
    curValor = CCur(txtValorFrete.Text)
    
    If optPropria.Value = True Then
        strModalidade = "FROTA PRÓPRIA"
    Else
        strModalidade = "TERCEIRIZADO"
    End If
    
    If chkSeguroEspecial.Value = True Then
        strSeguro = "COM SEGURO AD VALOREM"
    Else
        strSeguro = "SEGURO PADRÃO"
    End If
    
    lngLinha = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row + 1
    ws.Cells(lngLinha, 1).Value = "DSP-" & Format(lngLinha - 1, "000")
    ws.Cells(lngLinha, 2).Value = strDestino
    ws.Cells(lngLinha, 3).Value = strModalidade
    ws.Cells(lngLinha, 4).Value = curValor
    ws.Cells(lngLinha, 5).Value = strSeguro
    ws.Cells(lngLinha, 6).Value = Now
    
    ws.Range("D2:D" & lngLinha).NumberFormat = "R$ #,##0.00"
    ws.Columns("A:F").AutoFit
    
    Call AtualizarGradeHistorico
    txtValorFrete.Text = ""
    txtValorFrete.SetFocus
End Sub

📝 Atividade Extra: Questionário de Fixação (Caderno)

Instruções: Responda no caderno, de próprio punho, as 10 perguntas abaixo com base no que você estudou neste capítulo. Ao concluir, apresente o caderno ao professor para visto.

  1. Qual é a finalidade do controle ComboBox e por que ele previne falhas de digitação em rotinas logísticas?
  2. Como funciona o método .AddItem para adicionar elementos a uma lista suspensa no VBA?
  3. O que o comando cboDestino.Clear realiza e por que ele é executado antes de recarregar a lista?
  4. Em que situação a propriedade .List é preferível ao método .AddItem ao trabalhar com ListBox?
  5. Quais são as três propriedades essenciais que devem ser configuradas para criar uma ListBox com múltiplas colunas?
  6. Qual é a principal diferença conceitual de uso entre um OptionButton e um CheckBox?
  7. Por que agrupamos OptionButtons dentro de um controle do tipo Frame?
  8. O que a propriedade ListCount de um ListBox retorna?
  9. Como recuperamos o valor de uma célula específica dentro de uma ListBox através de índices (linha, coluna)?
  10. No Desafio de Fixação, de que forma o laço somou os valores monetários diretamente a partir da lista na tela?