News
Articoli
Download
Tips & Tricks
Link
Guestbook
 
 
 
 
Copyright© 2004
Ivanov Dmitrij
Evghegnevich
e-mail

Risoluzione consigliata
1024 x 768
 
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.