Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprenda Trabalhando com Controles de Formulário | Interação com o Usuário
VBA do Excel para Automação Empresarial

Trabalhando com Controles de Formulário

Deslize para mostrar o menu

TextBoxes e ComboBoxes cobrem a maioria das entradas de dados, mas alguns outros controles expressam certos tipos de escolhas de forma mais natural — e ler seus valores corretamente envolve algumas armadilhas importantes que vale a pena conhecer antes de confiar neles.

Figura 5.5

CheckBoxes

A propriedade Value de um CheckBox é um Boolean simples — True quando marcado, False quando desmarcado:

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

Cada CheckBox em um formulário é completamente independente dos demais — marcar chkWelcomeEmail não afeta chkNewsletter, que é exatamente o comportamento desejado quando as escolhas não são mutuamente exclusivas.

OptionButtons

OptionButtons funcionam de forma oposta: dentro do mesmo grupo, selecionar um desmarca automaticamente todos os outros — exatamente o que as opções de Status do screenshot precisam, já que um cliente não pode ser Ativo e Inativo ao mesmo tempo. Para saber qual está selecionado, é preciso verificar o Value de cada um 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

A exclusividade mútua é automática e totalmente gerenciada pelo VBA — nunca é necessário escrever código para desmarcar os outros dois quando um é selecionado. O agrupamento, porém, é importante: OptionButtons só se excluem mutuamente dentro do mesmo contêiner (o próprio formulário ou um controle Frame colocado nele), então dois grupos separados de OptionButtons no mesmo formulário — por exemplo, Status e Priority — precisam estar em Frames diferentes, ou o VBA trataria todos como um único grupo mutuamente exclusivo.

ListBoxes

Um ListBox exibe várias opções de uma vez sem precisar ser aberto, como a lista de cidades na Figura 5.5. Ele é preenchido da mesma forma que um ComboBox, com .AddItem, e a seleção é lida por meio de .Value ou .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 é a posição do item selecionado, começando do 0 para o primeiro item — e, de forma crítica, é -1 sempre que nada foi selecionado ainda, que é a verificação usada por cmdAssign_Click para identificar quando o usuário clica no botão sem escolher uma cidade antes. .Value retorna diretamente o texto selecionado, o que geralmente é mais útil do que a posição numérica fornecida por .ListIndex.

Multi-Page Forms

Um controle MultiPage (as abas Details / Preferences no topo da Figura 5.5) agrupa controles relacionados em páginas separadas do mesmo formulário físico — útil quando um formulário tem campos demais para caber confortavelmente em uma única tela. Cada página é identificada pela posição, e apenas os controles da página visível são relevantes para o usuário, embora todos ainda existam e possam ser acessados pelo código independentemente de qual página está sendo exibida:

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

MultiPage1.Value segue a mesma contagem iniciada em 0 do ListIndex — a primeira página é 0, a segunda (Preferences, na Figura 5.5) é 1 — e o evento Change é disparado automaticamente sempre que o usuário clica em uma aba diferente.

Tarefa

  1. Adicionar um Frame contendo três OptionButtons (optActive, optInactive, optProspect) ao frmAddCustomer, substituindo o antigo ComboBox cboStatus, e escrever o bloco If...ElseIf mostrado acima para ler a seleção.
  2. Adicionar duas CheckBoxes independentes (chkWelcomeEmail, chkNewsletter) e confirmar nos testes que marcar uma nunca afeta a outra.
  3. Adicionar um ListBox (lstCities) preenchido em UserForm_Initialize, e escrever uma verificação que exiba um erro se o usuário tentar prosseguir sem nada selecionado (ListIndex = -1).
  4. Desafio: agrupar os campos Company Name, Contact e Email em uma página do MultiPage chamada "Details", e as CheckBoxes e OptionButtons em uma segunda página chamada "Preferences".
Dica
expand arrow

1. Substituindo cboStatus por um Frame com OptionButtons

  • Exclua o cboStatus e seu Label do formulário primeiro — você está substituindo, não adicionando ao lado.
  • Arraste um controle Frame para o formulário e, em seguida, arraste os três OptionButtons para dentro dos limites do Frame — esse agrupamento é o que os torna mutuamente exclusivos como um grupo.
  • Renomeie cada OptionButton individualmente na janela de Propriedades; o Frame pode manter uma legenda simples como "Status", mas não precisa de um nome para código, a menos que você planeje referenciá-lo diretamente.
  • Para ler a seleção, verifique o .Value de cada OptionButton individualmente com If...ElseIf — não existe uma propriedade única que indique "qual do grupo está marcado".
  • Como cboStatus.Value não existe mais, o cmdSave_Click precisa de uma variável status construída a partir dos OptionButtons, gravada em newRow em seu lugar.

