Lavorare con i controlli modulo
Scorri per mostrare il menu
TextBoxes e ComboBoxes coprono la maggior parte dell'inserimento dati, ma alcuni altri controlli esprimono certi tipi di scelte in modo più naturale — e leggere correttamente i loro valori presenta alcune insidie reali che vale la pena conoscere prima di farci affidamento.
CheckBox
La proprietà Value di un CheckBox è un semplice Booleano — True quando è selezionato, False quando non lo è:
If chkWelcomeEmail.Value = True Then
MsgBox "A welcome email would be sent here."
End If
Ogni CheckBox su un form è completamente indipendente dagli altri — selezionare chkWelcomeEmail non ha effetto su chkNewsletter, che è esattamente il comportamento desiderato quando le scelte non sono mutuamente esclusive.
OptionButton
Gli OptionButton si comportano in modo opposto: all'interno dello stesso gruppo, selezionarne uno deseleziona automaticamente tutti gli altri — proprio ciò di cui hanno bisogno le scelte di Status nello screenshot, dato che un cliente non può essere contemporaneamente sia Active che Inactive. Per leggere quale è selezionato, bisogna controllare il valore di ciascuno singolarmente:
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
L'esclusività reciproca è automatica e gestita interamente da VBA — non è mai necessario scrivere codice che deselezioni gli altri due quando uno viene selezionato. Tuttavia, il raggruppamento è importante: gli OptionButton si escludono solo all'interno dello stesso contenitore (il form stesso o un controllo Frame posizionato su di esso), quindi due gruppi separati di OptionButton sullo stesso form — ad esempio, Status e Priority — devono essere inseriti in due Frame diversi, altrimenti VBA li tratterebbe tutti come un unico gruppo mutuamente esclusivo.
ListBox
Una ListBox mostra diverse scelte contemporaneamente senza dover essere aperta con un clic, come la lista City nella Figura 5.5. Si popola allo stesso modo di una ComboBox, con .AddItem, e si legge la selezione tramite .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 è la posizione dell'elemento selezionato, contando da 0 per il primo elemento — e, cosa fondamentale, è -1 quando non è stato ancora selezionato nulla, che è il controllo che cmdAssign_Click utilizza per intercettare un utente che clicca il pulsante senza aver prima scelto una città. .Value restituisce direttamente il testo selezionato, che di solito è più utile della posizione numerica fornita da .ListIndex.
Moduli Multi-Page
Un controllo MultiPage (le schede Details / Preferences in alto nella Figura 5.5) raggruppa controlli correlati su pagine separate dello stesso form fisico — utile quando un form ha troppi campi per stare comodamente su un solo schermo. Ogni pagina è identificata dalla posizione, e solo i controlli sulla pagina attualmente visibile sono significativi per l'utente, anche se tutti esistono e possono essere letti dal codice indipendentemente dalla pagina visualizzata:
Private Sub MultiPage1_Change()
If MultiPage1.Value = 1 Then
MsgBox "Now viewing Preferences"
End If
End Sub
MultiPage1.Value segue la stessa numerazione da 0 di ListIndex — la prima pagina è 0, la seconda (Preferences, nella Figura 5.5) è 1 — e l'evento Change viene attivato automaticamente ogni volta che l'utente clicca su una scheda diversa.
Attività
- Aggiungere un Frame contenente tre OptionButton (optActive, optInactive, optProspect) a frmAddCustomer, sostituendo la precedente ComboBox cboStatus, e scrivere il blocco If...ElseIf mostrato sopra per leggere la selezione.
- Aggiungere due CheckBox indipendenti (chkWelcomeEmail, chkNewsletter) e confermare durante il test che la selezione di una non influisce mai sull'altra.
- Aggiungere una ListBox (lstCities) popolata in UserForm_Initialize, e scrivere un controllo che mostri un errore se l'utente tenta di procedere senza aver selezionato nulla (ListIndex = -1).
- Sfida: raggruppare i campi Company Name, Contact ed Email in una pagina MultiPage chiamata "Details", e le CheckBox e OptionButton in una seconda pagina chiamata "Preferences".
1. Sostituzione di cboStatus con un Frame di OptionButton
- Eliminare prima
cboStatuse la sua etichetta dal form — lo si sta sostituendo, non aggiungendo accanto. - Trascinare un controllo Frame sul form, quindi trascinare tutti e tre gli OptionButton all'interno del bordo del Frame — questa inclusione li rende mutuamente esclusivi come gruppo.
- Rinominare ciascun OptionButton individualmente nella finestra delle Proprietà; il Frame può mantenere una Caption semplice come "Status" ma non necessita di un nome adatto al codice a meno che non si intenda farvi riferimento direttamente.
- Per leggere la selezione occorre controllare il
.Valuedi ciascun OptionButton individualmente conIf...ElseIf— non esiste una proprietà unica che indichi "quale del gruppo è selezionato". - Poiché
cboStatus.Valuenon esiste più, incmdSave_Clickserve una variabilestatuscostruita dagli OptionButton, da scrivere innewRowal suo posto.
2. Aggiunta di due CheckBox indipendenti
- Queste si trovano direttamente sul form (non all'interno del Frame) — le CheckBox fuori da un contenitore condiviso non si influenzano mai a vicenda, indipendentemente dal raggruppamento.
- Per "confermare durante il test", basta selezionare una, poi l'altra, e verificare che nessuna deselezioni la prima.
3. Aggiunta e validazione di una ListBox
- Popolarla nello stesso modo in cui veniva popolata
cboCity— chiamate ripetute a.AddIteminUserForm_Initialize. ListIndex = -1significa nessuna selezione effettuata — questa è la condizione esatta da verificare prima di consentire il salvataggio.- Questo controllo va inserito nello stesso blocco di validazione degli altri campi obbligatori, aggiungendo all'eventuale stringa
errors.
4. Sfida MultiPage
- Trascinare un controllo MultiPage sul form — viene fornito con due pagine di default, che sono esattamente quante servono.
- Rinominare ciascuna pagina tramite il menu contestuale → Page Order, oppure selezionando la scheda della pagina e usando il campo Caption nella finestra delle Proprietà (Caption controlla il testo visibile sulla scheda; esiste anche un campo (Name) separato se si vuole fare riferimento a una pagina per nome nel codice).
- Spostare i controlli esistenti su una pagina significa trascinarli fisicamente nell'area visibile di quella pagina in visualizzazione struttura — i riferimenti nel codice non cambiano, poiché il nome di un controllo non dipende dalla pagina su cui si trova.
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
Note di test:
- Il punto 2 (CheckBox) non ha una validazione dedicata — basta cliccare su
chkWelcomeEmailechkNewsletterindipendentemente e confermare che ciascuna si attivi/disattivi senza influenzare l'altra. - Il punto 4 (MultiPage) è solo una modifica di layout — non sono necessari cambiamenti di codice oltre a quelli già presenti, poiché ogni controllo mantiene il proprio nome indipendentemente dalla pagina su cui viene trascinato.
- Si noti che
cboCity.Valueviene ancora utilizzato per il salvataggio effettivo (newRow.Range(1, 5)), mentrelstCitiesesiste solo come controllo separato e validato indipendentemente secondo la consegna — i due non sono collegati tra loro a meno che non si scelga di estendere la funzionalità.
Grazie per i tuoi commenti!
Chieda ad AI
Chieda ad AI
Chieda pure quello che desidera o provi una delle domande suggerite per iniziare la nostra conversazione