Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Impara Lavorare con i controlli modulo | Interazione con l'Utente
Excel VBA per l'Automazione Aziendale

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.

Figura 5.5

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à

  1. 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.
  2. Aggiungere due CheckBox indipendenti (chkWelcomeEmail, chkNewsletter) e confermare durante il test che la selezione di una non influisce mai sull'altra.
  3. 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).
  4. 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".
Suggerimento
expand arrow

1. Sostituzione di cboStatus con un Frame di OptionButton

  • Eliminare prima cboStatus e 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 .Value di ciascun OptionButton individualmente con If...ElseIf — non esiste una proprietà unica che indichi "quale del gruppo è selezionato".
  • Poiché cboStatus.Value non esiste più, in cmdSave_Click serve una variabile status costruita dagli OptionButton, da scrivere in newRow al 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 .AddItem in UserForm_Initialize.
  • ListIndex = -1 significa 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.
Soluzione
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

Note di test:

  • Il punto 2 (CheckBox) non ha una validazione dedicata — basta cliccare su chkWelcomeEmail e chkNewsletter indipendentemente 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.Value viene ancora utilizzato per il salvataggio effettivo (newRow.Range(1, 5)), mentre lstCities esiste 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à.
Tutto è chiaro?

Come possiamo migliorarlo?

Grazie per i tuoi commenti!

Sezione 5. Capitolo 4

Chieda ad AI

expand

Chieda ad AI

ChatGPT

Chieda pure quello che desidera o provi una delle domande suggerite per iniziare la nostra conversazione

Sezione 5. Capitolo 4
some-alt