2. Adicionando duas CheckBoxes independentes

  • Elas ficam diretamente no formulário (não dentro do Frame) — CheckBoxes fora de um mesmo container nunca afetam uma à outra, independentemente do agrupamento.
  • Para "confirmar nos testes", basta marcar uma, depois a outra, e observar que nenhuma desmarca a primeira.

3. Adicionando e validando um ListBox

  • Preencha da mesma forma que o cboCity era preenchido — chamadas repetidas de .AddItem em UserForm_Initialize.
  • ListIndex = -1 significa nada foi clicado ainda — essa é a condição exata a ser verificada antes de permitir o salvamento.
  • Essa verificação deve estar no mesmo bloco de validação dos outros campos obrigatórios, acrescentando ao mesmo string errors.

4. Desafio MultiPage

  • Arraste um controle MultiPage para o formulário — ele já vem com duas páginas por padrão, que é exatamente o necessário.
  • Renomeie cada página pelo menu de contexto → Page Order, ou selecionando a aba da página e usando o campo Caption na janela de Propriedades (Caption controla o texto visível da aba; há um campo separado (Name) também, caso queira referenciar a página por nome no código).
  • Mover controles existentes para uma página significa arrastá-los fisicamente para a área visível da página no modo Design — as referências de código para eles não mudam, já que o nome do controle não depende da página onde está.
Solução
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 teste:

  • O ponto 2 (CheckBoxes) não possui validação dedicada — basta clicar em chkWelcomeEmail e chkNewsletter independentemente e confirmar que cada uma alterna sem afetar a outra.
  • O ponto 4 (MultiPage) é apenas uma alteração de layout — não são necessárias mudanças no código além do que já está aqui, pois cada controle mantém seu próprio nome independentemente da página em que está.
  • Note que cboCity.Value ainda é usado para o salvamento real (newRow.Range(1, 5)), enquanto lstCities existe apenas como um controle separado, validado independentemente conforme a tarefa — os dois não estão conectados, a menos que você decida estender isso por conta própria.
Tudo estava claro?

Como podemos melhorá-lo?

Obrigado pelo seu feedback!

Seção 5. Capítulo 4

Pergunte à IA

expand

Pergunte à IA

ChatGPT

Pergunte o que quiser ou experimente uma das perguntas sugeridas para iniciar nosso bate-papo

Trabalhando com Controles de Formulário

TextBoxes e ComboBoxes cobrem a maioria das entradas de dados, mas alguns outros controles expressam certos tipos de escolhas de forma mais natural — e ler seus valores corretamente envolve algumas armadilhas importantes que vale a pena conhecer antes de confiar neles.

Figura 5.5

CheckBoxes

A propriedade Value de um CheckBox é um Boolean simples — True quando marcado, False quando desmarcado:

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

Cada CheckBox em um formulário é completamente independente dos demais — marcar chkWelcomeEmail não afeta chkNewsletter, que é exatamente o comportamento desejado quando as escolhas não são mutuamente exclusivas.

OptionButtons

OptionButtons funcionam de forma oposta: dentro do mesmo grupo, selecionar um desmarca automaticamente todos os outros — exatamente o que as opções de Status do screenshot precisam, já que um cliente não pode ser Ativo e Inativo ao mesmo tempo. Para saber qual está selecionado, é preciso verificar o Value de cada um 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

A exclusividade mútua é automática e totalmente gerenciada pelo VBA — nunca é necessário escrever código para desmarcar os outros dois quando um é selecionado. O agrupamento, porém, é importante: OptionButtons só se excluem mutuamente dentro do mesmo contêiner (o próprio formulário ou um controle Frame colocado nele), então dois grupos separados de OptionButtons no mesmo formulário — por exemplo, Status e Priority — precisam estar em Frames diferentes, ou o VBA trataria todos como um único grupo mutuamente exclusivo.

ListBoxes

Um ListBox exibe várias opções de uma vez sem precisar ser aberto, como a lista de cidades na Figura 5.5. Ele é preenchido da mesma forma que um ComboBox, com .AddItem, e a seleção é lida por meio de .Value ou .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 é a posição do item selecionado, começando do 0 para o primeiro item — e, de forma crítica, é -1 sempre que nada foi selecionado ainda, que é a verificação usada por cmdAssign_Click para identificar quando o usuário clica no botão sem escolher uma cidade antes. .Value retorna diretamente o texto selecionado, o que geralmente é mais útil do que a posição numérica fornecida por .ListIndex.

