Lavorare con dati esterni (3)

 

Dopo le due precedenti pagine (esempio e esempio)un po' teoriche possiamo vedere un piccolo esempio applicativo: una semplice rubrica basata su un file esterno.

Andiamo in ambiente VBA (ALT + F11) ed iniziamo a costruire una userform simile a questa.

Vediamo ora le azioni che svolgeremo nel modulo relativo a questa UserForm.

Ad inizio modulo dichiariamo alcune variabili che ci serviranno in tutto il lavoro per non ripetere inutilmente azioni o istruzioni per richiamare i valori contenuti in alcune variabili:

Dim MatriceDati()
Dim NomeFile As String
Dim Corrente

All'apertura della UserForm, usando il suo evento UserForm_Activate, leggiamo il file di testo e memorizziamo il suo contenuto in una matrice e, nello stesso tempo riempiamo la ComboBox col primo campo di ogni record.

Nota importante

Particolare attenzione va posta al modo di popolamento della matrice MatriceDati()

Normalmente noi pensiamo una matrice a due dimensioni come ad una tabella formata da righe (Record) e colonne (Campi) come in questo esempio:

 
I campi
I record
|
|
|
\/
Nome Cognome Via CAP Città Prov
Migliavacca Luigi VIA P. FIMIANI-LOC.TRIVIO 35031 Varena FR
Rizzi Carlo VIA SABIN 34/2 35031 Noli TO
Liberali Franca VIA DEL TUSCOLANO 1 67030 Calcinatello BO

In questo caso è necessario pensare ad una matrice impostata in maniera diversa.

La matrice dinamica che andremo ad usare in questo applicativo andrà ridimensionata man mano che riceveremo i dati usando:

ReDim Preserve Matricei(X, Y)

Ma una una matrice dinamica può essere ridimensionata, senza perdere i dati in essa già memorizzati, incrementandone la seconda dimensione (quella contraddistinta con la y).

Nel nostro caso, mentre i campi dei record sono noti, occorre ridimensionare ed incrementare il numero dei record, per cui dobbiamo impostare la matrice in questo altro modo:

 
I record ------>
I campi
|
|
|
\/
Nome
Migliavacca Rizzi Liberali
Cognome
Luigi Carlo Franca
Via
VIA P. FIMIANI-LOC.TRIVIO VIA SABIN 34/2 VIA DEL TUSCOLANO 1
CAP
35031 35031 67030
Città
Varena Noli Calcinatello
Prov
FR TO BO

Per lavorare con una matrice così fatta occorre usare una tecnica particolare nell'usare i due cicli For ... Next nidificati che servono per leggere le righe e le colonne.

 

All'apertura della UserForm

Viene caricata la matrice di lavoro e viene caricata la ComboBox con tutti i nomi memorizzati nel database.

Private Sub UserForm_Activate()
Dim Cartella As String
Dim R1 As Integer, C1 As Integer, C As Integer
Dim Riga As String, Campi() As String, Filenum As Integer
Cartella = ThisWorkbook.Path & "\"
NomeFile = Cartella & "anagrafica4.txt"
If Len(Dir(NomeFile)) = 0 Then ' controllo se il file esiste ' viene controllato se esiste il file di testo
MsgBox "nessun file da leggere"
Exit Sub
End If
R1 = 0
Filenum = FreeFile
Open NomeFile For Input As #Filenum ' apro il file in lettura
Do
Line Input #Filenum, Riga ' dal file leggo una linea alla volta
ReDim Preserve MatriceDati(1 To 6, R1) ' qui ridimensiono la matrice che deve accogliere i nuovi dati
Campi = Split(Riga, "|") ' per leggere i campi suddivido la stringa letta usando il separatore "|"
C = 0
For C1 = LBound(Campi) To UBound(Campi) ' memorizzo i dati nella matrice
C = C + 1
MatriceDati(C, R1) = Campi(C1)
If C = 1 And R1 <> 0 Then ComboBox1.AddItem Campi(C1) ' aggiungo il primo campo alla ComboBox
Next
R1 = R1 + 1
Loop Until EOF(Filenum) = True
' continuo a leggere i dati dal file di testo fino a quando non ne raggiungo la fine
Close #Filenum
ComboBox1.ListIndex = -1
Corrente = 0
Label7.Caption = UBound(MatriceDati, 2) & " record in archivio"
End Sub

Fatto questo lavoro verrà mostrata la UserForm con questo aspetto.

