Lavorando col VBA di Excel si ha la possibilità di usare i controlli ListBox e ComboBox a più colonne. La tecnica è leggermente differente dall'uso degli equivalenti controlli ad una colonna.
Ricordo che anche in questi casi
Questi sono i dati che useremo in questi esempi (oramai penso che avremmo acquisito il nuovo metodo di lavorare con gli intervalli senza più fare riferimento alla classica notazione A1):
| Cognome | Nome | Indirizzo | CAP | Città |
|---|---|---|---|---|
| Migliavacca | Luigi | VIA DEL TUSCOLANO 1 | 42033 | Muscoline |
| Scicchitano | Andrea | VIA SABIN 34/2 | 67020 | Rifreddo di Saluzzo |
| Migliavacca | Luigi | VIA DEL TUSCOLANO 1 | 42033 | Muscoline |
| Migliavacca | Luigi | VIA DEL TUSCOLANO 1 | 42033 | Muscoline |
| Edelvisi | Maurizio | STRADA MARCHESANE 434 | 58020 | Uppello |
| Borsotti | Mirella | STRADA MARCHESANE 434 | 26010 | Malborghetto |
| Maggi | Massimo | VIA SABIN, 34/2 | 32032 | Cittadella |
Come vedremo qui di seguito esistono molti metodi per riempire una ListBox o una ComboBox.
Il discorso è leggermente articolato e spero di riuscire ad esporlo chiaramente.
Per comodità trascrivo l'ordine che ho seguito nell'esposizione di queste note.
Questo metodo possiamo applicarlo sia con l'uso di matrici, sia leggendo i dati direttamente da un intervallo del foglio di Excel
La ListBox viene riempita usando degli opportuni cicli ed usando l'ormai famoso Additem.
In VBA si ha la possibilità di usare anche ListBox e ComboBox a più colonne. Ma per usare queste occorrono alcuni accorgimenti.
Dalla guida in linea leggiamo queste notizie riguardanti queste voci.
Ma passiamo subito a degli esempi pratici.
Per usare una matrice a due dimensioni per un controllo ListBox o ComboBox eseguiamo queste operazioni:
Dim Intervallo As Range
Dim Righe, Colonne, R, C
Dim Matrice()
Worksheets("Foglio1").Select
With Range("A1").CurrentRegion
Righe = .Rows.Count - 1
Colonne = .Columns.Count
ReDim Matrice(1 To Righe, 1 To Colonne)
For R = 1 To Righe
For C = 1 To Colonne
Matrice(R, C) = .Cells(R + 1, C)
Next
Next
End With
With ListBox1
.Clear
.ColumnCount = Colonne
.BoundColumn = 2
For R = 1 To Righe
.AddItem
For C = 1 To Colonne
ListBox1.List(R - 1, C - 1) = Matrice(R, C)
Next
Next
End With
Se invece preleviamo i dati da un intervallo del foglio di Excel possiamo notare che la procedura è simile a quella appena vista:
Dim Righe, Colonne, R, C
Dim Intervallo As Range
With Range("A1").CurrentRegion
Righe = .Rows.Count - 1
Colonne = .Columns.Count
Set Intervallo = .Offset(1, 0).Resize(Righe, Colonne)
End With
With ListBox1
.Clear
.ColumnCount = Colonne
.BoundColumn = 2
For R = 1 To Righe
.AddItem
For C = 1 To Colonne
.List(R - 1, C - 1) = Intervallo(R, C)
Next
Next
End With
Invece di usare cicli, usando particolari proprietà e/o metodi è possibile riempire i controlli ListBox e ComboBox con una semplice istruzione
In questo esempio, per caricare il controllo ListBox con i valori di una matrice viene usata la proprietà List(). In questo caso, la proprietà List non include indici tra le sue parentesi.
Dim Intervallo As Range
Dim Righe, Colonne, R, C
Dim Matrice()
With Range("A1").CurrentRegion
Righe = .Rows.Count - 1
Colonne = .Columns.Count
ReDim Matrice(1 To Righe, 1 To Colonne)
For R = 1 To Righe
For C = 1 To Colonne
Matrice(R, C) = .Cells(R + 1, C)
Next
Next
End With
With ListBox1
.ColumnCount = Colonne
.BoundColumn = 2
.List() = Matrice
End With
Usare un intervallo per riempire una ListBox o una ComboBox è simile a quel che abbiamo appena visto.
Ma al posto di usare la proprietà List è necessario usare la proprietà RowSource che specifica l'origine da cui proviene un elenco di un controllo ComboBox o ListBox
Dim Righe, Colonne
Dim Intervallo As Range
With Range("A1").CurrentRegion
Righe = .Rows.Count - 1
Colonne = .Columns.Count
Set Intervallo = .Offset(1, 0).Resize(Righe, Colonne)
End With
With ListBox1
.ColumnCount = Colonne
.BoundColumn = 2
.RowSource = Intervallo.Address
End With
L'intestazione di colonna funziona solo quando per i controlli ComboBox o ListBox è impostata la proprietà ColumnHeads e viene usata la proprietà RowSource per caricare questi controlli. In questo caso viene usata come intestazione di colonna la riga immediatamente sopra l'intervallo che viene mandato nel controllo.
Ecco il codice di esempio:
Dim Righe, Colonne
Dim Intervallo As Range
With Range("A1").CurrentRegion
Righe = .Rows.Count - 1
Colonne = .Columns.Count
Set Intervallo = .Offset(1, 0).Resize(Righe, Colonne)
End With
With ListBox1
.ColumnHeads = True
.ColumnCount = Colonne
.BoundColumn = 2
.RowSource = Intervallo.Address
End With
E' possibile che qualche volta abbiamo la necessità di trasferire il contenuto di una matrice o di un intervallo ruotando i dati, ossia visualizzare i dati in colonne anzichè in righe come in questo esempio:
| Migliavacca | Rizzi | Liberali | Di Maggio | ecc. |
| Luigi | Carlo | Franca | Mario | ecc. |
| VIA P. FIMIANI-LOC.TRIVIO | VIA SABIN 34/2 | VIA DEL TUSCOLANO 1 | TERMINAL CONTENITORI MOLO VII | ecc. |
| 35031 | 84040 | 67030 | 48012 | ecc. |
| Varena | Noli | Calcinatello | Verzegnis | ecc. |
| FR | TO | BO | AL | ecc. |
A noi invece servono i dati disposti in quest'altra maniera:
| Migliavacca | Luigi | VIA P. FIMIANI-LOC.TRIVIO | 35031 | Varena | FR |
| Rizzi | Carlo | VIA SABIN 34/2 | 84040 | Noli | TO |
| Liberali | Franca | VIA DEL TUSCOLANO 1 | 67030 | Calcinatello | BO |
| Di Maggio | Mario | TERMINAL CONTENITORI MOLO VII | 48012 | Verzegnis | AL |
| Scicchitano | Andrea | VIA SABIN, 34/2 | 10060 | Peri | NA |
| Maggi | Massimo | VIA DEL TUSCOLANO 1 | 20070 | Formignana | VI |
| Gerardini | Silvano | Z.I SAMBUCETO | 61040 | Villaretto Chisone | LC |
| ecc. | ecc. | ecc. | ecc. | ecc. | ecc. |
Può capitare che per memorizzare dei dati in una matrice non possiamo dimensionare opportunamente la matrice con
ReDim Matrice(1 To Righe, 1 To Colonne)
perchè non conosciamo a priori la quantità di record da memorizzare. In questo caso dobbiamo rfidimensionare la matrice man mano che leggiamo i record con
ReDim Preserve Matrice(1 To Colonne, 1 To R)
Questo caso può succedere per esempio quando andiamo a leggere dei record residenti in un file di testo.
Vediamo con questo esempio cosa succede durante la lettura del file di testo e mandare i dati così letti in un controllo ListBox o ComboBox.
Dim Percorso, FileTesto, Filenum, Campi
Dim Riga, Matrice()
Dim Righe, Colonne, R, C
' viene determinato il nome del file da leggere comprensivo di percorso completo
Percorso = ThisWorkbook.Path & "\"
FileTesto = Percorso & "archivio.txt"
' quindi si verifica se il file esiste
If Dir(FileTesto) = "" Then
MsgBox "file non trovato"
Exit Sub
End If
' si inizia la lettura del file di testo riga per riga
Righe = 0
Filenum = FreeFile
Open FileTesto For Input As #Filenum
' nella prima riga del file di testo sono memorizzati i nomi dei campi
' da questi si determinano quanti campi deve contenere la matrice
Line Input #Filenum, Riga
Colonne = UBound(Split(Riga, "*")) + 1
' da qui continuo con la lettura del file di testo riga per riga
While Not EOF(Filenum)
Line Input #Filenum, Riga
Campi = Split(Riga, "*")
R = R + 1
' dopo aver suddiviso le singole righe lette dal file di testo
' incremento la matrice e memorizzo i nuovi dati nella matrice
ReDim Preserve Matrice(1 To Colonne, 1 To R)
For C = 0 To UBound(Campi)
Matrice(C + 1, R) = Campi(C)
Next
Wend
Close #Filenum
' finita la lettura del file ho la matrice pronta ma con i dati invertiti
With ListBox1
.Clear
.ColumnCount = Colonne
.BoundColumn = 2
.Column() = Matrice
End With
Il codice esposto qui sopra è abbastanza commentato per cui mi esimo dall'aggiungere ulteriori commenti. Aggiungo solo queste poche note.
Alla fine della lettura del file di testo nella matrice abbiamo i record disposti nelle colonne anzichè nelle righe.
A noi invece serve che i record siano giustamente nelle righe.
La ListBox del VBA riesce ad accogliere tra le sue righe la matrice ruotata di 90° con la istruzione ListBox1.Column() = Matrice anzichè ListBox1.List() = Matrice
Repetita iuvant
Primo caso:
ReDim Matrice(1 To Colonne, 1 To Righe)
For R = 1 To Righe
For C = 1 To Colonne
Matrice(R, C) = Intervallo(R, C)
Next
Next
La matrice ha questo schema:
| Campo 1 | Campo 2 | Campo 3 | |
| Record 1 | |||
| Record 2 | |||
| Record 3 | |||
| Record 4 | |||
| Record 5 | |||
| Record 6 | |||
| Record 7 |
secondo caso:
For R = 1 To Righe
ReDim Preserve Matrice(1 To Colonne, 1 To R)
For C = 1 To Colonne
Matrice(C, R) = Intervallo(R, C)
Next
Next
La matrice ha questo schema:
| Record 1 | Record 2 | Record 3 | Record 4 | Record 5 | Record 6 | Record 7 | |
| Campo 1 | |||||||
| Campo 2 | |||||||
| Campo 3 |
Ma vediamo come comportarci se la stessa situazione la troviamo in un foglio di Excel.
Questo metodo è un po' più complesso, nella sintassi da usare rispetto a quelli fin qui visti.
Dim Righe, Colonne
Dim Intervallo As Range
Worksheets("Foglio4").Select
With Range("A1").CurrentRegion
Righe = .Rows.Count - 1
Colonne = .Columns.Count
Set Intervallo = .Offset(1, 0).Resize(Righe, Colonne)
End With
With ListBox1
.ColumnCount = Colonne
.BoundColumn = 2
.List() = Application.Transpose(Intervallo)
End With
Per leggere l'elemento selezionato da una ListBox a più colonne possiamo usare uno dei seguenti metodi:
lettura della voce scelta nella ListBox aiutati dalla proprietà Value (di default, quindi non necessario)
Se abbiamo definito una BoundColumn ci verrà restituito il dato presente nella colonna designata
In caso contrario ci viene restituito il dato della prima colonna
TextBox1 = ListBox1
lettura della voce aiutati dalla proprietà List() indicando la riga, normalmente restituito dalla Proprietà ListIndex e la colonna di nostra scelta da cui prelevare il dato
R = ListBox1.ListIndex
TextBox1 = ListBox1.List(R, 3)
| <<<Prima parte | Seconda parte | Terza parte>>> | Quarta parte >>> |