Ti piace il sito?
Votalo con un clic sul
banner qui sotto ^_^
Creazione guidata o wizard
Che cos’è una creazione guidata? La risposta è semplice:
è un modo abbastanza elegante per raccogliere un insieme di informazioni,
non solo, ma aiuta anche a risparmiare il prezioso spazio che l’applicativo
occupa sullo schermo.
La realizzazione di una creazione guidata richiede una piccola analisi a
priori, che possiamo riassumere nei seguenti punti:
1)
Definiamo con precisione l’insieme delle informazioni che desideriamo
raccogliere
2) Suddividiamo in modo logico tali informazioni e decidiamo un nome per
identificare ogni raggruppamento
3) Decidiamo in che modo realizzare le pagine e ne creiamo una bozza su
carta o nell’ambiente di sviluppo
4) Passiamo all’implementazione vera e propria
Supponiamo di voler realizzare un’interfaccia grafica di acquisizione
dei dati relativi ad un brano musicale. Prendiamo in considerazione il diffusissimo
formato mp3.
Armiamoci di un po’ di pazienza ed in pochi minuti avremo tutto il
necessario.
Quali dati ci interessano? Sembra logico includere le seguenti informazioni:
nome del file, autore, album, provenienza, durata brano, dimensione file,
percorso file, qualità audio, genere musicale, titolo del brano.
Le informazioni si presentano in modo disomogeneo, quindi facciamo un po’
di ordine, suddividiamo il tutto in piccoli gruppi di dati che siano abbastanza
simili o attinenti tra loro.
Gruppo 1
Nome file, dimensione file, percorso file, provenienza
Gruppo 2
Titolo brano, autore, album, genere musicale
Gruppo 3
Qualità audio, durata brano
Questa suddivisione non vi sembra meglio rispetto al groviglio che avevamo
prima? Sicuramente è meno dispersiva e più facilmente comprensibile.
Ora, diamo un nome ad ogni singolo gruppo: ad esempio il primo potrebbe
essere “File”, il secondo “Discografia” ed il terzo
“Audio”. I nomi sembrano soddisfacenti, ma se non ci piacciono
possiamo cambiarli come ci pare. A questo punto dell’analisi, possiamo
chiederci se valga la pena di inserire qualche altra informazione utile.
Sembrerebbe di no, ma a volte può capitare di aggiungere qualcosa.
Passiamo alla fase successiva: pensiamo a come devono essere realizzate
le pagine.
Quali controlli utilizzare?
Analizziamo la pagina “File”. Per contenere il nome del file
sembra sufficiente una casella di testo, la stessa cosa vale anche per il
percorso e le eventuali note riguardanti la sua provenienza. La dimensione
del file sembra più sensato fornirla direttamente via codice. Possiamo
utilizzare un’etichetta per presentarla. Non è necessario obbligare
l’utente ad inserire le informazioni che possiamo reperire facilmente.
E’ da notare che visualizziamo comunque il dato relativo alla dimensione
del file, questo lo facciamo per far sì che l’utente percepisca
la memorizzazione del dato, anche se non può introdurlo liberamente.
Facciamo una piccola bozza per ogni pagina che analizziamo. Per la pagina
“File” dovremmo avere qualcosa come in figura 1.
Figura 1
La pagina “Discografia“ sarà formata da tre caselle
di testo per la digitazione del titolo del brano, nome dell’autore,
titolo dell’album e da una casella combinata che riporterà
l’elenco dei generi musicali più comuni figura 2.
Figura 2
Infine, per la pagina “Audio” avremo una casella di testo
per la durata del brano ed una casella combinata per la qualità audio
figura 3. Anche per la durata avremmo potuto utilizzare un’etichetta
come per la dimensione del file, questo perché è possibile
ricavare la durata del brano direttamente dal file mp3, ma ciò esula
dallo scopo del testo.
Figura 3
Per aiutare l’utente è bene presentare tutte le informazioni
in ordine di importanza. Trascurare questo fattore, da alcuni considerato
marginale, può incidere negativamente sulla percezione del valore
che l’utente attribuisce al software.
Giunti a questo punto, siamo pronti per affrontare la fase di implementazione.
Come facciamo a scorrere le pagine?
L’approccio base è nascondere quello che non deve essere visto.
Ci serve un contenitore che contenga tutti i controlli relativi ad una pagina.
Visual Basic dispone di diversi contenitori, il più leggero in termini
di memoria è il controllo Frame.
Procediamo nel seguente modo:
1) Apriamo un nuovo
progetto EXE Standard e lo chiamiamo MP3Wizard
2) Inseriamo un nuovo Form, oltre a quello creato in automatico, e lo chiamiamo
frmWizard
3) Inseriamo all’interno di frmWizard un controllo Frame e lo chiamiamo
fmPage
4) Tra le proprietà del frame fmPage selezioniamo Index e le attribuiamo
il valore zero (fmPage.Index=0). Questa operazione serve per creare un matrice
di controlli in modo esplicito. Non abbiate paura, l’utilizzo delle
matrici di controlli non è una cosa affatto difficile.
5) Inseriamo un altro frame all’interno del form e lo chiamiamo di
nuovo fmPage. Ma come, cerchiamo di usare un nome già esistente?
Ebbene si, la matrice di controlli non è altro che un insieme di
controlli aventi lo stesso nome, ma distinguibili tramite la proprietà
Index. Infatti, se provate a guardare la proprietà Index dell’ultimo
frame inserito, vedrete che non è più uguale a zero, ma è
impostata a uno (fmPage.Index=1). Quando viene assegnato un nome ad un controllo,
l’ambiente di sviluppo si preoccupa di vedere se ci sono altri controlli
omonimi dello stesso tipo, se tali sono presenti ed hanno la proprietà
Index impostata, allora il nuovo controllo entra a far parte della matrice
di controlli e la proprietà Index viene aggiornata in automatico.
Ecco spiegato il perché la proprietà Index dell’ultimo
frame inserito aveva un valore diverso.
6) Inseriamo l’ultimo frame e lo chiamiamo di nuovo fmPage e questo
avrà la proprietà Index uguale a due (fmPage.Index=2). Fate
attenzioni a non inserire un frame all’interno dell’altro, ma
ad inserirli tutti all’interno del form frmWizard.
Che cosa abbiamo ottenuto? Abbiamo un form di nome frmWizard ed una matrice
di frame avente tre elementi: fmPage(0), fmPage(1) e fmPage(2). Come avrete
già capito, la matrice di controlli rappresenta le nostre tre pagine:
“File” fmPage(0), “Discografia” fmPage(1), “Audio”
fmPage(2). Dunque, le figure 1, 2 e 3 si riferiscono rispettivamente ai
contenitori fmPage(0), fmPage(1), fmPage(2). Cercate di modificare le proprietà
ed inserire i dovuti all'interno dei frame raggiungendo un risultato simile
alle figure 1, 2 e 3. Avete notato che per riferirmi ad un particolare controllo
della matrice utilizzo la notazione dei vettori? Infatti, questo è
il modo per accedere agli elementi diversi della matrice.
Adesso inseriamo quattro pulsanti all’interno del form e li chiamiamo
cmdPrev, cmdNext, cmdEnd, cmdClose ed impostiamo le loro rispettive proprietà
Caption come segue: “< Indietro”, “Avanti >”,
“Fine”, “Chiudi”. Il risultato dovrebbe essere simile
alla figura 4.
Figura 4
E’ giunto il momento di scrivere il codice. Come prima cosa abbiamo
bisogno di una variabile che tenga traccia della pagina corrente. La dichiariamo
all’interno del form e sarà di tipo intero.
Option Explicit
'Variabile privata che specifica la pagina corrente
Dim pCurrentPage As Integer
Questa variabile ha una visibilità privata, anche se non esplicitamente
specificato. Come facciamo a cambiare dall’esterno il valore di una
variabile privata? Semplice, utilizziamo le proprietà. Creiamo ora,
all’interno del form, una proprietà in lettura–scrittura
che ci permetta di modificare e leggere il valore della variabile.
'********************************************************
'Proprietà in lettura [Get]
'Restituisce un intero che specifica la pagina corrente
'********************************************************
Public Property Get CurrentPage() As Integer
CurrentPage = pCurrentPage
End Property
Adesso inseriamo la stessa proprietà, questa volta in scrittura,
che ci permette di impostare il valore di pCurrentPage.
'********************************************************
'Proprietà in scrittura [Let]
'Imposta la pagina corrente
'********************************************************
Public Property Let CurrentPage(ByVal vNewValue As Integer)
'Se il nuovo valore di pagina non è compreso tra zero ed
'il numero dell'ultima pagina, si esce dalla proprietà
If vNewValue > Me.LastPageNumber() Or vNewValue < 0 Then
Exit Property
End If
'Se il nuovo valore rappresenta l'ultima pagina
If vNewValue = Me.LastPageNumber() Then
'Disabilito il pulsante "Avanti >"
cmdNext.Enabled = False
'Attivo il pulsante "Fine"
cmdEnd.Enabled = True
Else
'Abilito il pulsante "Avanti"
cmdNext.Enabled = True
'Disabilito il pulsante "Fine"
cmdEnd.Enabled = False
End If
'Se il nuovo valore rappresenta la prima pagina
If vNewValue = 0 Then
'Disabilito il pulsante "Indietro"
cmdPrev.Enabled = False
Else
'Abilito il pulsante "Indietro"
cmdPrev.Enabled = True
End If
If vNewValue = pCurrentPage Then
Exit Property
End If
'nascondo la pagina precedente
Me.fmPage(pCurrentPage).Visible = False
'Assegno il nuovo valore della pagina
pCurrentPage = vNewValue
'Visualizzo la pagina corrente
Me.fmPage(pCurrentPage).Visible = True
End Property
Come avrete notato, la proprietà in scrittura è un po’
più articolata, perché non imposta solamente il valore della
variabile, ma gestisce anche l’abilitazione e disabilitazione dei
pulsanti presenti all’interno del form, oltre a rendere visibile la
pagina corretta. Niente paura, la spulciamo per benino dopo aver introdotto
un’ultima proprietà.
Adesso abbiamo la proprietà CurrentPage che ci permette di leggere
e scrivere il valore della pagina corrente. Quali sono i valori corretti
che possiamo attribuire alla proprietà? Ci basiamo su un principio
molto semplice: la matrice di frame fmPage, che rappresenta le nostre pagine,
va da un valore minimo zero ad un valore massimo rappresentato dalla proprietà
Count-1 della matrice stessa. In Visual Basic ogni matrice di controlli
dispone della proprietà Count che specifica il numero di elementi
che compongono la matrice. Scriviamo la proprietà LastPageNumber
che ci restituisce, non il numero di elementi, bensì l’indice
superiore della matrice di controlli, che nel nostro caso specifica proprio
l’ultima pagina del Wizard.
'********************************************************
'Restituisce l'indice superiore della matrice di controlli fmPage
'********************************************************
Public Property Get LastPageNumber() As Integer
LastPageNumber = fmPage.Count - 1
End Property
Ritorniamo alla proprietà in scrittura (Let) CurrentPage. Che cosa
fa?
La prima istruzione di controllo if esegue una verifica del nuovo valore
attribuito alla proprietà. Se il nuovo valore assegnato non è
compreso tra l’indice inferiore e superiore della matrice di controlli
fmPage, allora non fa nulla, semplicemente esce dal corpo della proprietà.
L’istruzione if successiva controlla se il nuovo valore assegnato
è relativo all’ultima pagina, ovvero se è uguale a LastPageNumber.
Se è così, allora disabilita il pulsante “Avanti”
e abilita il pulsante “Fine”, in caso contrario fa il viceversa,
abilita il pulsante “Avanti” e disabilita il pulsante “Fine”.
L’ultimo if controlla se il nuovo valore è relativo alla prima
pagina, ovvero se è uguale a zero. Se è vero disabilita il
pulsante “Indietro”, altrimenti lo rende abilitato.
Le ultime due istruzioni if descritte ci permettono di avere una corretta
gestione dei pulsanti. Sarebbe inutile avere abilitato il pulsante “Indietro”
quando si è alla prima pagina e lo stesso vale per il pulsante “Avanti”
quando si è giunti all’ultima. Per il pulsante “Fine”
abbiamo supposto che sia disponibile solo alla fine delle pagine.
Siamo giunti all’ultimo if che controlla se la nuova pagina impostata
è uguale a quella corrente. Se così è non dobbiamo
più fare nulla e usciamo dalla proprietà
Dopo le istruzioni if, finalmente possiamo nascondere la pagina corrente
(Me.fmPage(pCurrentPage).Visible = False ), impostare la nuova pagina corrente
(pCurrentPage = vNewValue) e visualizzarla (fmPage(vNewValue).Visible =
True).
Tante parole per fare poche cose, ma vale la pena di capire ogni singolo
perché ^_^ Non disperate non manca molto.
Fin a questo punto ci siamo limitati a scrivere le proprietà e non
abbiamo ancora intercettato nessun click dell’utente.
Vediamo ora come le proprietà ci facilitano la vita.
Quando l’utente fa clic sul pulsante “Indietro” (cmdPrev)
dobbiamo ritornare alla pagina precedente. Ecco il codice dell’evento
click:
'********************************************************
'L'utente ha fatto clic sul pulsante "Indietro"
'********************************************************
Private Sub cmdPrev_Click()
'Se la pagina corrente non è la prima (zero),
'allora imposto la pagina precedente
If Me.CurrentPage() - 1 >= 0 Then
Me.CurrentPage() = Me.CurrentPage() - 1
End If
End Sub
Avete visto che utilizzo la parola chiave Me per riferirmi alle proprietà
del form? Questa è una scelta che permette di rendere il codice più
leggibile. Notate, che la proprietà CurrentPage è utilizzata
sia in lettura, per ottenere il valore (Valore=Me.CurrentPage()), sia in
scrittura, per impostare il valore (Me.CurrentPage() =Numero). E’
da sottolineare che l’abilitazione dei controlli e la visualizzazione
della pagina corretta sono gestite direttamente dalla proprietà CurrentPage,
quindi non dobbiamo fare altro che assegnare un valore corretto a questa
proprietà.
Per il pulsante “Avanti” (cmdNext) avremo una cosa simile.
'********************************************************
'L'utente ha fatto clic sul pulsante "Avanti"
'********************************************************
Private Sub cmdNext_Click()
'Se la pagina corrente non è l'ultima,
'allora imposto la pagina successiva
If Me.CurrentPage() + 1 <= Me.LastPageNumber() Then
Me.CurrentPage() = Me.CurrentPage() + 1
End If
End Sub
Di nuovo come prima, ma questa volta la proprietà CurrentPage è
incrementata di uno.
I pulsanti “Fine” (cmdEnd) e “Chiudi” (cmdClose)
che cosa devono fare? Con il pulsante “Fine” dobbiamo chiudere
il wizard in modo che l’esecuzione del programma prosegua. Per il
pulsante “Chiudi” faremo la stessa cosa con una piccola differenza:
da codice dobbiamo distinguere la situazione in cui l’utente chiude
il wizard, invalidando di fatto tutti i dati raccolti. Per fare questo abbiamo
bisogno di una semplice proprietà di sola lettura che chiamiamo IsCancel
e che ci restituirà un booleano True se l’utente ha chiuso
il Wizard, False altrimenti. Dobbiamo dichiarare prima di tutto una variabile
privata come segue:
Dim pIsCancel As Boolean
Ed aggiungere la proprietà che ne permette la lettura. Ecco il
codice di IsCancel:
Public Property Get IsCancel() As Boolean
'restituisce il valore della variabile flag
'che indica se l'utente ha chiuso il wizard
IsCancel = pIsCancel
End Property
Ritornando agli eventi click dei due pulsanti “Fine” e “Chiudi”
abbiamo il seguente codice:
'pulsante "Fine"
Private Sub cmdEnd_Click()
'Nascondo il form
Me.Hide
End Sub
'pulsante "Chiudi"
Private Sub cmdClose_Click()
'Nascondo il form
Me.Visible = False
'se l'utente ha annullato l'acquisizione
'dei dati imposto la variabile flag che
'tiene traccia di questa eventualità.
'esternamente potrò accedere a tale variabile
'utilizzando la proprietà IsCancel
pIsCancel = True
End Sub
Siamo a buon punto, ora ci mancano le inizializzazioni delle proprietà
e finalmente il wizard sarà finito. Possiamo prevedere una procedura
pubblica che permetta di inizializzare, anche dall’esterno, tutti
i controlli del nostro wizard.
Il codice potrebbe essere questo:
Public Sub InitControls()
Dim Ctrl As Control
On Error Resume Next
'imposto alcune proprietà dei controlli
For Each Ctrl In Controls
'se il controllo è una TextBox o ComboBox
If TypeOf Ctrl Is TextBox Or TypeOf Ctrl Is ComboBox Then
'Imposto il colori di sfondo e primo piano
Ctrl.BackColor = &HE0E0E0
Ctrl.ForeColor = &H1B61A0
End If
'Imposto il nome del font e le dimensioni'per tutti i controlli presenti nel form
Ctrl.FontName = "Verdana"
Ctrl.FontSize = 8
Next Ctrl
'Aggiungo alla casella combinata tre elementi
cmbQualità.AddItem "Pessima"
cmbQualità.AddItem "Buona"
cmbQualità.AddItem "Ottima"
'Imposto l'elemento che deve essere visualizzato
cmbQualità.ListIndex = 1
End Sub
All’interno dell’evento load del nostro wizard inseriamo quest’altro
codice:
Private Sub Form_Load()
'la variabile i è usata come indice del ciclo for,
'mentre n regola il numero di iterazioni
Dim i As Integer, n As Integer
'Posizione dell'ultima pagina
n = Me.LastPageNumber()
'Nascondo tutte le pagine tranne la prima (posizione 0)
For i = 1 To n
fmPage(i).Visible = False
Next i
'La prima pagina visualizzata è
'la prima della matrice di controlli
'in qesto modo vengono impostati
'in modo corretto anche i pulsanti
Me.CurrentPage = 0
'inizializzo la variabile flag che
'traccia l'eventuale chiusura del wizard
pIsCancel = False
'inizializzo alcune proprietà dei
'controlli presenti nel form
Call InitControls
End Sub
Ecco fatto! Non ci manca nulla per poter utilizzare il lavoro svolto finora.
Notate che il procedimento descritto permette di creare un wizard con una
struttura altamente riutilizzabile. Infatti, tutte le proprietà introdotte
sono assolutamente indipendenti dal tipo di informazione o numero di controlli
presenti nelle singole pagine. Esclusa la procedura InitControls potrete
mantenere lo scheletro del codice per implementare una creazione guidata
con un numero di pagine qualsiasi, senza cambiare una riga di codice già
scritto!
Infine, come ultimo argomento dell’articolo vediamo quanto è
facile utilizzare il nostro wizard. Vi ricordate che il nostro progetto
ha un altro form, quello che è stato creato in automatico (Form1)?
Cambiamo il suo nome in frmMain e lo impostiamo come oggetto di avvio all’interno
delle proprietà di progetto (Progetto -> Proprietà ->
Generale -> Oggetto di avvio). Inseriamo al suo interno un pulsante (cmdLoadWizard)
impostando la sua etichetta a “Visualizza Wizard” ed un paio
di caselle di testo (Text1 e Text2). Inseriamo il seguente codice nell’evento
click di questo pulsante:
Private Sub cmdLoadWizard_Click()
Dim frmMyWizard As New frmWizard
'ci assicuriamo che sia visibile la prima pagina
'del wizard quando questo verrà visualizzato
frmMyWizard.CurrentPage = 0
'visualizziamo il wizard come modale rispetto
'al form chiamante
frmMyWizard.Show vbModal, Me
'se l'utente non ha chiuso il wizard
'allora assegno un paio di valori alle
'caselle di testo Text1 e Text2
If Not frmMyWizard.IsCancel Then
Text1.Text = frmMyWizard.txtPercorso
Text2.Text = frmMyWizard.txtNome
Else
Text1.Text = ""
Text2.Text = ""
End If
'scarico il wizard
Unload frmMyWizard
Set frmMyWizard = Nothing
End Sub
Quest’ultimo codice non fa altro che visualizzare il nostro bellissimo
wizard e quando l’utente avrà finito di compilare i campi potremo
vedere il risultato all’interno delle caselle di testo Text1 e Text2.
Un’importante osservazione! Per accedere ai valori inseriti dall’utente
possiamo riferirci direttamente ai controlli di cui il wizard è composto
(es tutte le TextBox), questa pratica tuttavia, non è il modo corretto
per gestire il flusso informativo tra le parti componenti il nostro applicativo.
In realtà avremmo dovuto creare delle apposite proprietà all’interno
di frmWizard e tramite queste accedere ai valori desiderati, ma visto il
volume dell’articolo ho preferito tralasciare questo aspetto. Con
la speranza di essere stato chiaro, non certo conciso ^_^, vi lascio ai
vostri esperimenti ricordandovi che potete trovare i sorgenti dell'intero
progetto qui.