Prima di passare ad istruire le azioni per la gestione vera e propria del database (i pulsanti, Elimina, Nuovo, Modifica), iniziamo con lo scrivere codice per la ComboBox ed i due pulsanti di navigazione.

 

I pulsanti CommandButton1, CommandButton2, e la ComboBox1

Cambiando il valore della ListIndex della ComboBox coi due pulsanti, avanti ed indietro, evochiamo l'evento Change della ComboBox per cui evitiamo di scrivere codice per aggiornare le caselle di testo.

Le caselle di testo vengono aggiornate usando l'evento Change della ComboBox prendendo dalla matrice i dati dal record puntato dalla variabile Corrente.

Private Sub ComboBox1_Change()
If ComboBox1.ListIndex = -1 Then Exit Sub
Dim C1 As Integer
Corrente = ComboBox1.ListIndex + 1
For C1 = 1 To 6
Controls("Textbox" & C1) = MatriceDati(C1, Corrente)
Next
Label9.Caption = Corrente
End Sub


Private Sub CommandButton1_Click()
'indietro
If Corrente <= 1 Then Exit Sub
Corrente = Corrente - 1
ComboBox1.ListIndex = Corrente - 1 ' cambiando la ListIdex alla ComboBox ne evochiamo l'evento Change
End Sub


Private Sub CommandButton2_Click()
'avanti
If Corrente >= ComboBox1.ListCount Then Exit Sub
Corrente = Corrente + 1
ComboBox1.ListIndex = Corrente - 1 ' cambiando la ListIdex alla ComboBox ne evochiamo l'evento Change
End Sub

 

Il pulsante CommandButton3 (Elimina Record)

Vediamo ora come eliminare un record.

In questa fase dobbiamo modificare vari elementi:

Private Sub CommandButton3_Click()
'elimina
Dim Risposta As String
Dim R1 As Integer, R As Integer, C As Integer
Dim FileNum As Integer
Dim CTRL As Control
If ComboBox1.ListIndex = -1 Then
MsgBox "Nessun record selezionato o archivio vuoto"
Exit Sub
End If

Risposta = MsgBox("eliminare il record relativo a " & MatriceDati(1, Corrente) _
& "?", vbYesNo + vbExclamation, "ATTENZIONE")
If Risposta = vbNo Then Exit Sub
R1 = UBound(MatriceDati, 2)
'per eliminare un record nella matrice sposto in su i record sottostanti a quello da eliminare
For R = Corrente + 1 To R1
For C = 1 To 6
MatriceDati(C, R - 1) = MatriceDati(C, R)
Next
Next
'alla fine elimino l'ultimo record che è il duplicato del penultimo
'l'operazione si compie ridimensionando opportunamente la matrice
ReDim Preserve MatriceDati(1 To 6, R1 - 1)
'viene eliminata la voce relativa al record anche nella ComboBox1
ComboBox1.RemoveItem (Corrente - 1)
' registrazione delle modifiche nel file di testo
R1 = UBound(MatriceDati, 2)
FileNum = FreeFile()
Open NomeFile For Output As #FileNum ' anagrafica4.txt
For R = 0 To R1
For C = 1 To 5
Print #FileNum, MatriceDati(C, R) & "|";
Next
Print #FileNum, MatriceDati(C, R)
Next
Close #FileNum
ComboBox1.ListIndex = -1
Corrente = 0
Label7.Caption = UBound(MatriceDati, 2) & " record in archivio"
For Each CTRL In Controls
If (TypeName(CTRL) = ("TextBox")) Then
CTRL = ""
End If
Next
Corrente = 0
End Sub

Il pulsante CommandButton5 (Modifica Record)

Per modificare il record corrente vengono eseguite queste operazioni:

Private Sub CommandButton5_Click()
'modifica
Dim Risposta As String
Dim C1 As Integer, R1 As Integer, R, C
Dim A, B, FileNum
If ComboBox1.ListIndex = -1 Then
MsgBox "Nessun record selezionato o archivio vuoto"
Exit Sub
End If

Risposta = MsgBox("modificare il record relativo a " & ComboBox1.List(Corrente - 1) _
& "?", vbYesNo + vbExclamation, "ATTENZIONE")
If Risposta = vbNo Then Exit Sub
'modifica della matrice sostituendone i valori del record corrente
'con quelli letti dalle caselle di testo