Multi-Page Forms

Um controle MultiPage (as abas Details / Preferences no topo da Figura 5.5) agrupa controles relacionados em páginas separadas do mesmo formulário físico — útil quando um formulário tem campos demais para caber confortavelmente em uma única tela. Cada página é identificada pela posição, e apenas os controles da página visível são relevantes para o usuário, embora todos ainda existam e possam ser acessados pelo código independentemente de qual página está sendo exibida:

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

MultiPage1.Value segue a mesma contagem iniciada em 0 do ListIndex — a primeira página é 0, a segunda (Preferences, na Figura 5.5) é 1 — e o evento Change é disparado automaticamente sempre que o usuário clica em uma aba diferente.

Tarefa

  1. Adicionar um Frame contendo três OptionButtons (optActive, optInactive, optProspect) ao frmAddCustomer, substituindo o antigo ComboBox cboStatus, e escrever o bloco If...ElseIf mostrado acima para ler a seleção.
  2. Adicionar duas CheckBoxes independentes (chkWelcomeEmail, chkNewsletter) e confirmar nos testes que marcar uma nunca afeta a outra.
  3. Adicionar um ListBox (lstCities) preenchido em UserForm_Initialize, e escrever uma verificação que exiba um erro se o usuário tentar prosseguir sem nada selecionado (ListIndex = -1).
  4. Desafio: agrupar os campos Company Name, Contact e Email em uma página do MultiPage chamada "Details", e as CheckBoxes e OptionButtons em uma segunda página chamada "Preferences".
Dica
expand arrow

1. Substituindo cboStatus por um Frame com OptionButtons

  • Exclua o cboStatus e seu Label do formulário primeiro — você está substituindo, não adicionando ao lado.
  • Arraste um controle Frame para o formulário e, em seguida, arraste os três OptionButtons para dentro dos limites do Frame — esse agrupamento é o que os torna mutuamente exclusivos como um grupo.
  • Renomeie cada OptionButton individualmente na janela de Propriedades; o Frame pode manter uma legenda simples como "Status", mas não precisa de um nome para código, a menos que você planeje referenciá-lo diretamente.
  • Para ler a seleção, verifique o .Value de cada OptionButton individualmente com If...ElseIf — não existe uma propriedade única que indique "qual do grupo está marcado".
  • Como cboStatus.Value não existe mais, o cmdSave_Click precisa de uma variável status construída a partir dos OptionButtons, gravada em newRow em seu lugar.

2. Adicionando duas CheckBoxes independentes

  • Elas ficam diretamente no formulário (não dentro do Frame) — CheckBoxes fora de um mesmo container nunca afetam uma à outra, independentemente do agrupamento.
  • Para "confirmar nos testes", basta marcar uma, depois a outra, e observar que nenhuma desmarca a primeira.

3. Adicionando e validando um ListBox

  • Preencha da mesma forma que o cboCity era preenchido — chamadas repetidas de .AddItem em UserForm_Initialize.
  • ListIndex = -1 significa nada foi clicado ainda — essa é a condição exata a ser verificada antes de permitir o salvamento.
  • Essa verificação deve estar no mesmo bloco de validação dos outros campos obrigatórios, acrescentando ao mesmo string errors.

4. Desafio MultiPage

  • Arraste um controle MultiPage para o formulário — ele já vem com duas páginas por padrão, que é exatamente o necessário.
  • Renomeie cada página pelo menu de contexto → Page Order, ou selecionando a aba da página e usando o campo Caption na janela de Propriedades (Caption controla o texto visível da aba; há um campo separado (Name) também, caso queira referenciar a página por nome no código).
  • Mover controles existentes para uma página significa arrastá-los fisicamente para a área visível da página no modo Design — as referências de código para eles não mudam, já que o nome do controle não depende da página onde está.
Solução
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 teste:

  • O ponto 2 (CheckBoxes) não possui validação dedicada — basta clicar em chkWelcomeEmail e chkNewsletter independentemente e confirmar que cada uma alterna sem afetar a outra.
  • O ponto 4 (MultiPage) é apenas uma alteração de layout — não são necessárias mudanças no código além do que já está aqui, pois cada controle mantém seu próprio nome independentemente da página em que está.
  • Note que cboCity.Value ainda é usado para o salvamento real (newRow.Range(1, 5)), enquanto lstCities existe apenas como um controle separado, validado independentemente conforme a tarefa — os dois não estão conectados, a menos que você decida estender isso por conta própria.
Tudo estava claro?

Como podemos melhorá-lo?

Obrigado pelo seu feedback!

Seção 5. Capítulo 4
some-alt