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.
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
- 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.
- Adicionar duas CheckBoxes independentes (chkWelcomeEmail, chkNewsletter) e confirmar nos testes que marcar uma nunca afeta a outra.
- 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).
- 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".
1. Substituindo cboStatus por um Frame com OptionButtons
- Exclua o
cboStatuse 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
.Valuede cada OptionButton individualmente comIf...ElseIf— não existe uma propriedade única que indique "qual do grupo está marcado". - Como
cboStatus.Valuenão existe mais, ocmdSave_Clickprecisa de uma variávelstatusconstruída a partir dos OptionButtons, gravada emnewRowem 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
cboCityera preenchido — chamadas repetidas de.AddItememUserForm_Initialize. ListIndex = -1significa 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á.
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
chkWelcomeEmailechkNewsletterindependentemente 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.Valueainda é usado para o salvamento real (newRow.Range(1, 5)), enquantolstCitiesexiste 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.
Obrigado pelo seu feedback!
Pergunte à IA
Pergunte à IA
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.
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
- 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.
- Adicionar duas CheckBoxes independentes (chkWelcomeEmail, chkNewsletter) e confirmar nos testes que marcar uma nunca afeta a outra.
- 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).
- 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".
1. Substituindo cboStatus por um Frame com OptionButtons
- Exclua o
cboStatuse 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
.Valuede cada OptionButton individualmente comIf...ElseIf— não existe uma propriedade única que indique "qual do grupo está marcado". - Como
cboStatus.Valuenão existe mais, ocmdSave_Clickprecisa de uma variávelstatusconstruída a partir dos OptionButtons, gravada emnewRowem 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
cboCityera preenchido — chamadas repetidas de.AddItememUserForm_Initialize. ListIndex = -1significa 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á.
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
chkWelcomeEmailechkNewsletterindependentemente 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.Valueainda é usado para o salvamento real (newRow.Range(1, 5)), enquantolstCitiesexiste 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.
Obrigado pelo seu feedback!