For C1 = 1 To 6
MatriceDati(C1, Corrente) = Controls("Textbox" & C1)
Next
'viene aggiornata la ComboBox col valore letto dalla prima casella di testo
ComboBox1.List(Corrente - 1) = MatriceDati(1, Corrente)
R1 = UBound(MatriceDati, 2)
'viene riscritto il file di testo coi nuovi valori appena acquisiti
FileNum = FreeFile()
Open NomeFile For Output As #FileNum
For R = 0 To R1
For C = 1 To 5
Print #FileNum, MatriceDati(C, R) & "|";
Next
Print #FileNum, MatriceDati(C, R)
Next
Close #FileNum
End Sub

Il pulsante CommandButton4 (Aggiungi Record)

Questa è la fase più complessa di tutto l'applicativo che sto presentando.

Per evitare confusione in questa fase delicata del programma:

  1. Se la Caption del CommandButton4 è uguale a "Nuovo" (ho appena scelto l'opzione di voler inserire un nuovo record)
    • disabilito i CommandButton 1, 2, 3 e 5
    • cambio la Caption del CommandButton4 in "OK/Annulla"
    • cancello tutte le TextBox per prepararle ai nuovi inserimenti
    • porto a -1 la ListIndex della ComboBox (nessun valore visibile)
    • porto la variabile Corrente che punta al record corrente a 0
  2. Se la Caption del CommandButton4 è uguale a "OK/Annulla" (accetto i dati appena inseriti o, se non ho inserito nulla, voglio uscire da questa procedura)
    • riabilito i CommandButton 1, 2, 3 e 5
    • riporto la Caption del CommandButton4 a "Nuovo"
    • se non ho fatto alcun inserimento di nuovi dati esco subito dalla procedura
    • aggiungo alla ComboBox il valore che trovo nella textBox1
    • ridimensiono la MatriceDati() aggiungendo lo spazio per un n uovo record
    • accodo alla matrice i dati trovati nelle caselle di testo
    • aggiungo anche al file di testo usando il metodo Append gli stessi dati
    • cancello tutte le caselle di testo

Private Sub CommandButton4_Click()
'nuovo
Dim CTRL As Control
Dim FileNum As Integer
Dim UltimoRecord As Integer
Dim NuovoRecord As String, Riga As String
Dim Matricetemp(), Campi
Dim C As Integer, C1 As Integer, R As Integer, R1 As Integer
' richiesta di inserire un nuovo record
If CommandButton4.Caption = "Nuovo" Then
CommandButton4.Caption = "OK/Annulla"
CommandButton1.Enabled = False
CommandButton2.Enabled = False
CommandButton3.Enabled = False
CommandButton5.Enabled = False
For Each CTRL In Controls
If (TypeName(CTRL) = ("TextBox")) Then
CTRL = ""
End If
Next
ComboBox1.ListIndex = -1
Corrente = 0
TextBox1.SetFocus
Else
' richiesta di registrare il nuovo record
CommandButton4.Caption = "Nuovo"
CommandButton1.Enabled = True
CommandButton2.Enabled = True
CommandButton3.Enabled = True
CommandButton5.Enabled = True
If TextBox1.Text = "" Then Exit Sub
'aggiungo la nuova voce alla ComboBox1
ComboBox1.AddItem TextBox1.Text
UltimoRecord = UBound(MatriceDati, 2) + 1
'ridimensiono la matrice aggiungendovi un nuovo record
ReDim Preserve MatriceDati(1 To 6, UltimoRecord)
NuovoRecord = ""
'scrivi il nuovo record nella matrice
For C1 = 1 To 6
NuovoRecord = NuovoRecord & Controls("TextBox" & C1) & "|"
MatriceDati(C1, UltimoRecord) = Controls("textbox" & C1)
Next
NuovoRecord = Left(NuovoRecord, Len(NuovoRecord) - 1)
'accodo il nuovo record al file di testo
FileNum = FreeFile()
Open NomeFile For Append As #FileNum
Print #FileNum, NuovoRecord
Close #FileNum
For Each CTRL In Controls
If (TypeName(CTRL) = ("TextBox")) Then
CTRL = ""
End If
Next
ComboBox1.ListIndex = -1
Corrente = 0
Label7.Caption = UBound(MatriceDati, 2) & " record in archivio"
MsgBox UltimoRecord & " è il nuovo numero di record registrati in " & NomeFile
End If
End Sub

Mi sembra che questo possa essere tutto

Allego il file che ho usato per questo lavoro file da scaricare