Corso di visual basic

Materie:Altro
Categoria:Informatica

Voto:

2 (2)
Download:1430
Data:14.02.2007
Numero di pagine:89
Formato di file:.doc (Microsoft Word)
Download   Anteprima
corso-visual-basic_1.zip (Dimensione: 268.06 Kb)
trucheck.it_corso-di-visual-basic.doc     575.5 Kb
readme.txt     59 Bytes


Testo

Corso di visual basic
Lezione1
Introduzione a Visual Basic
Argomenti correlati:
Storia del Basic
Note introduttive sul linguaggio
IDE
Form
Progetto
Label
CommandButton
TextBox
EXE
Casella degli strumenti
Finestra proprietà
Proprietà
Metodi
Eventi
Sezione Generale
Istruzioni
Routine
Variabili
Dichiarazioni
Tipi di dati
Byte
Integer
Long
String
Dichiarazione esplicita
Lezione2
Le variabili in Visual Basic
Argomenti correlati:
Area di visibilità
Dichiarazioni a livello di modulo
Variabili locali
nota - finestra Immediata
Dichiarazioni Public
Dichiarazioni Private
Dichiarazioni Static
nota - Attribuzioni dei nomi alle variabili
Lezione3
Le routine in Visual Basic
Argomenti correlati:
Definizione di routine
Le dichiarazioni Sub, Public, Private
La chiusura di una routine: End Sub
nota - l'uso del simbolo " ' "
Richiamare una routine
I parametri
L'utilizzo di Call
Lezione4
Le funzioni in Visual Basic
Argomenti correlati:
Definizione di funzione
Differenze tra funzioni e routine
L'utilizzo delle funzioni
L'istruzione Function
Richiamare una funzione
L'istruzione Call
Le variabili nelle funzioni
I metodi ByVal e ByRef
L'istruzione Static
Lezione5
Il primo programma in Visual Basic
Argomenti correlati:
Il controllo Label
La proprietà Name
La proprietà Caption
L'istruzione Unload
Sviluppo della prima applicazione
Esempio scaricabile
Lezione6
convenzioni, algebra e variabili Booleane
Argomenti correlati:
Convenzioni di scrittura
Convenzioni di strutturazione del codice
Istruzioni nidificate
George Boole
Algebra Booleana: introduzione
Operatore di congiunzione: And
Operatore di negazione: Not
Operatore di disgiunzione: Or
Operatore di disgiunzione esclusiva XOr
Operatore di equivalenza: Eqv
Lezione7
Utilizzo delle variabili Booleane
Argomenti correlati:
Costrutto If...Then
Esempi di Algebra Booleana
Blocchi annidati
Else
ElseIf
End If
Lezione8
Altro ancora sulle variabili Booleane, Bit e Byte
Argomenti correlati:
True
False
Il valore numerico delle variabili booleane
Conversione tra valori numerici e variabili booleane
Definizione di bit
Definizione di byte
Confronto bit a bit
Lezione9
Studiare un'applicazione professionale:
il Blocco Note di Windows
Argomenti correlati:
Utilizzo comune del confronto bit a bit
Determinare gli attributi dei files
Strutturare una ricerca dei files nascosti
Riprodurre il Blocco Note di Windows (parte prima)
Lezione10
Studiare un'applicazione professionale:
il Blocco Note di Windows - Seconda parte
Argomenti correlati:
Riprodurre il Blocco Note di Windows (parte seconda)
Aprire un file con l'istruzione Open
Chiudere un file con l'istruzione Close
Intercettare le modifiche in una TextBox
La funzione MessageBox
Le combinazioni di pulsanti in una finestra
di messaggio
Lezione11
Studiare un'applicazione professionale:
il Blocco Note di Windows - Terza parte
Argomenti correlati:
Riprodurre il Blocco Note di Windows (parte terza)
La chiusura di una finestra
Utilizzare le InputBox
Il controllo CommonDialog
Le barre di scorrimento
Lezione12
Nozioni avanzate sulle variabili
Argomenti correlati:
Date
Single
Double
Currency
Object
Variant
Stringhe a lunghezza fissa
Tipi definiti dall'utente
Sottotipi di variabili
Istruzione DefTipo
Tabella dei caratteri di dicharazione
Tabella delle funzioni di conversione
Lezione13
Estensione della logica binaria
Argomenti correlati:
Organizzazione di bit
Il bit di segno
Rappresentazione binaria di un numero negativo
Vettori di variabili
Limite superiore di un vettore
L'istruzione Option Base
Limite inferiore di un vettore
Lezione14
Le matrici, il ciclo For...Next ed il campo minato passo per passo
Argomenti correlati:
Matrici
Matrici dinamiche
Matrici di controlli
Istruzione ReDim
Ciclo For...Next
Riprodurre il Campo Minato di Windows (parte prima)
Lezione15
Campo Minato: il controllo Timer,
le funzioni Rnd e Int(), le
istruzioni Randomize e GoTo e la
proprietà Tag
Argomenti correlati:
Riprodurre il Campo Minato di Windows (seconda prima)
Il controllo Timer
La funzione Rnd
La funzione Int()
L'istruzione GoTo
L'istruzione Randomize
La proprietà Tag
Lezione16
Campo Minato: algoritmi, diagrammi di flusso,
l'operatore Mod, la funzione IIf, il parametro Index
Argomenti correlati:
Riprodurre il Campo Minato di Windows (terza prima)
Gli algoritmi e i diagrammi di flusso
L'operatore Mod
La funzione IIf
Il parametro Index
Approfondimento sul controllo
'Barra di avanzamento'
Argomenti correlati:
Barra di avanzamento
Proprietà Max e Min
Determinazione della proprietà Value
Proprietà Scrolling
Esercizi svolti
Argomenti correlati:
Funzione MsgBox
If...End If
Funzione UBound
Matrici di Variabili
Lezione17
Campo Minato: gli ultimi accorgimenti
Argomenti correlati:
Funzione Len
Ultime modifiche all'applicazione
Progetto di esempio in formato compresso
Prima lezione - Introduzione a Visual Basic
Premessa
Questa è la prima parte di un corso in cui saranno spiegati i fondamenti del linguaggio Visual Basic: si partirà dalle nozioni di base, dando per scontato che il lettore non abbia alcuna conoscenza di programmazione, e si procederà man mano approfondendo argomenti più complessi. I lettori che hanno già qualche conoscenza di programmazione possono tranquillamente saltare le prime parti del corso. Si consiglia di seguire questo corso avendo a disposizione l'help fornito dall'MSDN, per avere informazioni più dettagliate sugli argomenti affrontati.
Innanzitutto è bene spiegare cosa si intende per Visual Basic: con questo termine si può intendere sia il programma che consente di sviluppare applicazioni per Windows, sia il linguaggio di programmazione in cui tali applicazioni vengono scritte. Concentriamoci per ora sulla seconda accezione del termine: il Visual Basic è chiamato così perché deriva dal linguaggio BASIC (Beginners All-Purpose Symbolic Instruction Code), un tipo di linguaggio semplice e molto diffuso che era implementato, ad es., nei mitici Commodore 64; nel corso dell'evoluzione del BASIC si sono aggiunte molte istruzioni e sono cambiate diverse caratteristiche, la principale delle quali è indicata dal termine "visual": come vedremo tra poco, infatti, per progettare l'interfaccia grafica di un'applicazione si ha a disposizione un ambiente grafico in cui si possono utilizzare numerosi componenti di vario tipo, come i pulsanti, le caselle di testo ecc. In questo modo è possibile creare l'interfaccia grafica in modo semplice e veloce, senza dover ogni volta scrivere le istruzioni necessarie per ordinare al computer di creare le finestre di cui abbiamo bisogno, come invece accadrebbe se utilizzassimo un linguaggio non "visuale". Per rendersene conto, cominciamo a vedere come si presenta l'ambiente di progettazione di Visual Basic 6.0, cioè il programma che avviate cliccando sul menu Start-Programmi-Microsoft Visual Basic 6.0: quello che vi si presenta è l'IDE (Integrated Development Environment, Ambiente di Sviluppo Integrato), e al suo interno ci sono diverse finestre: scegliendo di creare unnuovo progetto "exe standard" (Fig.1)
Fig.1: Apertura di un nuovo progetto "EXE Standard"
al centro compare una finestra con un "form": i form rappresentano le finestre che saranno visualizzate dalla vostra applicazione; a sinistra compare la "casella degli strumenti" (Fig.2), con una serie di icone ciascuna delle quali rappresenta un particolare oggetto inseribile sul form: ad es., ci sono le label (etichette), le textbox (caselle di testo), i commandbutton (pulsanti), ecc.; selezionando un'icona è possibile disegnare direttamente sul form l'oggetto ad essa associato. A destra compaiono la finestra di gestione dei progetti e la finestra delle proprietà(Fig.3): la prima fornisce l'insieme dei file che compongono il progetto di applicazione che state creando, mentre la seconda visualizza le proprietà dell'oggetto selezionato sul form: all'inizio, dato che il form è vuoto, la finestra delle proprietà elencherà proprio quelle del form; inserendo un oggetto qualunque sul form, verranno visualizzate le proprietà dell'oggetto appena inserito.
Fig.2: Casella degli strumenti utilizzabili in Visual Basic. Quelli visibili all'apertura dell'applicazione sono i controlli standard (o di default). Per visualizzare o gestire l'aggiunta/rimozione di altri controlli cliccare col pulsante destro del mouse sulla finestra "Generale".
Fig.3: La finestra delle Proprietà visualizza le proprietà dell'oggetto selezionato sul form. A seconda dell'oggetto selezionato, le proprietà visualizzate nella tabella saranno differenti. Per modificare una proprietà fare clic sulla casella corrispondente alla seconda colonna. A questo punto può essere richiesto di immettere manualmente dati come numeri, lettere o simboli, oppure scegliere tra più possibilità date o ancora potrà comparire una finestra di dialogo collegata all'argomento.
Ogni oggetto, o controllo, presente nella casella degli strumenti è dotato di un insieme di proprietà, metodi ed eventi.
Proprietà
le proprietà rappresentano gli attributi che definiscono l'aspetto e varie funzionalità di ogni controllo; ad es., la proprietà Name indica il nome con cui quel controllo è identificato all'interno del codice; le proprietà Height e Width indicano l'altezza e la larghezza del controllo, ecc. Molte proprietà sono comuni a diversi oggetti (ad es. qualunque controllo dispone della proprietà Name), altre invece sono specifiche di un controllo particolare (ad es., la proprietà Interval è disponibile solo per il controllo Timer). Solitamente le proprietà possono essere lette e anche impostate, ovvero è possibile sia leggere il valore della proprietà, sia assegnare ad essa un nuovo valore: in tal caso si dice che la proprietà è di lettura e scrittura; tuttavia esistono anche proprietà di sola lettura, alle quali non è possibile assegnare un nuovo valore, e di sola scrittura.
Metodi
un metodo è un'azione che l'oggetto può eseguire: ad es., l'oggetto form dispone dei metodi Show e Hide, che rispettivamente mostrano oppure nascondono il form all'utente; il controllo picturebox, invece, dispone del metodo Pset, che serve ad assegnare un certo colore ad un punto del picturebox. Come per le proprietà, alcuni metodi sono comuni a diversi controlli, altri invece sono specifici di un controllo particolare.
Eventi
gli eventi sono, come dice il nome, "situazioni" generate dal controllo quando si verificano certe condizioni; solitamente, ma non necessariamente, gli eventi si producono in conseguenza di un'azione dell'utente: per es., quando l'utente fa clic su un pulsante, il controllo che identifica quel pulsante genera un evento click; un esempio di evento non generato dall'utente è l'evento Timer del controllo omonimo, che viene generato dopo un certo numero di millisecondi specificato dalla proprietà Interval.
Cliccando due volte di seguito su un controllo contenuto nel form, o sul form stesso, si apre un'altra finestra, l'editor del codice, in cui il programmatore può scrivere le istruzioni necessarie a far compiere al computer determinate operazioni. L'editor del codice si presenta con un'area bianca in cui va scritto, appunto, il codice, e con due caselle a discesa poste nella parte superiore della finestra (Fig.4): la casella di sinistra fornisce l'elenco degli oggetti disponibili sul form selezionato, oltre alla sezione Generale, che serve per le dichiarazioni, come vedremo oltre; la casella di destra fornisce invece un elenco degli eventi associati al controllo selezionato nella casella di sinistra.
Fig.4: Caesella destra e sinistra
Ad es., selezionando nella casella di sinistra l'oggetto form e nella casella di destra l'evento click, verrà visualizzata nell'area del codice la sezione relativa all'evento click dell'oggetto form, in cui il programmatore può scrivere le istruzioni che devono essere eseguite quando l'utente fa clic sul form. Per accedere alle proprietà e ai metodi dei vari controlli, bisogna scrivere il nome del controllo seguito da un punto "." e dal nome del metodo o della proprietà (ad es. "Form1.Show" oppure "Command1.Caption").
Passiamo ora alle fondamenta del linguaggio Visual Basic. Come ogni linguaggio di programmazione, il Visual Basic è costituito da un insieme di parole chiave, funzioni, istruzioni che seguendo determinate regole sintattiche permettono al programmatore di impartire "ordini" al computer al fine di produrre un certo risultato. Prima di vedere nello specifico quali sono le particolarità del linguaggio Visual Basic, è necessaria un'introduzione sulle principali strutture che caratterizzano un qualunque linguaggio di programmazione:
Istruzioni
un'istruzione è un insieme di una o più parole che indicano al computer di eseguire una particolare operazione: ad es., l'istruzione Print permette di scrivere o mostrare una certa informazione sullo schermo, o anche su un file, a seconda del contesto in cui si usa.
Routine
una routine è un insieme di una o più istruzioni, eseguite in risposta ad un'azione dell'utente, come ad es. la pressione di un tasto, oppure in seguito ad una "chiamata" della routine da parte del programmatore.
Variabili
le variabili sono dei "contenitori" alle quali è associato un certo valore (non necessariamente numerico); ad es., se la variabile di nome messaggio contiene il valore "questo è un messaggio di prova", utilizzando l'istruzione Print seguita dal nome della variabile (cioè scrivendo: print messaggio) potremo vedere visualizzata, ad es. su una finestra, la frase "questo è un messaggio di prova".
Tornando al Visual Basic, cominciamo col vedere come si dichiarano le variabili, ovvero come si fa a ordinare al computer di utilizzare una certa variabile e di assegnare a quest'ultima un certo valore; la sintassi per dichiarare una variabile in Visual Basic è la seguente:
Dim NomeVariabile As TipoVariabile
Ad esempio:
Dim Pippo As Integer
mentre per assegnarle un valore bisogna scrivere il nome della variabile, il simbolo "=" e il valore da assegnare (es.: pippo=10). Il nome della variabile è a completa discrezione del programmatore, tuttavia bisogna osservare qualche restrizione: ad es., il nome non può essere più lungo di 255 caratteri, non può contenere punti o spazi, deve iniziare con una lettera, non può essere uguale a una delle parole-chiave utilizzate da Visual Basic Ad es., non si può fare:
Dim dim As Integer
perché "dim" è una parola riservata per la dichiarazione delle variabili. Il tipo della variabile indica il tipo di valore che è possibile assegnare alla variabile, ed è possibile scegliere tra diverse alternative; i tipi principali supportati da Visual Basic sono:
Tipo di dati
Spazio occupato in memoria
Intervallo di valori
Byte
1 byte
0/255
Integer
2 byte
-32768/+32767
Long
4 byte
-2147483648 / +2147483647
String
variabile
da 0 a circa 65000 caratteri
Ce ne sono anche altri, ma per ora bastano questi; come si può notare, i tipi di variabili si distinguono sia per la quantità di memoria che richiedono, sia per l'intervallo di valori che possono assumere; i primi tre tipi servono per memorizzare valori numerici, mentre il quarto memorizza delle stringhe, ovvero delle sequenze di caratteri (parole o frasi, come nell'esempio della variabile messaggio, in cui messaggio è una variabile di tipo string). La dichiarazione delle variabili, in Visual Basic, non è obbligatoria: quando Visual Basic trova un nome di variabile non dichiarato, crea automaticamente una variabile con quel nome (è ciò che si chiama dichiarazione implicita); questa può sembrare una bella comodità, ma in realtà è più una fonte di errori che altro: basta infatti un piccolo errore nel digitare il nome di una variabile, e Visual Basic penserà NON che la variabile è sempre la stessa ma con un nome sbagliato, BENSÌ che si tratta di un'altra variabile, che verrà automaticamente creata. Così, se per es. scrivo:
Dim Pippo As Integer
Pippo = 10
Pipo = 5
dove per errore nella terza riga ho scritto "pipo" anziché "pippo", non avrò una sola variabile (pippo) che contiene il valore 5, ma DUE variabili (Pippo e Pipo) che contengono rispettivamente i valori 10 e 5. Per evitare questo tipo di errori, è possibile rendere obbligatoria la dichiarazione esplicita di TUTTE le variabili: per fare ciò, bisogna andare sul menù strumenti, scegliere opzioni, e selezionare la casella "dichiarazione di variabili obbligatoria": nella sezione "generale" del nostro form sarà automaticamente aggiunta l'istruzione:
Option Explicit
che per l'appunto informa visual basic del fatto che la dichiarazione esplicita è obbligatoria. In questo modo Visual Basic genererà un errore perché riconosce che la variabile "Pipo" non è stata dichiarata.
table width=100%>
Seconda lezione - le variabili in Visual Basic
Una cosa importante da tenere presente a proposito delle variabili è la loro area di visibilità: ciò deriva dal fatto che una variabile può essere dichiarata in punti diversi del codice, e cioè nella sezione dichiarazioni di un form oppure all'interno di una routine; nel primo caso, la variabile sarà visibile, e quindi potrà essere utilizzata, in tutto il form (si dice che è una variabile dichiarata a livello di modulo); nel secondo caso la variabile sarà visibile soltanto all'interno della routine in cui è dichiarata (si dice che è una variabile locale). Per capire bene la differenza proviamo ad aprire un nuovo progetto e a scrivere, nella sezione dichiarazioni del form:
Dim VarGen As Integer
Public Sub Prova()
Dim VarLoc As Integer
VarGen=1
VarLoc=1
End Sub
e, nella routine Load del form:
Private Sub Form_Load()
Prova
VarGen=2
VarLoc=2
Debug.Print VarGen, VarLoc
End Sub
Nota: il comando Debug.Print
viene utilizzato per visualizzare
il valore delle due variabili nella
finestra Immediata
In parole povere, abbiamo dichiarato una variabile di nome VarGen visibile in tutto il form, e una variabile di nome VarLoc visibile solo nella routine di nome Prova: all'interno di questa routine alle due variabili viene assegnato il valore 1, mentre quando il form viene caricato si verifica l'evento Load, durante il quale vengono eseguite le istruzioni che abbiamo appena inserito: dapprima viene eseguita la routine prova con le sue due istruzioni, dopodichè alle due variabili viene assegnato il valore 2; infine il valore delle due variabili viene visualizzato nella finestra "Immediata", di cui si parlerà più avanti. Per visualizzare la finestra basta andare sul menù "Visualizza" e scegliere la voce "Finestra Immediata".
Ora, proviamo a mandare in esecuzione il nostro progetto andando sul menu Esegui e scegliendo Avvia, oppure premendo il tasto F5: quello che succederà è l'interruzione del programma con la visualizzazione del seguente messaggio di errore:
variabile non definita
dove la variabile è quella evidenziata, cioè la variabile VarLoc. Questo succede proprio perché all'esterno della routine Prova la variabile locale VarLoc non esiste: infatti le variabili locali vengono create all'inizio della routine in cui sono dichiarate, e automaticamente distrutte quando la routine finisce di essere eseguita. La variabile VarGen, invece, è visibile sia nell'evento load del form, sia nella routine prova, proprio perché è stata dichiarata nella sezione delle dichiarazioni generali del form.
Faccio notare che se si prova a eliminare l'istruzione Option Explicit che, se avete seguito il mio suggerimento (v. lezione precente), dovrebbe essere inserita automaticamente, quando il progetto viene avviato non si verifica alcun errore e nella finestra immediata viene stampata la riga:
2 2
Questo succede perché senza l'istruzione Option Explicit durante l'esecuzione vengono create DUE variabili VarLoc: una dichiarata esplicitamente nella routine Prova e distrutta alla fine della routine, l'altra dichiarata implicitamente nell'evento load del form e a cui viene assegnato il valore 2, che viene poi correttamente visualizzato nella finestra immediata.
Dato che in una applicazione possono esserci ben più di un unico form, ci si può chiedere se le variabili dichiarate a livello di modulo in un certo form siano visibili anche negli altri form: la risposta è positiva solo se nella dichiarazione delle variabili si usa la parola chiave Public, ad es.:
Public VarGen As Integer
questa parola chiave specifica appunto che la variabile dichiarata è pubblica, cioè è visibile in tutta l'applicazione a cui appartiene il form in cui è dichiarata; utilizzando invece la parola chiave Private o la classica Dim, la variabile sarà privata e quindi visibile solo nel form in cui è dichiarata. Prima ho detto che le variabili locali vengono create all'inizio della routine in cui sono dichiarate e distrutte alla fine: da ciò deriva che se la stessa routine viene richiamata più volte, la variabile viene creata e distrutta altrettante volte, ed ogni volta la variabile creata non ha alcuna connessione con la variabile omonima creata la volta precedente. Se ad esempio modifichiamo la nostra routine Prova in questo modo:
Public Sub Prova ()
Dim VarLoc As Integer
VarLoc=VarLoc + 1
Debug.Print VarLoc
End Sub
E nell'evento Load scriviamo:
Private Sub Form_Load()
Prova
Prova
End Sub
e proviamo ad avviare il progetto, vedremo che nella finestra immediata viene scritto:
1 1
e non, come qualcuno si potrebbe aspettare:
1 2
Se si desidera che una variabile locale conservi il proprio valore tra una chiamata e l'altra della routine, bisogna dichiararla con la parola chiave Static:
Public Sub Prova()
Static VarLoc As Integer
VarLoc = VarLoc + 1
Debug.Print VarLoc
End Sub
Se proviamo ancora ad eseguire il progetto, vedremo che questa volta nella finestra Immediata appare scritto:
1 2
La variabile VarLoc viene quindi creata solo una volta (la prima volta che la routine viene eseguita) e viene distrutta solo quando chiudiamo l'applicazione (cliccando ovviamente sulla X in alto a destra nel form, come per qualunque altra finestra); tuttavia questa variabile è ancora una variabile locale, quindi non è utilizzabile all'esterno della routine. Il motivo per cui VarLoc assume il valore 1 dopo l'istruzione:
Varloc = Varloc + 1
è presto detto: in Visual Basic ogni variabile numerica viene automaticamente inizializzata al valore 0 quando viene creata; le variabili stringa, invece, vengono inizializzate con una stringa vuota, ossia con zero caratteri.
Nota: Nel caso in cui si assegnino nomi composti agli elementi del codice la cui denominazione è arbitraria (variabili, costanti, procedure, funzioni), è buona norma mantenere la prima lettera maiuscola di ciascun nome, in modo da operare una separazione intuitiva. Nel corso di questa lezione ad esempio si è trattata la variabile VarLoc il cui nome deriva dall'unione dei nomi Variabile Locale. Lo stesso vale per VarGen, unione di Variabile e Generale
Terza lezione - le routine in Visual Basic
Dopo aver visto in dettaglio le variabili, vediamo meglio le routine. Innanzitutto bisogna dire che le routine, come le variabili, devono essere dichiarate: la dichiarazione avviene specificando la parola chiave Sub (in seguito vedremo che si possono utilizzare anche altre parole chiave) seguita dal nome della routine e da due parentesi tonde, la prima aperta e la seconda chiusa (tra poco vedremo cosa si può mettere all'interno delle parentesi). Ad esempio:
La parola Sub può essere preceduta dalle parole chiave Private o Public (NON da Dim: quest'ultima serve solo per le variabili), a seconda che la routine debba essere visibile solo nel form corrente o in tutto il progetto. Per inserire una routine è talvolta più comodo andare sul menù strumenti e scegliere inserisci routine: nella casella di testo in cui appare il cursore scrivete il nome della routine (le altre opzioni per ora lasciatele così come sono) e premete OK. La dichiarazione delle routine serve a indicare dove INIZIA la routine: per indicare dove FINISCE è necessario scrivere End Sub dopo l'ultima istruzione appartenente alla routine.
Quindi:
Nella prima lezione avevo detto che le routine sono un'insieme di più istruzioni: esse risultano molto comode quando si ha bisogno di eseguire più volte uno stesso gruppo di istruzioni, in modo che quando si ha necessità di eseguire una certa operazione, è sufficiente richiamare quella routine anziché riscrivere ogni volta le medesime istruzioni. Supponiamo, ad es., di voler calcolare i quadrati dei valori 2, 5, e 8 e di volerli visualizzare nella finestra Immediata: senza usare le routine saremmo costretti a scrivere (ad es. nell'evento Load del nostro form):
Private Sub Form_Load()
'dichiaro la variabile:
Dim x As Integer
x = 2 ^ 2 'il simbolo "^" serve a indicare
' l'elevamento a potenza
'stampo il risultato:
Debug.Print x
x = 5 ^ 2
Debug.Print x
x = 8 ^ 2
Debug.Print x
End Sub
Nota: il simbolo " ' " serve a
indicare un commento
al codice: il commento viene
del tutto ignorato da Visual Basic,
la sua utilità deriva dal fatto
che permette a chi legge il codice
di comprendere meglio cosa
fanno le istruzioni.
Utilizzando invece una routine di nome "Quadrato" che calcola il quadrato di un numero potremmo scrivere, nella sezione delle dichiarazioni:
Dim x As Integer
Private Sub Quadrato() 'qui inizia la routine
Dim y As Integer 'dichiaro una variabile locale
y = x ^ 2 'calcolo il quadrato
Debug.Print y 'stampo il risultato
End Sub 'qui finisce la routine
e, nell'evento Load del form:
Public Sub Form_Load ()
x = 2 'imposto la variabile
Quadrato 'calcolo il quadrato e stampo il risultato
x = 5
Quadrato
x = 8
Quadrato
End Sub
In un esempio così semplice è poco utile ricorrere alle routine, ma se pensate che una routine potrebbe contenere centinaia di istruzioni che vanno ripetute più o meno spesso vi accorgerete della loro grande utilità; un aspetto da non trascurare è infatti anche la leggibilità del codice, ovvero la sua facilità di comprensione: utilizzando le routine per eseguire operazioni complesse è molto più semplice capire come funziona un programma (procedendo con gli esempi ve ne renderete conto sempre meglio).
Nell'esempio qui sopra abbiamo impostato una variabile dichiarata a livello di modulo (la variabile x) in modo che potesse essere utilizzata anche nella routine: in realtà ciò non è necessario, infatti è possibile utilizzare il passaggio di parametri alla routine. Un parametro è una variabile che viene comunicata alla routine, la quale può utilizzarla più o meno come se fosse una sua variabile locale: in questo modo è possibile travalicare i limiti imposti dall'area di visibilità delle variabili (v. lez. precedente).
Ad es., se noi avessimo dichiarato la variabile x non nella sezione delle dichiarazioni ma all'interno dell'evento Load, avremmo ottenuto un errore del tipo:
perché la variabile non sarebbe stata visibile nella routine quadrato (a meno che abbiate dimenticato di inserire l'istruzione "Option Explicit": d'ora in poi darò sempre per scontato che sia attivata la dichiarazione obbligatoria delle variabili). Invece utilizzando il passaggio di parametri questo problema non sussiste più. Per indicare a Visual Basic che la nostra routine vuole uno o più parametri bisogna elencare i parametri, o argomenti, all'interno delle parentesi nella dichiarazione della routine:
Private Sub Quadrato (Param As Integer)
Dim y As Integer
y = Param ^ 2
Debug.Print y
End Sub
Come avrete notato, l'indicazione dell'argomento segue le stesse regole della dichiarazione delle variabili, eccezion fatta per la parola chiave Dim (o Private o public) che in questo caso NON va inserita perché non avrebbe senso: l'argomento di una routine è utilizzabile solo all'interno di quella routine. Per indicare più parametri bisogna separarli con la virgola: ad es:
... (Param1 As Integer, Param2 As String ...) ...
Il nome del parametro non ha nulla a che vedere col nome della variabile passata come argomento (nel nostro caso la variabile x): nel caso in cui i nomi fossero uguali, all'interno della routine verrebbe comunque utilizzato il parametro e non la variabile originaria. All'interno dell'evento load del form potremo ora scrivere:
Public Sub Form_Load()
Dim x As Integer
x = 2
Quadrato x
x = 5
Quadrato x
x = 8
Quadrato x
End Sub
e nella finestra immediata vedremo:
Naturalmente l'argomento da passare alla routine può essere una variabile dichiarata da noi, ma anche un valore fisso: possiamo quindi fare a meno, nel nostro caso, di utilizzare la variabile x e scrivere direttamente:
Public Sub Form_Load()
Quadrato (2) 'oppure Quadrato 2
Quadrato (5) 'oppure Quadrato 5
Quadrato (8) 'oppure Quadrato 8
End Sub
ottenendo il medesimo risultato: ogni volta che viene chiamata la routine quadrato, la variabile param assumerà di volta in volta il valore 2, 5, 8. Avrete sicuramente capito che per eseguire una routine è sufficiente scriverne il nome, eventualmente seguito dagli argomenti richiesti dalla routine: gli argomenti andranno separati da virgole nel caso siano più di uno (ad es. quadrato 2, 3, "Pippo" se gli argomenti sono tre: i primi due numerici e il terzo una stringa). E' però possibile anche utilizzare l'istruzione Call, seguita dal nome della routine e dai parametri, questa volta però obbligatoriamente racchiusi tra parentesi:
Call Quadrato (2)
oppure:
Call Quadrato (2, 3, Pippo)
se gli argomenti sono diversi.
L'uso delle parentesi sarà fondamentale nell'uso delle funzioni, che vedremo la prossima volta.
Quarta lezione - le funzioni in Visual Basic (a cura di Giorgio Abraini)
Dopo le routine, tocca alle funzioni: la differenza tra una routine e una funzione è che la prima esegue un certo numero di istruzioni, la seconda esegue un certo numero di istruzioni e in più restituisce un valore, che quindi può essere memorizzato in una variabile. L'uso delle funzioni è comodo quando è opportuno sapere quale sia il risultato finale delle operazioni eseguite: ad es., la funzione potrebbe restituire un valore di errore se qualcosa è andato storto durante la sua esecuzione, oppure potrebbe restituire il risultato di un'operazione matematica come quella eseguita dalla routine quadrato che abbiamo visto nella lezione precedente. Per dichiarare una funzione bisogna usare la parola chiave Function al posto di Sub; inoltre è sempre meglio, se possibile, specificare il tipo di dati restituito dalla funzione. Ad es.:
Private Function Quadrato (Param As Integer) As Long
Quadrato=Param ^ 2
Debug.Print Quadrato
End Function
Il valore che la funzione deve restituire deve essere assegnato al nome della funzione:
Quadrato = Param ^ 2
che viene quindi trattato come se fosse una variabile: perciò rispetto alla routine Quadrato (v.lez. precedente) si può fare a meno di dichiarare una variabile locale a cui assegnare il risultato dell'elevamento a potenza. Inoltre, possiamo fare a meno di usare l'istruzione Debug.Print all'interno della funzione, perché il valore che vogliamo visualizzare è quello restituito dalla funzione e quindi è visibile anche all'esterno di essa: nell'evento Load del form, anziché scrivere
Quadrato 2
Quadrato 5
Quadrato 8
possiamo quindi scrivere:
Debug.Print Quadrato(2)
Debug.Print Quadrato(5)
Debug.Print Quadrato(8)
In questo caso è necessario usare le parentesi tonde: queste vanno sempre indicate se si vuole che la funzione restituisca effettivamente un valore; infatti è anche possibile richiamare la funzione senza parentesi:
Quadrato 2
come se fosse una routine, ma non sarà possibile scrivere:
Debug.Print Quadrato 2
perché chiamando la funzione senza usare le parentesi si impedisce che essa restituisca un valore. Lo stesso accade se si chiama la funzione con l'istruzione Call; pertanto mentre una routine può essere chiamata indifferentemente con o senza le parentesi, una funzione deve essere chiamata con le parentesi (ma senza usare l'istruzione Call) affinché restituisca un valore.
Per quanto riguarda il passaggio di parametri, valgono le stesse regole che abbiamo visto per le routine; a questo proposito c'è da sapere un'altra cosa: i parametri possono essere preceduti dalla parola chiave ByVal o ByRef. Queste parole chiave specificano, rispettivamente, che il parametro viene passato per valore o per riferimento: senza entrare troppo nei dettagli, è sufficiente dire che nel primo caso la funzione (o la routine) conosce soltanto il valore della variabile passata come argomento, mentre nel secondo caso conosce, per così dire, la variabile stessa, potendo quindi intervenire direttamente su di essa. Concretamente ciò significa che se la variabile è passata per valore, la funzione (o la routine) potrà eseguire operazioni sul valore della variabile ma non potrà modificarla; se invece la variabile è passata per riferimento, la funzione potrà modificare direttamente la variabile. Per ulteriori dettagli consultare l'articolo COME DICHIARARE LE API
Un esempio chiarirà tutto: creiamo una funzione con due argomenti, uno passato per valore, l'altro per riferimento e facciamo qualche operazione su di essi:
Private Function Prova (ByVal Valore As Integer, ByRef Riferimento As _
Integer) As Integer
Valore = Valore + 1
Riferimento = Riferimento + 1
Prova = Valore + Riferimento
End Function
La funzione non fa altro che incrementare di 1 i due argomenti e restituirne la somma.
Ora dichiariamo, nell'evento Load del form, due variabili e passiamole alla funzione:
Private Sub Form_Load()
Dim Var1 As Integer, Var2 As Integer
Dim Risultato As Integer
Var1 = 3
Var2 = 10
Risultato = Prova(Var1, Var2)
Debug.Print Risultato
Debug.Print Var1, Var2
End Sub
Eseguendo il progetto, noteremo che nella finestra immediata compaiono i valori:
15 è il risultato della funzione ((3+1)+(10+1)=15); la variabile Var1, che è stata passata per valore e che quindi non ha potuto essere stata modificata, conserva il valore che aveva prima della chiamata della funzione, cioè 3; invece Var2, che è stata passata per riferimento, è stata effettivamente modificata durante l'esecuzione della funzione, e infatti dopo la chiamata ha assunto il valore 11, cioè il valore originario più 1.
Se non viene specificata né ByVal né ByRef, Visual Basic penserà automaticamente che la variabile è stata passata per riferimento: questa infatti è l'impostazione predefinita. Un'altra parola chiave che può precedere gli argomenti è Optional: questa specifica che l'argomento seguente non è obbligatorio, e in tal caso è possibile indicare un valore di default che l'argomento deve assumere nel caso in cui l'istruzione chiamante la funzione (o la routine) ometta effettivamente il parametro. Ad es.:
Private Function Prova (A As Integer, Optional B As Integer = 1) As Integer
Prova = A + B
End Function
se nell'evento Load del form scriviamo:
Private Sub Form_Load()
Debug.Print Prova(2)
Debug.Print Prova(2, 3)
End Sub
vedremo nella finestra immediata:
3
5
Nel primo caso, infatti, il parametro B è omesso e quindi assumerà per default il valore 1.
Come per le routine, anche le variabili locali di una funzione possono essere dichiarate Static per indicare che il loro valore viene mantenuto tra una chiamata e l'altra della funzione (v.lez. 2): se si vuole che TUTTE le variabili locali siano statiche, si può dichiarare la funzione in questo modo:
Private Static Function Prova() As Integer
Dim A As String 'variabile statica
Dim B As Long 'variabile statica
End Function
Le variabili locali saranno statiche anche se nella loro dichiarazione non è stata specificata la parola chiave Static.

Quinta lezione - il primo programma in Visual Basic (a cura di Giorgio Abraini)
Ora che conosciamo le basi fondamentali della programmazione, possiamo avvicinarci alla creazione del nostro primo programma: il classico "Hello World!", ovvero un programma che non fa altro che visualizzare un messaggio di saluto.
Per fare ciò cominciamo col creare un nuovo progetto e a inserire nel form una Label, cioè un'etichetta: il controllo Label lo trovate sulla casella degli strumenti che solitamente si trova sul bordo sinistro dello schermo, ed è quello indicato dall'icona "A".
Una Label è un controllo che permette di visualizzare una stringa: una volta inserita sul form, potremo vedere le sue proprietà nella finestra delle proprietà, posta sulla destra dello schermo; le proprietà possono essere visualizzate in ordine alfabetico o per categoria: scegliendo l'ordine alfabetico, vedremo innanzitutto la proprietà Name
che è la prima dell'elenco e che definisce il nome con cui si può fare riferimento al controllo all'interno del codice, come si può leggere anche nella descrizione fornita nella parte inferiore della finestra delle proprietà. Il nome predefinito, per qualunque controllo, è dato dal nome del tipo di controllo (nel nostro caso Label) più un numero che viene incrementato ogni volta che si aggiunge un altro controllo dello stesso tipo: se provate ad aggiungere un'altra Label sul form, vedrete che il suo nome predefinito sarà Label2, e così via.
Diamo un'occhiata ad altre proprietà interessanti: la proprietà
Caption
ad es., assume il valore "Label1": provando a modificare questo valore (basta cliccarci sopra una volta e scrivere) vedrete che cambierà anche la stringa visualizzata sul form (per ora lasciate pure "Label1"); pertanto sarà questa la proprietà che dovremo opportunamente impostare per visualizzare il messaggio di saluto. Prima di vedere come, inseriamo sul form anche un CommandButton, cioè un pulsante, che trovate anch'esso sulla casella degli strumenti.
nella finestra delle proprietà, modificate la proprietà Caption e assegnategli il valore "Saluta!" (senza scrivere anche le virgolette). La posizione dell'etichetta e del pulsante sul form può essere cambiata semplicemente puntando il mouse sul controllo e trascinandolo nella posizione voluta. Ora andate sulla finestra del codice (se non la vedete cliccate due volte sul form, oppure cliccate sulla prima icona che vedete nella finestra del progetto, facendo attenzione ad aver selezionato la voce "Form1" nella medesima finestra): selezionate la voce Command1 dall'elenco degli oggetti e la voce click dall'elenco delle routine, e vi troverete, ovviamente, nella parte di codice che verrà eseguita quando si fa click sul pulsante:
ora, all'interno di questa routine scrivete:
Label1.Caption = "Hello World!"
oppure:
Label1.Caption = "Salve Mondo!"
se non amate la lingua inglese…
Provate ad avviare il progetto premendo F5 e cliccate sul pulsante "Saluta": il testo dell'etichetta diventerà "Hello World!". Quello che succede dovrebbe essere evidente: cliccando sul pulsante viene scatenato l'evento Click relativo ad esso, e viene quindi eseguita l'istruzione che abbiamo inserito noi, e che imposta il valore della proprietà caption dell'etichetta a "Hello World!".
Si dice che questa proprietà viene impostata in fase di esecuzione, ovvero mentre il programma è in esecuzione; la proprietà caption del pulsante, invece, è stato impostato in fase di progettazione, ovvero quando abbiamo "disegnato" la nostra applicazione. Volendo, avremmo potuto impostare la proprietà Caption dell'etichetta già in fase di esecuzione: in tal caso non avremmo avuto bisogno del pulsante "Saluta".
Proviamo ora a migliorare un po' la nostra applicazione: di solito è buona norma inizializzare il valore di proprietà o variabili che andranno in seguito modificate; ad esempio, prima di premere il pulsante "Saluta", l'etichetta mostrava la stringa "Label1", cosa non molto bella a vedersi: quindi è meglio annullare la proprietà Caption. E' possibile farlo in fase di progettazione, cancellando "Label1" nella finestra delle proprietà e premendo Invio; in fase di esecuzione, bisogna usare l'istruzione:
Label1.Caption = ""
dove tra le virgolette non ci devono essere spazi: è una "stringa nulla", di lunghezza zero, che non contiene alcun carattere. Questa istruzione dovremmo metterla in una routine che viene eseguita subito, non appena l'applicazione viene avviata: il posto più adatto è senz'altro la routine Load del form, quella che abbiamo usato per i nostri esempi nelle lezioni precedenti; infatti l'evento Load del form viene generato prima della visualizzazione della finestra. Se volessimo poter annullare, ossia cancellare, il saluto, dovremmo inserire sul form un altro pulsante: assegnate il valore "Annulla" (sempre senza le virgolette) alla proprietà Caption e, nella routine Click (del Command2, non del Command1!) ripetete l'istruzione:
Label1.Caption = ""
E' buona norma, inoltre, inserire un pulsante per uscire dal programma, anche se non è necessario in quanto basta premere la X in alto a destra nel form per chiudere l'applicazione; per terminare un programma, bisogna "scaricare" tutti i form attualmente caricati in memoria (nel nostro caso solo il form Form1): nella routine Click del pulsante Command3, quindi, dovremo scrivere:
Unload Form1
L'istruzione unload serve appunto ad eliminare dalla memoria del computer i riferimenti al form di nome Form1, e di conseguenza a terminare l'applicazione; l'istruzione determina la generazione dell'evento Unload, che è il corrispettivo dell'evento Load di cui si è detto sopra; analogamente, esiste anche l'istruzione Load che serve per "caricare" un form in memoria. Al posto di "Form1" è possibile utilizzare la parola chiave Me, che indica, in questo caso, il form corrente, ossia quello a cui appartiene la riga di codice che sta per essere eseguita. La Caption del pulsante Command3 andrebbe impostata, per esempio, a "Esci". Se proviamo ad eseguire l'applicazione, vedremo inizialmente solo i tre pulsanti "Saluta", "Annulla" ed "Esci": premendo "Saluta", il programma visualizzerà il messaggio di saluto, premendo "Annulla" questo messaggio scomparirà e premendo "Esci" chiuderemo il programma. Questo esempio è ovviamente molto semplice, ma cercate di apprezzare il fatto che abbiamo creato un'applicazione completa scrivendo soltanto 4 (e dico 4) righe di codice.
Per migliorare ulteriormente il nostro programma potremmo, ad es., cambiare la stringa che compare nella barra del titolo del form (nel nostro caso "Form1"): anche in questo caso si tratta della proprietà Caption (del form); basta andare nella finestra della proprietà, selezionare il form dalla casella a discesa, selezionare la proprietà Caption e scrivere, ad esempio:
"Programma di esempio Hello World"
Posizionando i pulsanti sul form, probabilmente non sarete riusciti (almeno non al primo colpo) a farli delle stesse dimensioni: per renderli uguali basta selezionarli tutti insieme, come si fa per selezionare più file in Gestione Risorse, cioè disegnando un rettangolo attorno ad essi
oppure cliccandoci sopra tenendo premuto il tasto Ctrl; dopodichè, andate sul menù Formato - Rendi uguale e scegliete Entrambe: i tre pulsanti avranno tutti la stessa altezza e larghezza. Nel caso non siano allineati, potete selezionarli e andare sul menù Formato - Allinea e scegliere la voce opportuna. Inoltre, è opportuno modificare i nomi dei vari controlli: ad esempio, il nome dell'etichetta potrebbe essere "lblHelloWorld" (senza spazi, mi raccomando); quello dei pulsanti "cmdSaluta", "cmdAnnulla", "cmdEsci"; naturalmente in tal caso bisogna modificare anche le istruzioni del codice. Così:
Label1.Caption = ""
diventa:
lblHelloWord.Caption = ""
e così via. Il motivo per cui i nomi sono preceduti da lbl o cmd ve lo spiegherò la prossima volta.
Insieme a questa lezione è disponibile il progetto finale di esempio "Hello World": per aprirlo basta selezionarlo dal menu File - Apri progetto; il progetto è disponibile solo per facilitarvi nella comprensione della lezione: è opportuno che cerchiate di realizzare da soli il progetto.
Sesta lezione - convenzioni, algebra e variabili Booleane (a cura di Giorgio Abraini)
Come promesso, cominciamo a vedere cosa sono e come si usano le cosiddette convenzioni di scrittura del codice: tali convenzioni sono regole che ogni programmatore dovrebbe seguire, al fine di scrivere un codice facilmente comprensibile e modificabile per chiunque abbia l'occasione di leggerlo ed eventualmente correggerlo e migliorarlo; rappresentano una sorta di galateo del programmatore, che permette di standardizzare il codice e quindi di renderne più semplice la lettura, la comprensione e la diffusione. Le convenzioni più semplici sono quelle che riguardano la denominazione di oggetti e variabili: secondo tali convenzioni è bene utilizzare un prefisso di due o tre caratteri specifico per ogni tipo di oggetto o variabile: ad es., se utilizziamo un CommandButton dovremmo usare il prefisso cmd; per le etichette (c.d. Label) il prefisso è lbl, per i form è frm, e così via. Leggendo un codice di questo tipo:
frmMain.Show
lblFileName.Caption="c:\pippo.txt"
cmdSave_Click
saremmo in grado di intuire subito che prima di tutto viene visualizzato il form frmMain, dopodiché la caption di un'etichetta viene aggiornata con un nome di file opportuno, e infine viene richiamata la routine corrispondente all'evento Click di un CommandButton. Senza usare i prefissi, non avremmo potuto sapere a priori a quale oggetto si riferisse la proprietà Caption o la routine Click. Analogo discorso vale per le variabili: innanzitutto è bene usare una lettera per indicarne l'area di validità: g per le variabili globali, o pubbliche, m per quelle private ma dichiarate a livello di modulo (quindi nella sezione dichiarazioni di un form, ad esempio); per le variabili locali delle routine non si usa alcun prefisso. Poi bisognerebbe utilizzare un altro prefisso per indicare il tipo di variabile: str per le stringhe, int per gli integer, lng per i long, ecc. Ad esempio:
gstrUserName
indica una variabile pubblica che contiene il nome dell'utente (e quindi si tratta di una stringa). Ci sono poi delle convenzioni per la strutturazione del codice, ovvero l'inserimento di commenti e l'uso di una formattazione adeguata del codice: ad esempio, ogni subroutine o funzione dovrebbe includere un commento che ne indichi lo scopo, il significato delle variabili richieste in input, il valore restituito, e ogni altra informazione non direttamente intuibile dal codice ma che serve a capire meglio cosa fa e come funziona la routine. Per quanto riguarda la formattazione del codice, bisognerebbe utilizzare i rientri: consideriamo ad es. queste istruzioni:
Non preoccupatevi del significato delle istruzioni if e for: le vedremo in dettaglio più avanti. La cosa importante è sapere è sapere che queste istruzioni sono nidificate, ovvero contenute l'una nell'altra: il blocco if…end if contiene il blocco for…next, che a sua volta contiene l'istruzione di aggiornamento della label. La nidificazione è appunto immediatamente evidenziata dal rientro delle varie istruzioni: maggiore è il rientro, maggiore è il livello logico di nidificazione; è un po' lo stesso principio utilizzato da Gestione risorse per visualizzare la gerarchia delle directories: più una cartella è "rientrata", meno importante è il suo livello gerarchico. Queste sono alcune delle convenzioni di scrittura del codice: altre le vedremo strada facendo. Non pensate però che queste convenzioni servano solo se il vostro codice deve essere letto da altri: anzi, la loro utilità è rivolta soprattutto all'autore del codice, perché spesso accade che egli non si ricordi più come e perché aveva strutturato in un certo modo il codice (quando avrete un po' più di esperienza ve ne accorgerete!).
Ora introduciamo il discorso delle operazioni logiche e delle variabili booleane (pron. buleàne). Chiunque dovrebbe avere familiarità coi concetti di vero e falso, due condizioni logiche opposte che stanno alla base del nostro modo di ragionare e che sono oggetto di studi filosofici (e successivamente matematici) sin dai tempi di Aristotele. Questi due concetti sono fondamentali per l'intero mondo dei computer, anzi per lo stesso funzionamento dei computer: infatti, come tutti sanno, un computer si basa su una logica binaria, in cui ogni singolo bit può assumere due soli valori: 0 e 1, che possono essere logicamente associati ai valori falso e vero. Il logico e matematico George Boole,
vissuto nel XIX secolo, sviluppò un insieme di regole (che formano l'algebra booleana) per eseguire operazioni logiche sui valori vero e falso. Le principali regole, di cui probabilmente avrete già sentito parlare, sono queste:
And (congiunzione), Or (disgiunzione), Not (negazione), Xor (exclusive Or)
Questi operatori logici funzionano come le operazioni matematiche (anzi, a ben guardare SONO operazioni matematiche, pur essendo diverse da quelle che abbiamo studiato alle elementari): legano tra di loro due espressioni booleane (cioè due valori del tipo vero/falso) e forniscono un risultato. In Visual Basic esiste un tipo di dati particolare, il tipo Boolean, che indica proprio una variabile booleana, che può assumere il valore vero o falso, o meglio True o False, che sono le parole chiave utilizzate nell'ambito delle operazioni logiche booleane. Lo schema del risultato degli operatori logici And, Or, Xor è il seguente:
True And True = True
True And False = False
False And True = False
False And False = False
True Or True = True
True Or False = True
False Or True = True
False Or False = False
Come si può notare, la congiunzione (And) di due espressioni è vera solo se sono vere entrambe le espressioni ("io lavoro a Milano e a Torino" è vera solo se è contemporaneamente vero che lavoro a Milano e che lavoro a Torino); la disgiunzione (Or) è vera solo se è vera almeno una delle due espressioni ("io lavoro a Milano o a Torino" è vera se lavoro sia a Milano che a Torino, ma è vera anche se lavoro a Milano e a Brescia o se lavoro a Torino e a Roma; sarà invece falsa solo se non lavoro né a Milano né a Torino). La disgiunzione esclusiva (Xor) non è immediatamente intuibile come la congiunzione e la disgiunzione, ma è un misto di queste due operazioni e della negazione: l'Xor restituisce True solo se le due espressioni sono diverse, mentre restituisce False solo se le due espressioni sono uguali; corrisponde alla "o" disgiuntiva, come quando si dice "io lavoro o a Milano o a Torino": mentre l'Or permette la compresenza di entrambe le alternative, l'Xor ne esclude una delle due. La negazione, infine, coinvolge una sola espressione e restituisce semplicemente il suo opposto:
Not True = False
Not False = True
Questi operatori possono essere raggruppati insieme: così come si può scrivere (5+2)*3, si può anche scrivere
(True Or False) And True
il risultato di questo esempio sarà True. Ora potete capire che l'Xor è il risultato di questa espressione (x e y sono due variabili booleane):
x Xor y = ((Not x) And y) Or (x And (Not y))
Ci sono altri due operatori logici disponibili in Visual Basic, che sono utilizzati molto raramente ma che potrebbero comunque essere utili: l'equivalenza (Eqv) e l'implicazione (Imp):
True Eqv True = True
True Eqv False = False
False Eqv True = False
False Eqv False = True
True Imp True = True
True Imp False = False
False Imp True = True
False Imp False = True
In parole povere, l'Eqv è la negazione dell'Xor: infatti due espressioni sono equivalenti se sono uguali (entrambe false o entrambe vere); l'Imp corrisponde più o meno alla costruzione "se… allora" con l'uso di una condizione sufficiente ma non necessaria, ad esempio "se x>y, allora x>y/2": questa implicazione è falsa solo se fosse x>y ma xy affinché sia x>y/2. Se volessimo implementare un operatore (chiamiamolo "Nec") per le condizioni necessarie ma non sufficienti, dovrebbe venir fuori una cosa del genere:
True Nec True = True True Nec False = True False Nec True = False False Nec False = True
Ma questo operatore in Visual Basic non esiste… Comunque gli operatori logici che è necessario conoscere sono solo And, Or, Not, e sarebbe bene conoscere anche Xor: la prossima volta vedremo degli esempi.
Settima lezione - Utilizzo delle variabili Booleane (a cura di Giorgio Abraini)
Nella scorsa lezione abbiamo visto cosa sono le variabili booleane e quali operazioni si possono fare con esse: ora vediamo come si usano. L'istruzione principale che fa uso di espressioni booleane è il costrutto
"if condizione then istruzione"
che, come dice il nome, esegue una o più istruzioni se si verifica una certa condizione. Ad esempio, l'istruzione:
If lblName.Visible = True Then lblName.Caption = "Pippo"
aggiorna la proprietà Caption dell'etichetta lblName se questa etichetta è visibile: il tipo della proprietà Visible, infatti, è proprio Boolean. Quando Visual Basic deve eseguire un'istruzione come quella riportata, prima di tutto verifica se l'espressione compresa tra le parole chiave If e Then è vera o falsa: se l'etichetta è visibile (cioè se la proprietà visible è uguale a True), la condizione è verificata e quindi sarà eseguita l'istruzione seguente la parola chiave Then; altrimenti la condizione non sarà verificata e l'istruzione sarà del tutto ignorata. Solitamente i programmatori VB ("VB" è ovviamente l'abbreviazione di Visual Basic…) utilizzano una sintassi leggermente diversa, organizzata su più di una riga (cosiddetta a blocco):
If lblName.Visible = True Then
lblName.Caption = "Pippo"
End If
In Visual Basic di solito si inserisce una istruzione per ogni riga, poiché è sufficiente il ritorno a capo per far capire a Visual Basic che l'istruzione è terminata; nel caso della If…Then, quindi, è possibile far stare tutta l'istruzione sulla stessa riga, ma se le istruzioni che seguono Then vengono riportate nelle righe successive, è necessario indicare dove finiscono le istruzioni appartenenti al blocco If, e questo si fa appunto con la parola chiave End If, esattamente come si fa con End Sub o End Function per indicare la fine di una subroutine o di una funzione.
Personalmente ritengo che la seconda sintassi sia più chiara e comoda, soprattutto quando le istruzioni da eseguire nel blocco sono più di una: infatti, se volessimo utilizzare la sintassi su una singola riga, bisognerebbe separare le varie istruzioni con i ":", ad esempio:
If lblName.Visible = True Then lblName.Caption = "Pippo": Form1.Caption = "Pippo": ...
(nota: il carattere " : " può essere utilizzato per riunire più istruzioni sulla stessa riga anche al di fuori di un'istruzione If…Then) L'istruzione If…Then permette di eseguire certe istruzioni anche nel caso in cui la condizione non sia verificata: ciò è possibile grazie alla parola chiave Else, ad esempio:
If strName = "Pippo" Then
frmProva.Show
Else
frmProva.Hide
End If
Se la variabile stringa strName è uguale a "Pippo", il form frmProva viene mostrato, altrimenti viene nascosto.
In questo modo è possibile ottenere una certa flessibilità nell'esecuzione condizionale di un gruppo di istruzioni; questa flessibilità è ulteriormente aumentata dalla possibilità di utilizzare la parola chiave ElseIf:
If strName = "Pippo" Then
frmProva.Show
frmProva.lblName.Caption = "Pippo"
ElseIf strName = "Topolino" Then
frmProva.Show
frmProva.lblName.Caption = "Minnie"
ElseIf strName = "Pluto" Then
frmProva.Show
frmProva.lblName.Caption="Orazio"
Else
frmProva.Hide
End If
In un blocco di questo tipo, Visual Basic comincia a verificare le condizioni una per una: quando una delle condizioni è verificata, vengono eseguite le istruzioni relative a quella condizione, ignorando le altre; se nessuna delle condizioni è verificata, vengono eseguite le istruzioni comprese nella clausola else, se è presente: altrimenti non viene eseguita alcuna istruzione. Infatti le clausole Else e ElseIf sono facoltative.
All'interno di ogni blocco If…Then è possibile inserire qualunque istruzione, anche un'altra If…Then (si parla dei cosiddetti blocchi annidati); ad esempio si potrebbe fare:
If intX > 10 Then
If intX > 100 Then
intY = intX / 2
Else
intY = intX + 10
End If
ElseIf intX > 5 Then
intY = 0
Else
bValue = True
End If
In questo blocco viene valutato il valore della variabile Integer intX: se è maggiore di 10, viene effettuato un ulteriore controllo: se intX è maggiore di 100, viene posto intY = intX/2, altrimenti intY = intX+10; se invece intX non è maggiore di 10 (cioè intX 5: in questo caso si pone intY = 0. Se infine intX 10 And intX < 100) Or (intX > 200 And intX 10 And intX < 20 Then
con intX = 5; sapendo che intX > 10 è False e che il valore della condizione dipende dall'operatore And, si potrebbe anche fare a meno di confrontare intX con 20, perché qualunque sia il risultato di questo confronto la condizione sarà comunque False, dato che la prima espressione è False e l'And restituisce True solo se entrambe le espressioni sono True.
In effetti alcuni linguaggi più "intelligenti" (ad esempio il C++) evitano i confronti inutili; Visual Basic invece li fa sempre tutti, e nel caso di controlli pesanti bisognerebbe tenerne conto, facendo in modo che vengano valutate solo le espressioni strettamente necessarie. Ad es., l'istruzione:
If intX > 10 And intX < 20 Then
intX = intX * 2
End If
potrebbe essere modificata in questo modo:
If intX > 10 Then
if intX < 20 Then
IntX = IntX * 2
End If
End If
In questo modo, se intX=5, Visual Basic non perde tempo a confrontare intX con 20, perché già la prima condizione è falsa, e quindi il secondo blocco If sarà ignorato.
Ottava lezione - Altro ancora sulle variabili Booleane, Bit e Byte (a cura di Giorgio Abraini)
Finora abbiamo visto come vengono valutate le espressioni per verificare se sono true o false, e negli esempi abbiamo sempre usato dei confronti: in realtà questo spesso non è necessario, anzi è ridondante; consideriamo ad esempio questa condizione:
If frmAbout.Visible = True Then
La condizione risulta true se frmAbout.Visible è True, risulta False se frmAbout.Visible è False: in parole povere il valore della condizione è identico al valore di frmAbout.Visible; pertanto non c'è alcun bisogno di effettuare il confronto, ma sarà sufficiente scrivere:
If frmAbout.Visible Then
Infatti la condizione valutata dalla if deve essere interpretabile come un valore Boolean, e frmAbout.Visible è proprio un valore Boolean che può quindi essere usato direttamente come una condizione. E se avessimo bisogno di confrontare questa proprietà con False? E' semplicissimo, basta fare:
If Not frmAbout.Visible = True Then
Se il form non è visible, la condizione sarà verificata e le istruzioni saranno eseguite. Una cosa simile si può fare anche con i valori numerici: bisogna infatti sapere che i valori True e False in realtà non sono altro che numeri, e precisamente la parola chiave true corrisponde al valore -1 e la parola chiave false corrisponde al valore 0. Provate infatti a creare un nuovo progetto, a inserire un CommandButton e a scrivere queste righe nella routine dell'evento click:
Dim blnProva As Boolean
blnProva = -1
Debug.Print blnProva
blnProva = 0
Debug.Print blnProvaa
Una volta avviato il progetto, quando premerete il pulsante vedrete nella finestra immediata:
True
False
Ora provate a scrivere, al posto di "blnProva = -1", "blnProva = 34" e riavviate il progetto: ripremendo il pulsante, vedrete ancora nella finestra immediata:
True
False
Questo accade perché, quando un valore numerico viene convertito in Boolean, esso è interpretato da VB come true se è diverso da 0, anche se il valore predefinito di true è -1, ed è interpretato come false quando è uguale a 0.
Analogamente, è possibile convertire un valore Boolean in un valore numerico:
Dim intProva As Integer
intProva = True
Debug.Print intProva
intProva = False
Debug.Print intProva
Questa volta premendo sul pulsante vedrete nella finestra immediata:
-1

Insomma, le parole chiave True e False funzionano quasi come delle variabili numeriche il cui valore è rispettivamente -1 e 0: infatti la guida in linea di Visual Basic afferma che le variabili Boolean sono memorizzate come variabili numeriche a 16 bit (2 byte), ossia come gli Integer. Ma se un numero può essere direttamente interpretato come un valore Boolean, non c'è bisogno di scrivere, ad esempio:
If intProva 0 Then ' significa "diverso da"
ma si può scrivere direttamente:
If intProva Then
La conversione tra valori numerici e Booleani non è invece immediata quando si usano gli operatori logici; proviamo ad esempio a scrivere:
If 4 And 2 Then
Si potrebbe pensare che, essendo 4 0 e 2 0, e quindi entrambi True, la condizione risulti verificata perché True And True = True; invece no.
Il fatto è che gli operatori logici, se applicati a valori o variabili numeriche, eseguono un confronto "bit a bit" dei due operandi. Qui occorre fare una breve digressione sui bit e sui byte. Un bit è l'unità fondamentale su cui si basa il funzionamento di un computer, e può assumere solo due valori: 0 e 1; questa caratteristica deriva dal fatto che i supporti fisici di memorizzazione dei dati possono assumere solo due stati stabili: possono cioè essere magnetizzati in senso positivo oppure negativo. Pertanto i processori eseguono i calcoli in base al sistema numerico binario, cioè in base alle potenze di 2 (perché 2 sono i valori possibili: 0 e 1), mentre noi utilizziamo il sistema decimale, cioè in base alle potenze di 10 (perché 10 sono le cifre da 0 a 9). Solitamente come unità di misura della capacità di memoria dei computer viene preso il byte, che è semplicemente un gruppo di 8 bit contigui: se avessimo un numero di 8 cifre decimali questo sarebbe compreso tra 0 e
10 ^ 8 - 1 = 100.000.000 - 1 = 99.999.999
Invece un numero di 8 cifre binarie (cioè un byte) è compreso tra 0 e
2 ^ 8 - 1 = 256 - 1 = 255
infatti il tipo di dati byte memorizza proprio un numero compreso tra 0 e 255 (v.lez. 1). Un numero di 16 bit (cioè 2 byte, come un integer), ad es., è compreso tra 0 e
2 ^ 16 - 1 = 65535
e così via (il motivo per cui il tipo integer può assumere valori tra -32768 e +32767 lo vedremo un'altra volta). Viceversa, il numero 206 può essere scomposto, in base al sistema decimale, in
200 + 0 + 6 = 2 * 10 ^ 2 + 0 * 10 ^ 1 + 6 * 10 ^ 0
numerando ogni cifra a partire da destra (cioè considerando 6 la prima cifra, 0 la seconda cifra, 2 la terza cifra) potremo scrivere
206 = 2 * 10 ^ (3 - 1) + 0 * 10 ^ (2 - 1) + 6 * 10 ^ (1 - 1)
In altri termini, ogni numero può essere espresso come la somma dei prodotti di ogni cifra per 10 elevato alla posizione occupata da quella cifra meno uno. Questa spiegazione può sembrare molto complicata, ma è importante per capire come convertire un numero decimale in base binaria e un numero binario in base decimale; supponiamo ad es. di avere il numero binario 101: per sapere a quale numero decimale corrisponde, dovremo moltiplicare 1 (la prima cifra da destra) per 2^ (1 - 1); poi dovremo moltiplicare 0 (la seconda cifra) per 2 ^ (2 - 1) e infine 1 (la terza cifra da destra) per 2 ^ (3 - 1). In parole povere:
101 = 1 * 2 ^ (3 - 1) + 0 * 2 ^ (2 - 1) + 1 * 2 ^ (1 - 1) =
= 1 * 2 ^ 2 + 0 + 1 * 2 ^ 0 =
= 4 + 0 + 1 =
= 5
Se invece dovessimo convertire un numero decimale in binario, dovremmo dividere il numero per 2: se il risultato è intero, scriviamo 0, altrimenti 1; poi prendiamo la parte intera della divisione e la dividiamo ancora per 2, e procediamo in questo modo finchè non resta 0; infine si ribalta la serie di 0 e 1 che abbiamo scritto. Ad eempio, per convertire il numero 9 si procede così:
9 / 2 = 4,5
4 / 2 = 2
2 / 2 = 1
1 / 2 = 0,5
0 / 2 = 0
1

1
0
La successione che abbiamo ottenuto è quindi 10010, che ribaltata diventa: 01001, cioè 1001: infatti
1001 = 1 * 2 ^ (4 - 1) + 0 + 0 + 1 * 2(1 - 1) = 8 + 1 = 9
Ora torniamo al confronto bit a bit. Questo confronto, come dice il termine, prende in considerazione ogni singolo bit di un operando e lo confronta con il bit che occupa la stessa posizione nell'altro operando: il risultato del confronto sarà determinato dall'operatore logico utilizzato, tenendo conto che il bit 0 corrisponde a False e il bit 1 corrisponde a True. Supponiamo ad eempio. di fare 23 And 200:
00010111 (rappresentazione binaria di 23)
11001000 (rappresentazione binaria di 200)
--------
00000000 risultato dell'And bit a bit
Il risultato è 0 perché, per ogni posizione, i bit corrispondenti dei due operandi non sono mai contemporaneamente 1 e 1 (cioè True e True). Facciamo altri esempi:
13 Or 17:
00001101 (13)
00010001 (17)
--------
00011101 (29)
Not 55::
00110111 (55)
----------
11001000 (200)
Nel caso del Not x, vi faccio notare che il risultato è sempre 255 - x, ovvero il valore massimo che può assumere un byte meno il valore dell'operando (nel caso in cui x sia un dato di tipo byte).
Nona lezione - Studiare un'applicazione professionale: il Blocco Note di Windows (a cura di Giorgio Abraini)
Nella precedente lezione abbiamo visto che gli operatori logici, se applicati a variabili numeriche, eseguono un confronto bit a bit dei due operandi (o dell'operando, nel caso si usi l'operatore Not): ora vediamo in quali casi è utile utilizzare questo tipo di confronto.
Supponiamo di avere una serie di attributi relativi a un oggetto: questi attributi possono essere presenti singolarmente, oppure essere presenti contemporaneamente ad altri; ad esempio, gli attributi di un file (archivio, nascosto, sistema, sola lettura) possono essere presenti uno per uno, oppure anche più di uno insieme.
Per sapere quali attributi possiede un determinato file, essi vengono memorizzati come un numero: infatti ad ogni attributo corrisponde un preciso valore; all'attributo archivio corrisponde il valore 32, all'attributo nascosto corrisponde il valore 2, all'attributo sistema corrisponde 4 e all'attributo sola lettura corrisponde 1.
Così, se il valore degli attributi di un file è 2, sapremo che quello è un file nascosto, e viceversa; se invece il file, oltre ad essere nascosto, è anche di sola lettura, allora il valore dei suoi attributi sarà 3, cioè 2+1. Non è un caso, infatti, se i valori dei singoli attributi sono tutti potenze di 2 (anche 1 è potenza di 2: 1 = 2 ^ 0): in questo modo non ci sono possibilità di equivoci nel caso siano presenti più di un attributo, perché la somma di potenze di 2 sarà sempre diversa da una potenza di 2.
Ora, supponiamo che il valore degli attributi del file pippo.txt sia 34: come facciamo a sapere quali attributi corrispondono a 34?
Bisogna considerare il corrispondente valore binario:
00100010 (34)
-------------
00100000 (32)
00000010 (2)
Come si vede, 34 è la somma di 32 e 2 perché sono impostati (cioè valgono 1) il secondo e il sesto bit (a partire da destra, come sempre), che corrispondono appunto alle potenze:
2 ^ 1 = 2
e :
2 ^ 5 = 32
In questo caso è molto semplice capire quali sono i bit impostati, ma ovviamente non è sempre così: per capirlo senza fare troppa fatica è sufficiente usare l'operatore And con i valori giusti. Nel nostro caso sappiamo che i valori possibili sono 1, 2, 4, 32 oppure una loro combinazione; ebbene, eseguendo l'And con ognuno di questi valori sapremo subito quali bit sono impostati:
34 And 1 = 0
34 And 2 = 2
34 And 4 = 0
34 And 32 = 32
I valori diversi da 0 sono proprio 2 e 32, quindi sappiamo che il valore 34 corrisponde agli attributi nascosto e archivio.
Allora, se ad esempio volessimo cercare tutti i file nascosti all'interno di una directory, potremmo eseguire questo controllo (per ogni file):
If intAttr And vbHidden Then
dove intAttr è la variabile che contiene il valore degli attributi del file in esame, e vbHidden è una costante di VB che assume proprio il valore dell'attributo Hidden (nascosto), cioè 2: la condizione
intAttr And vbHidden
sarà diversa da 0 (e quindi True) solo se il file è nascosto; potrebbe anche avere altri attributi, ma a noi interessa solo che sia nascosto.
Altri esempi dell'uso del confronto bit a bit li vedremo man mano che se ne presenterà l'occasione.
Ora ricominciamo coi nostri progetti d'esempio: uno dei modi migliori per imparare un linguaggio di programmazione è provare a replicare un'applicazione "professionale", che conosciamo già e che sappiamo già utilizzare; per cominciare, naturalmente, è bene partire da un'applicazione semplice, ad esempio il Blocco Note di Windows.
Innanzitutto bisogna capire da quali controlli è costituita l'applicazione: nel caso del blocco note è facile vedere che esso comprende un menu e un'area bianca in cui scrivere il testo; quest'area bianca non è altro che un TextBox.
Cominciamo allora con l'aprire un nuovo progetto, inseriamo un TextBox sul form e chiamiamolo txtFile, mentre il form lo chiamiamo frmNotePad e come Caption gli diamo "Blocco Note - VBItalia"; trasciniamo txtFile in modo che l'angolo superiore sinistro coincida con l'angolo superiore sinistro di frmNotePad e poi ridimensioniamolo in modo che occupi tutta l'area disponibile in frmNotePad (vedi figura sottostante):
Lo stesso risultato si può ottenere selezionando txtFile e impostando a 0 le proprietà Top e Left nella finestra delle proprietà; le proprietà Height e Width vanno invece impostate con lo stesso valore delle proprietà ScaleHeight e ScaleWidth di frmNotePad.
Già che ci siamo, impostiamo la proprietà Multiline di txtFile su True, in modo che sia possibile visualizzare il testo su più righe e la proprietà ScrollBars su 2 in modo da visualizzare una barra di scorrimento verticale sulla destra di txtFile.
Ora inseriamo il menù, cominciando dal menù File, il più importante: per farlo dobbiamo visualizzare il nostro form (se non è visibile andate sul menù Finestra o cliccate due volte sulla voce giusta nella finestra di Gestione Progetti) e visualizzare l'Editor di Menù dal menù Strumenti, oppure premere l'apposito pulsante sulla barra degli strumenti (nella configurazione standard è il terzo pulsante da sinistra).
L'Editor di Menù permette di impostare le principali proprietà delle voci del nostro menù: prima di tutto bisogna scegliere la Caption, ossia la stringa che appare sul menù, nel nostro caso "File"; poi bisogna impostare il nome del menù, ad es. "mnuFile": il prefisso "mnu" indica, secondo le convenzioni di sintassi, che si tratta appunto di una voce di menù; le altre opzioni per ora potete lasciarle così come sono.
Ora che abbiamo il nostro menù File, bisogna inserire le apposite voci: per semplificarci il compito inseriremo solo le voci "Apri", "Salva", "Esci"; clicchiamo quindi sul pulsante "Successivo", che crea un'altra voce di menù, scriviamo la caption "Apri…", scriviamo il nome "mnuApri" e infine premiamo il pulsantino con la freccia verso destra: nella lista dei menù che compare in basso vedremo che la voce "Apri…" è preceduta da tre puntini, i quali indicano che Apri è un sottomenù di File;
clicchiamo ancora su "Successivo", scriviamo la caption e il nome per la voce "Salva": VB intuisce che si tratta ancora di un sottomenù di File, per cui non c'è più bisogno di premere il pulsante con la freccia verso destra; ripetiamo il procedimento per la voce Esci.
Ora che abbiamo disegnato il nostro menù File, possiamo premere OK: sul frmNotePad vedremo quindi il nostro menù, e cliccandoci sopra vedremo le sue voci; cliccando infine su una delle voci si aprirà la finestra del codice in corrispondenza della routine click di quella voce di menù.
Ora dobbiamo inserire il codice per l'apertura e il salvataggio dei file e per la chiusura dell'applicazione: occupiamoci prima dei file.
Per sapere quale file aprire o con quale nome salvare il testo che l'utente ha inserito nel textbox txtFile si potrebbe utilizzare la finestra di dialogo standard di Windows, proprio quella che usa lo stesso Blocco Note o qualunque altra applicazione di Windows, ma per ora ci accontentiamo di un semplice inputbox, ovvero una finestrella che chiede l'inserimento di una stringa: nel nostro caso questa stringa sarà il nome del file.
Prima di tutto, però, dobbiamo dichiarare le variabili che ci serviranno: queste saranno il nome del file da aprire o salvare e il contenuto di questo file;
cominciamo dunque a inserire, nella routine mnuApri_Click, le istruzioni:
Dim strNomeFile As String
Dim strTesto As String
StrNomeFile = Inputbox("Inserisci il nome del file:")
La funzione Inputbox è quella che fa comparire l'omonima finestra:
Come parametri richiede obbligatoriamente il prompt, ovvero il messaggio visualizzato nella finestra, e facoltativamente il "titolo" della finestra, cioè la stringa che compare nella barra del titolo; gli altri parametri, tutti facoltativi, per ora non ci interessano: comunque sulla guida trovate tutte le informazioni necessarie.
Il valore restituito dalla funzione è proprio ciò che l'utente inserisce nella casella di testo contenuta nella finestra, e noi assegniamo questo valore a una nostra variabile locale.
Una volta saputo il nome del file, dobbiamo aprirlo e leggerne il contenuto: per fare questo dobbiamo utilizzare l'istruzione open e quelle ad essa collegate.
Open serve ad aprire un file in lettura e/o scrittura: questa istruzione sarà analizzata in dettaglio più avanti, ora è sufficiente sapere che per utilizzarla è necessario indicare il nome del file da aprire, la modalità di apertura (in parole povere se vogliamo aprirlo per leggere e/o per scrivere) e un numero di file che verrà utilizzato per identificare univocamente il file che abbiamo aperto; la sintassi è la seguente:
Open nome del file For modalità di apertura As #numero del file
Noi conosciamo il nome del file, sappiamo che vogliamo aprirlo per leggerlo (quindi la modalità sarà input) e dato che è il primo file che apriamo possiamo usare 1 come numero di file:
Open strNomeFile For Input As #1
dopodiché dobbiamo leggerne il contenuto: esiste una funzione apposita, Input, che vuole sapere quanti byte deve leggere e da quale file; il numero di byte da leggere è esattamente uguale alla lunghezza del file, che possiamo recuperare attraverso la funzione LOF (Length Of File):
strTesto = Input(LOF(1),1)
Il testo letto da Input() viene così memorizzato in strTesto.
Ora che abbiamo letto il contenuto del file, possiamo anche chiuderlo:
Close 1
Ricordate di chiudere sempre i file aperti che non vi servono più, perché finché restano aperti non saranno accessibili da altre applicazioni.
Ora non ci resta che visualizzare ciò che abbiamo letto:
txtFile.Text = strTesto
Se volete, potete già provare ad avviare il progetto e ad aprire qualche file: per chiuderlo non possiamo ancora usare il menù File->Esci perché non abbiamo inserito le istruzioni necessarie, però è sufficiente cliccare sulla "x" nel form.
Il progetto sarà completato con la prossima lezione, perciò ora salvate il progetto (menù File->Salva progetto).
Decima lezione - Studiare un'applicazione professionale: il Blocco Note di Windows - Seconda parte (a cura di Giorgio Abraini)
Dopo aver inserito il codice per il menù mnuApri, scriviamo quello per mnuSalva: questa volta ci serve una sola variabile, e cioè strNomeFile, perché il testo che dobbiamo scrivere sul file è quello contenuto in txtFile, a cui possiamo accedere attraverso la proprietà Text; quindi non abbiamo bisogno di una variabile per il testo. Per sapere con quale nome salvare il file, usiamo ancora la funzione InputBox:
Dim strNomeFile As String
strNomeFile = InputBox("inserisci il nome del file:")
Ora dobbiamo nuovamente aprire il file (questa volta per scriverci sopra, quindi in modalità output) e scrivere il contenuto del TextBox:
Open strNomeFile For Output As #1
Print #1, txtFile.Text
Close 1
Per scrivere sul file abbiamo usato l'istruzione Print, che ha bisogno di conoscere il numero del file su cui scrivere e i valori da scrivere: questi valori possono essere separati da "," o da ";": la virgola indica che i valori saranno separati, all'interno del file, da una tabulazione, mentre il punto e virgola indica che i valori saranno scritti uno di seguito all'altro. Infine dobbiamo scrivere il codice per il menù Esci: qui basterebbe scrivere "Unload frmNotePad" o "Unload me" come avevamo fatto per il progetto "hello world!", ma dobbiamo assicurarci che non ci siano ancora attività in corso; in particolare, nel nostro caso dobbiamo assicurarci che non ci siano file aperti al momento di chiudere l'applicazione.
E' vero che ogni file viene chiuso dopo aver letto/scritto su di esso, ma questo è quello che avviene in circostanze normali: è possibile che in certi casi si verifichi un qualche errore che impedisca la chiusura del file nelle routine mnuApri e mnuSalva; il programmatore deve essere abbastanza previdente da includere ulteriori controlli al momento di uscire dall'applicazione. E' vero anche che quando un'applicazione termina, Visual Basic si preoccupa di chiudere tutti i file rimasti aperti, ma un controllo in più non fa mai male: è un po' come spegnere le luci e chiudere il gas quando si esce di casa.
Pertanto possiamo scrivere:
Close
Unload Me
L'istruzione Close serve a chiudere il file specificato dal numero passato come argomento all'istruzione (ad es., close 1 chiude il file numero 1); se si usa senza argomenti, close chiude tutti i file, aperti con l'istruzione Open, che non sono ancora stati chiusi; dopo aver chiuso tutti i file eventualmente ancora aperti possiamo uscire dall'applicazione con l'istruzione Unload.
Il nostro editor personale è così del tutto funzionante, tuttavia è ancora ampiamente migliorabile: ad esempio, potremmo inserire la voce di menù "Nuovo" per creare un nuovo file; questo non significa altro che ripulire il txtFile in modo che l'utente possa scrivere il testo del nuovo file: basterebbe quindi fare:
txtFile.Text = ""
In realtà questo non è sufficiente, perché bisogna controllare se sono state effettuate modifiche al testo dall'ultimo salvataggio: in questo caso, infatti, il programma dovrebbe chiedere gentilmente all'utente se vuole salvare le modifiche. Per realizzare questo controllo abbiamo bisogno di una variabile che ci dica se il testo è cambiato dall'ultimo salvataggio oppure no: dato che le possibilità sono solo due (il testo è cambiato/il testo non è cambiato) è opportuno utilizzare una variabile booleana che valga false se il testo non è cambiato e true se il testo è cambiato; per capire quando il testo cambia si può semplicemente sfruttare l'evento change del txtFile, che viene appunto generato quando il valore della proprietà txtFile.Text cambia.
Questa variabile però non può essere locale rispetto alla routine txtFile_Change, perché deve essere visibile anche nel menù Nuovo, quindi bisogna dichiararla a livello di modulo; allora scriviamo, nella sezione dichiarazioni del frmNotePad:
Dim blnTextChanged As Boolean
e nella routine txtFile_Change:
blnTextChanged = True
Non basta: non dobbiamo soltanto impostare a true blnTextChanged quando il testo cambia, ma dobbiamo anche impostare la variabile a False quando i cambiamenti vengono salvati su un file; perciò, nella routine mnuSalva_Click scriviamo alla fine:
blnTextChanged = False
Ora torniamo al menù Nuovo: innanzitutto bisogna inserirlo nel menù File, quindi apriamo il menù editor, selezioniamo dalla lista la voce "Apri", premiamo il pulsante "Inserisci" e infine scriviamo la caption "Nuovo" e il nome "mnuNuovo"; ora dall'editor del codice selezioniamo la routine mnuNuovo_Click: qui dobbiamo inserire il controllo per verificare se il testo è cambiato o no.
Trattandosi di un controllo, dobbiamo usare l'istruzione If...Then, secondo questo schema:
If blnTextChanged Then
'il testo è cambiato, quindi chiediamo all'utente se vuole salvare le modifiche
Else
'il testo non è cambiato, quindi basta svuotare il txtFile
End If
Per chiedere all'utente se vuole salvare le modifiche, in teoria potremmo utilizzare ancora la funzione inputbox: ma in questo caso è molto più conveniente usare un MessageBox, ovvero una di quelle normalissime finestre di Windows che visualizzano un messaggio chiedendoci cosa vogliamo fare: esattamente la stessa finestra che ci mostra proprio il Blocco Note quando non abbiamo salvato le modifiche a un file.
Per far apparire questa finestra dobbiamo usare l'istruzione MsgBox, la cui sintassi è questa:
msgbox prompt, buttons, title, helpfile, context
Tralasciando gli ultimi due argomenti, il prompt è il messaggio vero e proprio visualizzato nella finestra; title è invece il titolo della finestra di messaggio; il parametro buttons è un numero che indica lo "stile" del MessaGebox, ovvero indica quali pulsanti devono essere associati alla finestra: ad ogni pulsante o combinazione di pulsanti è associata una costante di Visual Basic, così che per visualizzare, ad es., i pulsanti "sì" e "no", basta scrivere vbYesNo al posto del parametro buttons, anche se è possibile scrivere direttamente il valore della costante vbYesNo, e cioè 4.
Analoghe costanti sono associate all'icona che è possibile visualizzare nel messaggio: per visualizzare i pulsanti e un'icona è perciò sufficiente fare la somma dei relativi valori; il principio è lo stesso dell'esempio sulle proprietà di un file che abbiamo visto nella lez. 9.
Per i nostri scopi potremmo utilizzare un'istruzione del genere:
MsgBox "Il testo è stato modificato. Vuoi salvare le modifiche?", _
vbYesNo+vbQuestion, "Conferma salvataggio"
ma questa istruzione non fa altro che visualizzare il messaggio: noi invece vogliamo anche sapere qual è la risposta dell'utente, cioè vogliamo sapere qual è il valore restituito dalla funzione MsgBox; ertanto dobbiamo usare msgbox con le parentesi, come se fosse una funzione e non una semplice istruzione, in modo che restituisca un valore.
Tale valore è una costante che rappresenta il pulsante premuto dall'utente: ad es., la costante vbOK vale 1 e indica che l'utente ha premuto il pulsante ok; pertanto noi dovremmo fare un controllo di questo tipo:
Dim intRisposta As Integer
intRisposta = MsgBox("Il testo è stato modificato. Vuoi salvare le modifiche?", _
vbYesNo+vbQuestion, "Conferma salvataggio")
If intRisposta = vbYes Then
'salviamo le modifiche
Else
'non salviamo le modifiche
End If
Per salvare le modifiche dovremmo scrivere istruzioni analoghe a quelle utilizzate nella routine mnuSalva_Click: ma chi ce lo fa fare di ripetere le medesime istruzioni, quando abbiamo già una routine bell'e pronta? Basterà semplicemente richiamarla.
In definitiva, la routine mnuNuovo_click sarà così:
Dim intRisposta As Integer
If blnTextChanged Then
intRisposta = MsgBox("Il testo è stato modificato. Vuoi salvare le modifiche?", _
vbYesNo+vbQuestion, "Conferma salvataggio")
If intRisposta = vbYes Then
'richiamo la routine che permette di salvare le modifiche
mnuSalva_Click
Else
txtFile.Text = ""
End If
Else
txtFile.Text = ""
End If
Nella prossima lezione apporteremo ulteriori miglioramenti al nostro Blocco Note personale.
Undicesima lezione - Studiare un'applicazione professionale: il Blocco Note di Windows - Terza parte (a cura di Giorgio Abraini)
Nella lezione precedente abbiamo visto come agire nel caso in cui l'utente abbia apportato modifiche al testo senza salvarle: questo controllo non va effettuato solo se l'utente sceglie di creare un nuovo file, ma anche se ne vuole aprire uno già esistente o se vuole chiudere il programma; quindi dobbiamo modificare anche le routine mnuApri e mnuEsci. In questi casi, però, non c'è bisogno di pulire il TextBox, perciò basterà scrivere:
Dim intRisposta As Integer
If blnTextChanged Then
intRisposta = MsgBox("Il testo è stato modificato. Vuoi salvare le modifiche?", _
vbYesNo + vbQuestion, "Conferma salvataggio")
If intRisposta = vbYes Then mnuSalva_click 'richiamo la routine che permette
'di salvare le modifiche
End If
La figura sotto mostra il risultato che si ottiene attraverso il codice appena descritto:
Per quanto riguarda l'uscita dal programma, questo controllo viene effettuato solo se l'utente sceglie il menù File->Esci, ma non se preme il pulsante "X" in alto a destra nel form.
In questo secondo caso, infatti, non viene certo richiamata la routine mnuEsci_click, ma viene generato direttamente l'evento Unload del frmNotePad: pertanto saremo noi a richiamare la routine mnuEsci_click dall'evento Unload:
mnuesci_Click
A questo punto, se si cerca di aprire un file senza avere salvato le modifiche al file correntemente aperto, succederà questo: compare un messaggio con la richiesta di confermare il salvataggio delle modifiche; se si risponde sì, comparirà una finestra per chiedere il nome del file da salvare, dopodiché comparirà un'altra finestra per chiedere il nome del file da aprire.
Poiché queste due ultime finestre sono identiche, sarà opportuno differenziarle leggermente per evitare confusioni: basterà a questo scopo specificare il titolo della finestra in questo modo:
strNomeFile = InputBox("Inserisci il nome del file:", "Salva")
nella routine mnuSalva_click, che dà come risultato la seguente finestra:
e:
strNomeFile = InputBox("Inserisci il nome del file:", "Apri")
nella routine mnuApri_click che permette di visualizzare la seguente finestra:
C'è un altro problema: quando si apre un file, il suo contenuto viene caricato in txtFile, e di conseguenza viene generato l'evento Change che imposta blnTextChanged a True; il programma perciò penserà che il testo sia stato modificato dall'utente, anche se in realtà è stato semplicemente aperto.
Per correggere questo "bug" (i "bug", come dovreste sapere, sono gli errori commessi dai programmatori nello sviluppo di un'applicazione), basterà aggiornare blnTextChanged in mnuApri_Click aggiungendo in fondo, DOPO l'istruzione txtFile.Text = strTesto:
blnTextChanged = False
Un altro bug da correggere è quello che si verifica quando il file che abbiamo inserito nell'inputbox non viene trovato: ciò può accadere sia perché abbiamo sbagliato a scrivere il nome del file, sia perché non abbiamo inserito il percorso completo del file; infatti, se manca il percorso, Visual Basic cercherà il file nella directory corrente, che solitamente è quella in cui risiede l'IDE (ad es. c:\programmi\microsoft visual basic\vb98). Per accertarvi di quale sia la directory corrente, potete scrivere, nella finestra immediata:
e premere "invio"; "?" è un carattere che nella finestra immediata ha lo stesso significato di Print, curdir è la funzione che restituisce, appunto, la directory corrente.
Per ovviare a questo inconveniete useremo, al posto del banale InputBox, le finestre di dialogo standard utilizzate da Windows per scegliere i file da aprire o salvare: queste finestre si chiamano CcommonDialog e sono contenute nel file comdlg32.ocx.
Il controllo CommonDialog di norma non è presente nella casella degli strumenti, perciò dobbiamo aggiungerla noi: cliccando col tasto destro del mouse sulla casella degli strumenti, apparirà un menù popup; scegliendo la voce Componenti
apparirà una finestra con una lista di componenti che è possibile aggiungere a quelli standard già presenti nella casella degli strumenti.
Scrollate la lista fino a trovare la voce "Microsoft Common Dialog Control 6.0" (l'ultimo numero potrebbe differire a seconda della versione di Visual Basic installata) e selezionatela, poi premete OK.
Nella casella degli strumenti ci sarà un oggetto in più, il controllo Commondialog: selezionatelo e inseritelo sul frmNotePad (non importa in quale punto, tanto non sarà visibile durante l'esecuzione del programma).
Questo controllo dispone di diverse interessanti proprietà, che approfondiremo a suo tempo: le cose essenziali da sapere sono che per visualizzare la finestra bisogna utilizzare uno dei metodi Show (nel nostro caso serviranno ShowOpen e ShowSave), e che il nome del file scelto dall'utente è contenuto nella proprietà FileName, insieme al percorso completo; invece il semplice nome del file, senza il percorso, è contenuto nella proprietà FileTitle.
Ora, al posto dell'InputBox, possiamo usare:
CommonDialog1.ShowOpen
in mnuApri_click, e:
CommonDialog1.ShowSave
in mnuSalva_click.
Non abbiamo più bisogno della variabile strNomeFile, perché per aprire il file basterà fare:
Open CommonDialog1.FileName For ...
Dato che solitamente col blocco note si aprono i file di testo, impostiamo i tipi di file che è possibile scegliere dalla finestra di dialogo nella casella di riepilogo posta in basso: per fare questo bisogna impostare correttamente la proprietà Filter, secondo questo schema:
descrizione1|filtro1|descrizione2|filtro2
La descrizione è la voce che compare nella casella di riepilogo, mentre il filtro è una stringa che indica quali file visualizzare; il carattere che separa la descrizione dal rispettivo filtro è la pipe (simbolo "|"), che solitamente si trova sulla tastiera sopra "\"; nel nostro caso basterà fare:
CommonDialog1.Filter="File di testo|*.txt|Tutti i file|*.*"
Questa riga si può mettere ad esempio nel Form_Load oppure appena prima di visualizzare la finestra di dialogo, o ancora direttamente nella finestra delle proprietà in fase di progettazione; selezionando, nella finestra di dialogo, il tipo "File di testo", saranno visualizzati solo i file con estensione *.txt; selezionando "Tutti i file" saranno visualizzati tutti i file. Ora il nostro blocco note ha un'aspetto un po' più professionale.
Restano ancora un paio di cose da fare (veramente ce ne sarebbero diverse, ma per ora ci accontentiamo così): se l'utente prova a ridimensionare la finestra, avrà la spiacevole sorpresa che il TextBox non si adegua alle dimensioni del form.
Per rimediare a quest'altro bug, dobbiamo sfruttare l'evento Resize del frmNotePad: questo evento è scatenato da qualunque operazione di ridimensionamento, e noi non dobbiamo fare altro che rendere uguali le dimensioni del txtFile a quelle del frmNotePad; basterà inserire queste due istruzioni:
txtFile.Width = Me.ScaleWidth - 50 txtFile.Height = Me.ScaleHeight - 50
Il "-50" serve solo a lasciare un po' di spazio tra il margine inferiore del txtFile e quello del frmNotePad; ho usato la proprietà ScaleWidth al posto di Width, perché la prima misura l'area interna del form: provate a usare Me.Width e vi accorgerete della differenza.
Il secondo problema è che il txtFile è multiline, ma non ha le ScrollBar: è come se nel "vero" Blocco note fosse impostato "A capo automatico", e per vedere le righe non visibili nel TextBox dobbiamo usare le frecce per muovere il cursore; per risolvere il problema basterà impostare la proprietà ScrollBars del txtFile su "3 - Both", in modo da avere sia quella orizzontale che quella verticale.
Ora il nostro blocco note è pronto: in un'oretta e con circa 40 righe di codice abbiamo replicato in buona parte il Blocco Note di Windows.
Dodicesima lezione - Nozioni avanzate sulle variabili (a cura di Giorgio Abraini)
Completiamo il discorso sulle variabili e sulla loro dichiarazione: finora abbiamo visto i tipi Byte, Integer, Long, String e Boolean; naturalmente ce ne sono diversi altri, ad eempio: Date, utilizzato per memorizzare le date e le ore, che occupa 8 byte in memoria e che rappresenta una data compresa tra il 1/1/100 e il 31/12/9999; Single, utilizzato per memorizzare valori frazionari a precisione singola (4 byte) compresi tra circa -3,4*10^38 e circa 3,4*10^38; Double, che memorizza valori frazionari a precisione doppia (8 byte) compresi tra circa -1,8*10^308 e circa 1,8*10^308; Currency, che memorizza valori frazionari con quattro decimali (8 byte), solitamente utilizzato per calcoli con valori monetari; Object, che memorizza un riferimento a un oggetto qualunque, come un form, un textbox, un file, ecc.
I tipi Single e Double sono gestiti in modo diverso rispetto ai valori interi come Integer e Long: infatti i bit al loro interno sono divisi in due gruppi, la mantissa e l'esponente, secondo la notazione scientifica in base allo standard IEEE; per questo motivo i Single e i Double sono in grado di gestire numeri molto grandi o molto piccoli, ma talvolta hanno difficoltà a trattare le operazioni con numeri interi e con le approssimazioni.
Per quanto riguarda il tipo String, finora abbiamo considerato solo le stringhe a lunghezza variabile, ma in realtà ci sono anche le stringhe a lunghezza fissa, che si dichiarano in questo modo:
Dim Nome As String * lunghezza della stringa
Mentre una stringa di lunghezza variabile occuperà in memoria 10 byte più il numero di byte necessari per la stringa vera e propria, una stringa di lunghezza fissa occuperà solo il numero di byte indicato nella dichiarazione della variabile: questo perché, se la stringa è di lunghezza variabile, Visual Basic dovrà utilizzare memoria aggiuntiva (10 byte, in questo caso) per conservare informazioni quali, appunto, la lunghezza effettiva della stringa.
Esistono poi i dati definiti dall'utente, o meglio dal programmatore, che consistono in un insieme di sottovariabili ciascuna col suo tipo specifico; ad esempio io posso definire un mio tipo di variabile in questo modo:
Type Pippo
X As Integer
Y As Integer
Name As String * 5
End Type
dove Pippo naturalmente è un nome scelto a caso, e successivamente dichiarare una variabile di questo tipo:
Dim Topolino As Pippo
la variabile Topolino occuperà in memoria tanto spazio quanto è necessario per l'insieme dei suoi membri, cioè 2+2+5=9 byte, e si potrà assegnare un valore a ciascuno dei suoi membri scrivendo, ad esempio:
Topolino.X = 10 Topolino.Y = 2000 Topolino.Name = "Pluto"
Nella prima lezione avevo detto che la sintassi per la dichiarazione di variabili è la seguente:
Dim Nome As Tipo
In realtà la seconda parte ("As Tipo") si può omettere: in tal caso, per default, la variabile dichiarata sarà di tipo Variant.
Questo particolare tipo di dati è in grado di contenere qualunque altro tipo di dati, tranne che quelli definiti dall'utente e le stringhe di lunghezza fissa: in virtù di questa sua caratteristica, il tipo variant ha una grande flessibilità, che consente di utilizzarlo per memorizzare dati che a priori potrebbero assumere diversi tipi. Ad esempio, è possibile fare una cosa del genere:
Dim V As Variant 'oppure semplicemente: Dim V
V = 13.5
V = "Pippo"
V = 4000
Se si utilizzassero variabili di tipo definito, bisognerebbe dichiararne tre diverse: una di tipo Double, una di tipo String e una di tipo Integer.
La flessibilità del tipo Variant ha ovviamente un rovescio della medaglia: poiché un Variant può assumere valori di vario tipo, VB deve memorizzare, oltre al dato in sé, anche altre informazioni sul tipo di dato; per questo un Variant impegna 16 byte aggiuntivi oltre a quelli necessari per memorizzare il dato, se si tratta di un dato numerico, e ben 22 byte aggiuntivi se il dato contiene caratteri.
Non solo, ma poiché non si sa a priori di che tipo è il dato memorizzato in un Variant, ogni volta che questo dato viene utilizzato Visual Basic deve perdere tempo a convertirlo opportunamente nel tipo più appropriato: infatti ogni variabile di tipo Variant assume di volta in volta un sottotipo che indica per l'appunto quale tipo di dati sarebbe più appropriato per il valore assegnato alla variabile Variant.
Così, nell'esempio precedente, la variabile V assumerà i sottotipi Double, String e Integer: per rendersene conto è sufficiente utilizzare la funzione
TypeName(NomeVar)
che restituisce una stringa corrispondente al sottotipo della variabile NomeVar; oppure la funzione
VarType(nomevar)
che invece restituisce un Integer corrispondente.
Da tutto ciò risulta che il tipo Variant deve essere utilizzato solo se è giustificato da un bisogno di flessibilità: se un programmatore sa che una variabile assumerà sempre e soltanto valori compresi, ad esempio tra 1000 e 100000, dovrebbe dichiarare questa variabile come Long, perché così facendo otterrà un vantaggio in termini di utilizzazione della memoria e di velocità di esecuzione.
E' vero che se le variabili sono poche questo vantaggio risulta impercettibile, ma se le variabili sono numerose dichiararle come variant è una scelta decisamente inefficiente.
E comunque è sempre una buona regola di programmazione assegnare un tipo definito a ogni variabile tutte le volte che è possibile farlo.
C'è un modo per evitare che le variabili dichiarate senza tipo assumano di default il tipo variant: bisogna usare l'istruzione DefTipo, la cui sintassi è:
DefTipo IntervalloCaratteri
dove tipo indica il tipo di dati definito (ad esempio DefBool, DefInt, DefStr), e intervallocaratteri indica l'intervallo di lettere per le quali sarà impostato il tipo predefinito.
Ad esempio, scrivendo, PRIMA di ogni dichiarazione a livello di modulo (e quindi dopo Option Explicit):
DefLng A - G, X - Z
Dim Arg
Dim X
Dim Pippo
Le variabili Arg e X saranno di tipo Long, perché il carattere iniziale del loro nome è compreso negli intervalli specificati nell'istruzione Deftipo, mentre la variabile Pippo sarà un Variant perché la "p" non rientra negli intervalli "a-g" e "x-z".
Naturalmente è possibile usare più istruzioni DefTipo, con intervalli diversi, ed è anche possibile ignorare questa istruzione scrivendo esplicitamente il tipo di dato da assegnare a una variabile: la dichiarazione esplicita completa ha sempre la prevalenza rispetto alle impostazioni di default.
L'istruzione DefTipo non vale soltanto per le variabili dichiarate, ma anche per i parametri passati come argomenti delle routine e per il tip restituito dalle funzioni.
Un modo per abbreviare le dichiarazioni consiste nell'utilizzare i caratteri di dichiarazione del tipo: i tipi principali, infatti, sono identificati da un carattere speciale che può essere posposto al nome della variabile.
Ad esempio, le seguenti dichiarazioni sono equivalenti:
Dim X As Integer
Dim X%
perché il carattere % è il carattere di dichiarazione del tipo Integer: notate che il nome della variabile è semplicemente "X", senza il carattere di dichiarazione del tipo.
I caratteri di dichiarazione supportati in Visual Basic sono questi:
Carattere
Tipo
%
Integer
&
Long
@
Currency
#
Double
!
Single
$
String
Spesso è necessario convertire un tipo di variabile in un altro: la conversione più frequente è forse quella da numero a stringa e viceversa (cioè da 34 a "34" e viceversa), ma talvolta capita anche di dover convertire un Integer in un Long, o un Single in un Integer, o un Long in Date, ecc.
Esistono perciò delle apposite funzioni di conversione del tipo, che iniziano con "c" seguito dall'abbreviazione del tipo restituito:
Funzione
Tipo restituito
Intervallo
CBool
Booleano
Qualsiasi espressione numerica o stringa valida
CByte
Byte
Da 0 a 255
CCur
Currency
Da -922.337.203.685.477,5808 a 922.337.203.685.477,5807
CDate
Date
Qualsiasi espressione di data valida
CDbl
Double
Da -1,79769313486232E308 a -4,94065645841247E-324 per valori negativi; da 4,94065645841247E-324 a 1,79769313486232E308 per quelli positivi
CDec
Decimal
+/-79.228.162.514.264.337.593.543.950.335 per numeri con fattore di divisione zero, ovvero numeri senza decimali. Per i numeri con 28 decimali l'intervallo è +/-7,9228162514264337593543950335. Il numero minore possibile diverso da zero è 0,0000000000000000000000000001
CInt
Integer
Da -32.768 a 32.767; le frazioni vengono arrotondate
CLng
Long
Da -2.147.483.648 a 2.147.483.647; le frazioni vengono arrotondate
CSng
Single
Da -3,402823E38 a -1,401298E-45 per valori negativi; da 1,401298E-45 a 3,402823E38 per valori positivi
CStr
String
I valori restituiti da CStr dipendono dall'argomento della funzione
CVar
Variant
Lo stesso intervallo di Double per valori numerici. Lo stesso intervallo di String per valori non numerici
Naturalmente il parametro passato a queste funzioni deve poter assumere un valore che rientra nell'intervallo specifico del tipo restituito: ad esempio non è possibile scrivere:
CInt(100000.45)
perché il valore 100000 è superiore al massimo consentito per un Integer.
Tredicesima lezione - Estensione della logica binaria (a cura di Giorgio Abraini)
Nella lezione 8 abbiamo visto cosa sono i bit, i byte e come funziona la logica binaria che sta alla base dei moderni computer: seguendo questa logica abbiamo capito che un byte può contenere un valore compreso tra 0 e 255, due byte possono contenere un valore compreso tra 0 e 65535, e così via.
Tuttavia, questo è vero solo se si usa una logica strettamente binaria: nulla però impedisce di considerare una sequenza di bit in un modo leggermente diverso, secondo la nostra convenienza; potremmo cioè suddividere una sequenza di bit in uno o più gruppi aventi significati diversi.
In altre parole, i bit possono essere organizzati in modo tale che possano assumere un valore diverso da quello che avrebbero secondo il sistema binario puro e semplice: questo è proprio ciò che si fa per permettere al calcolatore di considerare i numeri negativi.
Una variabile di tipo Integer, ad esempio, consta di 16 bit (2 byte) e secondo il sistema binario dovrebbe poter assumere valori da 0 fino a 65535, mentre in realtà sappiamo che il suo intervallo di valori va da -32768 a +32767, ovvero da -2^15 a +2^15 - 1.
E' chiaro quindi che il valore assoluto del numero memorizzato in una variabile Integer è rappresentabile soltanto con 15 bit, e non con 16: che fine ha fatto il sedicesimo bit?
E' facile intuire che il sedicesimo bit è utilizzato per rappresentare il segno del numero; in fin dei conti non cambia nulla: l'intervallo -32768/+32767 ha lo stesso numero di elementi dell'intervallo 0/65535 (in entrambi i casi infatti il numero di valori possibili è 65536 = 2^16), però grazie al "trucco" di utilizzare un bit come un bit di segno abbiamo la possibilità di usare anche i numeri negativi.
Per la precisione, il bit di segno è il bit più significativo, ovvero quello più a sinistra (ricordate sempre che i bit si contano a cominciare da destra), e gli altri quindici sono usati per indicare il numero vero e proprio; quindi il modo in cui gli Integer vengono memorizzati nella memoria del computer è schematizzabile in questo modo:
primo byte
secondo byte
x xxxxxxx
xxxxxxxx
dove la prima x a sinistra rappresenta il sedicesimo bit, ovvero il bit di segno: se questo vale 0, il valore della variabile sarà positivo, se vale 1 il valore sarà negativo; ad esempio, il numero 1453 è rappresentabile in questo modo:
00000101 10101101
Contrariamente a quanto si potrebbe pensare, però, il numero -1453 NON è rappresentato così:
10000101 10101101
bensì è rappresentato così:
11111010 01010011
Il motivo di questo strano fenomeno è che per il computer il numero -1453 non è banalmente il numero 1453 con "un meno davanti", ma è quel numero che, sommato a 1453, restituisce 0: infatti se provate a fare la somma (nel sistema binario, quindi scrivendo 0 "col riporto di 1" quando la somma di due bit è uguale a 2)
0000010110101101+ (+1453)
1111101001010011= (-1453)
-----------------
10000000000000000
troverete che il risultato della somma è un 1 seguito da 16 zeri; poiché noi stiamo considerando gli Integer, i bit che ci interessano sono solo gli ultimi 16, e quindi dell'1 iniziale possiamo fregarcene tranquillamente: quello che resta è 0, che è proprio il risultato della somma algebrica 1453 + (-1453).
Tutto ciò sembra molto macchinoso, ma c'è un modo molto semplice per trovare la rappresentazione binaria di un numero negativo, conoscendo quella del corrispondente numero positivo: la rappresentazione binaria di -x è uguale a (not x) +1: prendete la rappresentazione binaria di 1453, cambiate ogni bit (cioè applicate l'operatore not ad ogni singolo bit) e infine aggiungete 1 al risultato ("aggiungere" nel senso di fare la somma, non nel senso di scriverlo in fondo come se fosse il diciassettesimo bit!): troverete esattamente la rappresentazione binaria di -1453. Lo stesso discorso naturalmente può essere applicato anche agli altri tipi numerici (Long, Single, Double, ecc.): infatti in Visual Basic tutti i tipi di dati numerici, ad eccezione del tipo byte, sono "con segno", cioè utilizzano il bit più significativo come bit di segno. Una particolare organizzazione dei bit consente anche di rappresentare i numeri frazionari, oppure di rappresentare i numeri secondo la notazione scientifica (con una mantissa e un esponente) come avviene nei Single e nei Double.
Svelato l'arcano mistero dei numeri negativi, passiamo ai vettori di variabili: spesso si ha necessità di utilizzare più di una variabile dello stesso tipo per effettuare una stessa operazione; ad esempio, per calcolare la media di 10 valori ho bisogno di sommare questi 10 valori e dividere la somma per 10: a tale scopo potrei utilizzare 10 variabili diverse, che naturalmente vanno dichiarate una per una, più o meno in questo modo:
Dim Var1 As Integer
Dim Var2 As Integer
...
Dim Var10 As Integer
Var1 = 1
Var2 = 44
Var = 13
...
Var10 = 32
Media = (Var1 + Var2 + Var3+ ... + Var10) / 10
E se a un certo punto dovessi aver bisogno di fare la media non più di 10 valori, ma di 20 valori, dovrei dichiarare altre 10 variabili, impostare il valore opportuno per ciascuna di esse, sommarle alle 10 dichiarate in precedenza, e infine dividere la somma per 20! E' chiaro che questo modo di procedere è a dir poco laborioso: in questi casi è estremamente conveniente utilizzare i vettori, ovvero un insieme di variabili dello stesso tipo e con lo stesso nome, distinguibili le une dalle altre solo tramite un indice.
La dichiarazione di un vettore di variabili si fa scrivendo tra parentesi il numero di elementi che deve avere il vettore: per esempio nel nostro caso potremmo scrivere:
Dim Var(10) As Integer
Avremo così a disposizione 10 variabili (anzi, in realtà 11 come spiegherò tra poco) di tipo Integer di nome Var: la prima variabile che ci interessa sarà Var(1), la seconda Var(2), ecc. fino alla decima, Var(10).
Nella dichiarazione, il numero indicato tra parentesi indica il limite superiore del vettore, ovvero il valore massimo che può assumere l'indice: se provassimo a scrivere, ad esempio:
var(11) = 1000
otterremmo un errore, perché il limite massimo dell'indice è 10.
Il limite superiore deve essere compreso nell'intervallo di valori valido per il tipo Long (-2147483648/+2147483647): da ciò segue che è possibile specificare come limite superiore anche un valore negativo, ma bisogna tenere presente un paio di cose.
Prima di tutto, così come c'è un limite superiore, c'è anche un limite inferiore per l'indice: se non è specificato nella dichiarazione, questo limite inferiore è per default 0; pertanto scrivendo:
Dim Var(10) As Integer
l'indice di questo vettore potrà andare da 0 a 10, e quindi il vettore avrà 11 elementi; se si desidera che l'indice parta sempre da 1 anziché da 0, è possibile utilizzare l'istruzione:
Option Base 1
Questa istruzione, come l'analoga Option Explicit va inserita nella sezione generale del form.
Però è anche possibile indicare entrambi i limiti, quello inferiore e quello superiore, nella dichiarazione stessa, usando la parola chiave To:
Dim Var(1 To 10) As Integer
indica un vettore con 10 elementi, il cui indice va da 1 a 10; analogamente si potrà scrivere:
Dim Var(10 To 20) As Long
oppure:
Dim Pippo(-5 To 32) As String
e così via. La seconda cosa da tenere presente è che, ovviamente, il limite inferiore deve sempre essere minore del limite superiore: perciò è sbagliato scrivere
Dim Var(10 To 0) As Integer
ed è anche sbagliato scrivere:
Dim Var(-5) As Integer
perché in questo caso il limite inferiore implicito è 0 o 1, che è maggiore di -5.
Quattordicesima lezione - Le matrici, il ciclo For...Next ed il campo minato passo per passo (a cura di Giorgio Abraini)
Oltre ai vettori è possibile utilizzare anche matrici, che non sono altro che vettori a più dimensioni (o meglio, i vettori sono matrici a una sola dimensione).
Ad esempio, supponiamo di voler memorizzare le dimensioni di 100 scatole: non ci basterà un vettore con 100 elementi, perché per ogni scatola avremo bisogno di 3 variabili per le 3 dimensioni (altezza, larghezza, profondità), pertanto dovremo utilizzare una matrice:
Dim Scatole(1 To 100, 1 To 3) As Integer
o anche:
Dim Scatole(99,2) As Integer
In entrambi i casi (se non avete inserito l'istruzione Option Base 1) avremo una matrice con 100 * 3 = 300 elementi, ovvero con 100 "righe" e 3 "colonne".
Per indicare che si vogliono usare più dimensioni, quindi, bisogna separare i limiti di ogni dimensione con una virgola.
E' possibile inoltre dichiarare una matrice dinamica, il cui numero di elementi può cambiare nel corso dell'esecuzione del programma: a questo scopo è però necessario usare una parola chiave apposita, ReDim, e dichiarare la matrice senza indicarne le dimensioni:
Dim Var() As Long 'dichiarazione della matrice: non è
'indicata la dimensione della matrice
e successivamente, in un altro punto del codice:
ReDim Var(120) As Long 'ridimensionamento della matrice
In tal caso è anche possibile utilizzare una variabile, anziché un'espressione costante, per indicare il limite inferiore e/o superiore della matrice, ad esempio:
ReDim Var(x) As Long
L'importante è ricordare che il ridimensionamento di una matrice riguarda esclusivamente il numero di elementi, non il loro tipo, né tantomeno il nome della matrice.
A proposito del tipo degli elementi, è interessante notare che se essi sono Variant possono contenere a loro volta altre matrici o vettori:
Dim Vettore(3) As Long
Dim MatriceVariant(5,2)
MatriceVariant(2,0) = Vettore
MatriceVariant(2,1) = 10
MatriceVariant(1,2) = "stringa"
Le istruzioni precedenti definiscono un semplice vettore di quattro elementi e una matrice di 18 (6 righe per 3 colonne) elementi Variant, ad uno dei quali è stato assegnato il nostro vettore: perciò l'elemento 2,0 della matrice non è semplicemente un numero, o una stringa, o un qualunque altro tipo elementare di dato, ma è esso stesso un vettore di 4 elementi.
Per accedere a uno dei quattro elementi di MatriceVariant(2,0) bisogna usare questa sintassi:
MatriceVariant(2,0)(i) 'i varia da 0 a 3
E' come se la matrice bidimensionale MatriceVariant avesse, nel caso dell'elemento 2,0 (e SOLO per quell'elemento), una terza dimensione data dai 4 elementi del nostro vettore iniziale.
Naturalmente il vettore e la matrice restano variabili (o meglio, insiemi di variabili) distinte e indipendenti, e per rendersene conto basta mettere alla prova queste semplici istruzioni:
Dim vettore(3) As Long
Dim MatriceVariant(5,2)
Vettore(2) = 10
MatriceVariant(1,1) = Vettore
Vettore(2) = 20
Debug.Print Vettore(2), MatriceVariant(1,1)(2)
Nella finestra di debug si leggeranno i valori 20 e 10, perché MatriceVariant(1,1) non risente delle modifiche successive all'assegnazione del vettore all'elemento della matrice.
Per utilizzare in modo efficiente i vettori non bisogna trattare gli elementi come se fossero variabili completamente distinte.
Supponiamo ad es. di voler inizializzare gli elementi di un vettore a un certo valore: non avrebbe molto senso scrivere:
Dim Vettore(2) As Long
Vettore(0) = 10
Vettore(1) = 10
Vettore(2) = 10
perché in tal caso sarebbe come utilizzare tre variabili v1, v2, v3, distinte e indipendenti le une dalle altre: in altre parole, non si sfrutterebbe la loro appartenenza a un solo vettore che le comprende tutte.
Talvolta è necessario agire in questo modo, ma spesso è molto più conveniente sfruttare la possibilità di accedere ad ogni elemento tramite il suo indice, e questo si fa usando i cicli.
Un ciclo non è altro che un gruppo di istruzioni che vengono eseguite ripetutamente, ovvero ciclicamente, fino a che una certa condizione risulta verificata.
Di cicli ne esistono vari tipi in Visual Basic, ma quello in assoluto più utilizzato è il ciclo For...Next.
Questo ciclo è identificato da due istruzioni, poste rispettivamente all'inizio e alla fine del ciclo:
For contatore = inizio To fine
...
Next contatore
dove contatore indica una variabile numerica che "conta" quante volte devono essere eseguite le istruzioni comprese nel ciclo: essa assume un valore iniziale (inizio) e viene incrementata dall'istruzione Next fino a raggiungere il valore finale (fine).
Approfondiremo più avanti il discorso del ciclo For...Next e degli altri cicli: per ora accontentatevi di sapere che il ciclo viene eseguito fintantoché il valore di contatore è minore o uguale a fine; il nostro esempio precedente risulterebbe trasformato così:
Dim Vettore(2) As Long
Dim i As Integer 'contatore per il ciclo
For i = 0 To 2
Vettore(i) = 10
Next i
Alla prima esecuzione del ciclo i assume valore 0 e quindi il primo elemento del vettore è inizializzato a 10.
Alla seconda esecuzione i è uguale a 1 ed è inizializzato il secondo elemento. Alla terza esecuzione i è uguale a 2 ed è inizializzato il terzo elemento; dopodiché i supera il valore 2 e quindi il ciclo termina. Torniamo alle variabili Variant: queste possono contenere, tra le altre cose, anche riferimenti ad oggetti, e in particolare riferimenti a controlli; è possibile quindi creare vettori o matrici di controlli. Per illustrare meglio questo concetto, realizziamo un altro progetto di esempio: questa volta proviamo a replicare uno dei giochi di Windows più famosi, il "Campo minato".
Cominciamo quindi con l'aprire un nuovo progetto "exe standard", e inseriamo nel form un pulsante: ridimensioniamolo fino a farlo diventare un quadrato abbastanza piccolo, cancelliamo la proprietà caption (in realtà questa proprietà non è vuota, ma contiene la stringa nulla) e assegniamo la stringa "Mina" alla proprietà name.
Ora selezioniamo il nostro pulsante, copiamolo andando sul menù Modifica o semplicemente premendo ctrl+c, e infine incolliamolo sullo stesso form andando ancora sul menù Modifica o premendo : apparirà un messaggio di questo tenore:
Noi diciamo di Sì, e com'era prevedibile un nuovo pulsante uguale al primo sarà inserito nel form.
In realtà i due pulsanti non sono proprio uguali: ciò che cambia è la proprietà Index.
Prima di copiare e incollare il nostro pulsante, la sua proprietà Index era vuota: è ovvio che sia così, perché questa proprietà, secondo la guida, "identifica un controllo in una matrice di controlli", e quando il pulsante era ancora da solo non c'era alcuna matrice di controlli.
Ora che abbiamo incollato un nuovo pulsante, invece, la matrice di controlli c'è, e pertanto è necessario distinguere i vari pulsanti per mezzo del loro indice: questo indice ha esattamente la stessa funzione dell'indice dei vettori di variabili, solo che ora al posto delle variabili ci sono dei controlli.
Continuiamo a incollare i pulsanti fino ad averne, ad esempio, 16 disponendoli in quadrato: non è indispensabile ma è molto meglio se li disponete in modo che l'indice segua un ordine logico, ad esempio procedendo da sinistra verso destra riga per riga.
Così nella prima riga ci saranno i pulsanti con indice 0,1,2,3, nella seconda quelli con indice 4,5,6,7, e così via.
Ora aggiungiamo due etichette, da mettere nella parte alta del form: lblMine e lblTempo (se volete, impostate la proprietà Alignment a "right"); infine aggiungiamo un menù "Partita" con le voci "Nuova" (il nome sarà "mnuNew") e "Esci" ("mnuExit"), separate da una barra orizzontale: per fare questa barra sarà sufficiente mettere come caption un solo trattino "-" e nient'altro; naturalmente le voci "Nuova", "Esci" e la linea di separazione devono essere gerarchicamente subordinati rispetto al menù "Partita" (v.anche la lez. 9). Resta ancora da inserire un timer, che trovate naturalmente sulla casella degli strumenti (è quello con un orologio come icona): potete metterlo dove volete, tanto risulta invisibile in fase di esecuzione; ora il nostro form è pronto, dobbiamo solo scrivere il codice per far funzionare il gioco, e lo faremo nelle prossime lezioni.
Quindicesima lezione - Campo Minato: il controllo Timer, le funzioni Rnd e Int(), le istruzioni Randomize e GoTo e la proprietà Tag (a cura di Giorgio Abraini)
Cominciamo dall'etichetta lblTempo, che dovrà misurare il trascorrere del tempo di gioco: la cosa più ovvia è aggiornarla ogni secondo, quindi dobbiamo impostare correttamente le proprietà del Timer; questo controllo misura evidentemente il trascorrere del tempo generando un evento Timer ogni volta che passano tot millisecondi.
Il numero di millisecondi trascorsi i quali viene generato l'evento è indicato dalla proprietà Interval: dato che noi vogliamo contare i secondi, sarà bene impostare questa proprietà a 1000 ms, cioè 1 secondo; in parole povere, quando il timer viene attivato comincerà a contare i millisecondi: quando arriva a 1000 genera l'evento Timer, reimposta a 0 il contatore e ricomincia a contare fino ad arrivare nuovamente a 1000, generando un altro evento Timer; e così via.
Il controllo Timer non deve essere attivo subito, ma deve cominciare a contare solo quando l'utente darà il via alla partita: quindi nella finestra delle proprietà impostate Enabled a false.
Cliccando due volte sul Timer, comparirà l'editor del codice sulla routine dell'evento Timer: qui dobbiamo inserire le istruzioni per aggiornare l'etichetta; banalmente, sarà sufficiente dichiarare una variabile statica che funzioni da contatore e scriverne il valore nell'etichetta:
Static i As Integer
i = i + 1
lblTempo.Caption = CStr(i)
La variabile i va dichiarata come Static perché deve tener conto dei secondi passati anche "al di fuori" della routine "timer1_timer": se non si usa la parola chiave Static, la variabile verrà creata ogni volta che l'evento timer viene generato, e di conseguenza assumerà sempre il valore zero.
Il timer, come ho detto, dovrà essere attivato quando comincia una nuova partita, quindi nella routine mnuNew_click bisognerà scrivere:
Timer1.Enabled = True
e corrispondentemente esso dovrà essere disattivato quando la partita finisce: ce ne occuperemo tra poco.
Ora viene la parte più difficile, scrivere il codice associato ai pulsanti, che costituiscono il gioco vero e proprio: innanzitutto bisogna decidere dove piazzare le mine; visto che le caselle sono 16, potremmo piazzare 3 mine.
Naturalmente non possiamo decidere noi direttamente dove metterle, ma dovremo affidarci al caso, usando quindi i numeri casuali: per generare i numeri casuali esiste una funzione, Rnd, che restituisce un numero (più o meno) casuale compreso tra 0 (incluso) e 1 (escluso), ovvero tra 0 e 0,99999.
Noi però dobbiamo decidere sotto quale pulsante mettere la mina, quindi dobbiamo avere un numero che ci dica qual è l'indice del pulsante in questione; e dal momento che i pulsanti sono 16, il loro indice andrà da 0 a 15, e pertanto abbiamo bisogno di un numero casuale compreso tra 0 e 15: ottenerlo è banalissimo, basta infatti moltiplicare il risultato della funzione Rnd per 16, e prenderne la parte intera:
Int(16 * Rnd)
La funzione Int() non fa altro che troncare la parte decimale di un numero restituendo solo la parte intera: se ad esempio Rnd restituisce 0.1, 16 * 0.1 = 1.6 e quindi int(1.6) = 1; visto che Rnd restituisce sempre un numero minore di 1, il prodotto 16 * Rnd sarà sempre minore di 16, ovvero compreso tra 0 e 15: proprio ciò che ci serve.
Dato che abbiamo bisogno di 3 mine, ci conviene fare un vettore di tre elementi, anziché dichiarare tre variabili distinte; quindi, nella sezione delle dichiarazioni del form, scriviamo:
Dim PosMine(2) As Integer
La posizione delle mine dobbiamo deciderla all'inizio di ogni partita: quindi in mnuNew_click, scriviamo:
Dim i As Integer 'contatore per il ciclo
Randomize Timer
For i = 0 To 2
PosMine(i) = Int(Rnd * 16)
Next i
L'istruzione Randomize serve ad inizializzare il generatore di numeri casuali: come saprete, i numeri "casuali" generati da un qualunque computer non sono veramente casuali, poiché sono generati in modo deterministico, seguendo regole ben precise; solo che queste regole sono tali da garantire, entro certi limiti, una sorta di casualità nei numeri generati, che per questo vengono definiti più correttamente "pseudo-casuali".
Ora, la funzione rnd calcola i numeri pseudo-casuali a partire da un numero base, che viene modificato dall'istruzione Randomize tramite il suo argomento: nel nostro caso questo argomento è il valore restituito dalla funzione Timer, che restituisce il numero di secondi trascorsi dalla mezzanotte del giorno corrente (la funzione Timer non c'entra nulla con il controllo Timer, né tantomeno con l'evento Timer); dato che questo valore è altamente aleatorio, anche il numero base utilizzato dalla funzione rnd cambia in modo abbastanza casuale, e quindi tale funzione calcola numeri un po' più casuali di quanto accadrebbe se si utilizzasse rnd senza Randomize.
Ora che abbiamo le posizioni delle mine, potremmo controllare, ogni volta che il giocatore preme un pulsante, se l'indice di quel pulsante appartiene al vettore PosMine: questo è un metodo alquanto laborioso e inefficiente, e sarebbe molto meglio se ogni pulsante sapesse già se sotto di esso si nasconde una mina oppure no. Per fare questo è sufficiente sfruttare una proprietà comune a quasi tutti i controlli, la proprietà Tag, che consente di memorizzare dati aggiuntivi utili per la funzionalità del controllo: nel nostro caso, ad esempio, potremmo associare ai pulsanti che nascondono una mina la stringa "mina", assegnandola alla proprietà Tag; quando il giocatore preme il pulsante, basterà controllare se la proprietà contiene la stringa "mina" per sapere se il gioco è terminato o se può continuare.
Ancora meglio, visto che le possibilità sono solo due (la mina c'è o non c'è), potremmo assegnare alla proprietà Tag direttamente un valore booleano che indichi l'eventuale presenza della mina; in altre parole, potremmo scrivere, all'interno del ciclo descritto sopra:
Mina(PosMine(i)).Tag = True
C'è però da considerare un'altra cosa: sarebbe comodo che ogni pulsante sapesse non solo se nasconde o meno una delle mine, ma anche quante altre mine ci sono sotto i pulsanti che lo circondano; in tal caso, nella proprietà Tag dovremmo scrivere il numero di mine circostanti ogni pulsante e, se una mina si trova proprio sotto il pulsante considerato, dovremmo assegnare un valore particolare. Ad es., se intorno al pulsante x ci sono due mine, potremmo scrivere x.Tag = 2; se intorno ad esso ce n'è una sola, scriveremmo x.Tag = 1; ma se la mina si trova proprio sotto x, non possiamo scrivere di nuovo x.Tag = 1, altrimenti si farebbe confusione: possiamo però scrivere x.Tag = -1, perché il numero di mine circostanti non può essere negativo, e quindi se la proprietà Tag contiene il valore -1, il pulsante saprà che la mina è sotto di lui, e non sotto qualche altro pulsante che lo circonda.
Un pericolo di cui una persona inesperta si accorgerebbe difficilmente è che diversi elementi del vettore PosMine potrebbero assumere valori uguali: questo dipende banalmente dal fatto che noi prendiamo in considerazione non l'intero numero casuale (moltiplicato per 16), ma solo la sua parte intera; se ad esempio la funzione rnd restituisse consecutivamente i valori 0.19 e 0.24, risulterebbe che Int(16 * 0.19) = Int(16 * 0.24) = 3, con la conseguenza che le nostre mine sarebbero in realtà due e non tre, perché due dei tre valori calcolati coincidono.
Per evitare questo rischio, è sufficiente controllare che il numero casuale appena estratto sia diverso da quelli già memorizzati: il modo più naturale di eseguire questo controllo sarebbe utilizzare un altro ciclo (ma di tipo diverso rispetto al For...Next), e siccome non ne ho ancora parlato, preferisco usare un metodo meno ortodosso ma di più semplice comprensione; si tratta di riscrivere la routine mnuNew_click in questo modo:
Dim t As Integer 'variabile temporanea per eseguire i controlli
Randomize Timer
PosMine(0)= Int(Rnd * 16)
Mina(PosMine(0)).Tag = -1
'Estrai:
t = Int(Rnd * 16)
If t = PosMine(0) Then
GoTo Estrai
Else
PosMine(1) = t
Mina(PosMine(1)).Tag = -1
End If
'EstraiDiNuovo:
t = Int(Rnd * 16)
If t = PosMine(0) Or t = PosMine(1) Then
Goto EstraiDiNuovo
Else
PosMine(2) = t
Mina(PosMine(2)).Tag = -1
End if
Quello che succede è abbastanza intuitivo: si estrae un numero casuale e lo si memorizza in PosMine(0); se ne estrae un altro e si controlla che sia diverso da quello precedente: se è diverso, lo si memorizza nell'elemento successivo del vettore (PosMine(1)), altrimenti se ne estrae un altro; la riestrazione è ottenuta attraverso l'istruzione Goto, che ordina al computer di interrompere la normale esecuzione del programma saltando a un altro punto del programma stesso, e precisamente al punto indicato da un'apposita etichetta.
Nel nostro caso le etichette sono due: "Estrai" e "EstraiDiNuovo"; la sintassi vuole che il nome delle etichette non abbia alcuno spazio al suo interno e sia seguito dai due punti ":". La riestrazione continua finchè non si trova un numero diverso da quello memorizzato in PosMine(0).
Poi si passa alla terza e ultima estrazione, che segue lo stesso schema logico, controllando questa volta che il numero casuale sia diverso da entrambi i valori precedentemente memorizzati, ovvero PosMine(0) e PosMine(1), grazie all'operatore logico Or.
Quindicesima lezione - Campo Minato: algoritmi, diagrammi di flusso, l'operatore Mod, la funzione IIf, il parametro Index (a cura di Giorgio Abraini)
Ricapitolando, ora conosciamo le posizioni delle mine, e ogni pulsante sa, grazie alla proprietà tag, se nasconde una mina oppure no; ora dobbiamo scrivere le istruzioni per il gioco vero e proprio: cosa succede quando il giocatore preme un pulsante?
Quello che succede è molto semplice: se sotto il pulsante premuto si trova una mina, il gioco termina; altrimenti viene visualizzato il numero di mine circostanti quel pulsante, come mostra la figura sotto:
bisognerà quindi effettuare un controllo del genere:
If Mina(i).Tag = -1 Then
'il gioco è finito!
Else
'il gioco continua
End If
Prima di tutto, però, bisogna calcolare quante mine circondano ogni pulsante: questa operazione è opportuno farla all'inizio della partita, in modo che il calcolo sia effettuato una volta sola; quindi dobbiamo aggiornare la routine mnuNew_Click.
A questo punto dobbiamo escogitare un algoritmo semplice ed efficiente per fare questi calcoli: per chi non lo sapesse, un algoritmo è una sequenza di operazioni che permettono di ottenere un generico obiettivo; la sequenza con cui queste operazioni devono essere eseguite è determinata dal verificarsi di certe condizioni, e il controllo di queste condizioni è parte integrante dell'algoritmo.
Solitamente si usa un grafico (il cosiddetto diagramma di flusso) per descrivere un algoritmo (ovvero il flusso delle operazioni), ma qui preferisco usare un elenco numerato.
Un algoritmo per calcolare il numero di mine circostanti un pulsante potrebbe essere questo:
1) prendo in considerazione un pulsante (chiamiamolo "x");
2) prendo in considerazione tutti i pulsanti che lo circondano, uno alla volta (chiamiamo questo pulsante "y");
3) se y nasconde una mina, incremento un contatore c, altrimenti non faccio nulla;
4) esamino il pulsante successivo tra quelli che circondano x, e torno al punto 2); se ho esaminato tutti i pulsanti che circondano x, assegno x.Tag = c e reimposto c = 0 (altrimenti c si ricorderebbe dei valori relativi ad altri pulsanti);
5) esamino il pulsante successivo a x e torno al punto 1; se ho esaminato tutti i pulsanti, ho terminato l'algoritmo.
In parole povere, questo algoritmo descrive due cicli annidati tra loro: il primo esamina tutti i nostri 16 pulsanti, uno alla volta; il secondo, quello più interno, esamina tutti i pulsanti che circondano quello preso in esame dal primo ciclo; all'interno del secondo ciclo viene aggiornato il contatore delle mine.
C'è però un altro algoritmo, che mi sembra più semplice ed efficiente: visto che sappiamo che le mine sono in tutto 3, non c'è alcun bisogno di fare due cicli per andare a cercare i pulsanti che nascondono una mina; noi sappiamo dove sono le mine, e quindi ci basterà aggiornare le proprietà Tag dei pulsanti che circondano quelli che nascondono una mina.
L'algoritmo è questo:
1) inizializzo le proprietà tag di tutti i pulsanti a 0;
2) prendo in esame il pulsante con indice PosMine(i) (chiamiamolo "x"; inizialmente i = 0);
3) pongo x.tag = -1 e aggiorno le proprietà tag di tutti i pulsanti che circondano x;
4) se i = 2 termino l'algoritmo, altrimenti pongo i = i + 1 e torno al punto 2.
Tradotto in codice, questo algoritmo diventerebbe così (ovviamente va aggiunto alla routine mnuNew_Click):
Dim i As Integer 'contatore
Dim x As Integer, y As Integer 'coordinate del pulsante con la mina
Dim x1 As Integer, y1 As Integer 'coordinate dei pulsanti circostanti 'quello con la mina
For i = 0 To 15
Mina(i).Tag = 0
Next i
For i = 0 To 2
Mina(PosMine(i)).Tag = -1
x = Int(PosMine(i) / 4) 'riga in cui si trova la mina
y = PosMine(i) Mod 4 'colonna in cui si trova la mina
For x1 = IIf(x = 0, 0, x - 1) To IIf(x = 3, 3, x + 1)
For y1 = IIf(y = 0, 0, y - 1) To IIf(y = 3, 3, y + 1)
If Mina(4 * x1 + y1).Tag > -1 Then
Mina(4 * x1 + y1).Tag = Mina(4 * x1 + y1).Tag + 1
End If
Next y1
Next x1
Next i
Timer1.Enabled = True
Il primo ciclo è quello di inizializzazione; il secondo appare più complicato di quanto sia in realtà: innanzitutto viene aggiornato il Tag del pulsante con la mina, poi vengono calcolate le coordinate di questo pulsante; questa operazione è opportuna perché i pulsanti sono disposti graficamente come una matrice bidimensionale 4 x 4, mentre nel codice sono "allineati" in un vettore unidimensionale.
Prendiamo ad esempio il decimo pulsante, quello con indice 9: dato che ogni riga è di quattro elementi, per sapere su quale riga e colonna si trova bisogna sottrarre dall'indice il multiplo di 4 immediatamente precedente all'indice stesso (in questo caso 8): la differenza (9 - 8 = 1) indica la colonna, mentre questo multiplo diviso per 4 indica la riga (8 / 4 = 2).
La colonna viene ottenuta tramite l'operatore Mod, che restituisce il resto intero di una divisione: ad esempio, 9 Mod 4 = 1 perché 9 / 4 è uguale a 2 col resto di 1; ovviamente 8 Mod 4 = 0, così come 10 Mod 5, 6 Mod 2, 3 Mod 3 ecc.
Per analogia con l'indice dei pulsanti, anche le righe e le colonne le contiamo a partire da 0, quindi il pulsante con indice 9 si troverà sulla riga 2 colonna 1, ovvero il decimo pulsante si trova sulla terza riga, seconda colonna.
Ottenute le coordinate, è più semplice controllare i pulsanti che circondano la mina.
Avrete notato una nuova funzione nei cicli più interni: la funzione IIf; questa funzione è simile ad una if "concentrata": i tre argomenti indicano rispettivamente la condizione da verificare, il risultato da restituire se la condizione è vera, il risultato da restituire se la condizione è falsa; in altri termini, IIf(x=0, 0, x-1) equivale a:
If x = 0 Then
risultato = 0
Else
risultato = x - 1
End If
Questo controllo è necessario perché, se è x = 0, cioè se la mina si trova sulla prima riga, i pulsanti circostanti partono anch'essi dalla prima riga e non dalla riga precedente come avverrebbe negli altri casi, semplicemente perché non esiste una riga precedente alla prima; lo stesso dicasi per l'ultima riga
IIf(x = 3, 3, x + 1))
e per la prima e ultima colonna.
Nel ciclo più interno ho introdotto un altro controllo per verificare che il pulsante controllato NON sia quello che nasconde la mina: infatti i due cicli analizzano tutti i pulsanti che circondano la mina, compreso quello che nasconde la mina stessa, e quest'ultimo non deve essere modificato.
Per non parlare della possibilità di avere due mine adiacenti. Infine, la proprietà Tag viene aggiornata incrementando di 1 il valore della stessa proprietà.
Questo per consentire un'indicazione corretta nel caso le mine circostanti un determinato pulsante siano 2, oppure 3.
L'ultima istruzione serve per abilitare il Timer, in modo da poter misurare i secondi di gioco. Ora possiamo finalmente scrivere il codice da eseguire quando il giocatore preme un pulsante: si tratta semplicemente di controllare il valore della proprietà Tag; nella routine Mina_Click noterete la presenza del parametro Index: questo è ovviamente l'indice del pulsante premuto dall'utente, ed è stato aggiunto automaticamente da Visual Basic quando abbiamo deciso di creare la matrice di pulsanti. In questa routine dunque scriviamo:
Dim x As Integer, y As Integer 'coordinate del pulsante premuto
Dim x1 As Integer, y1 As Integer 'coordinate dei pulsanti circostanti 'quello premuto
If Mina(Index).Tag > 0 Then
Mina(Index).Caption = Mina(Index).Tag
ElseIf Mina(Index).Tag = -1 Then
For x = 0 To 2
Mina(PosMine(x)).Caption = "M"
Next x
Timer1.Enabled = False
Else 'mina(index).tag=0
x = Int(Index / 4) 'riga in cui si trova la mina
y = Index Mod 4 'colonna in cui si trova la mina
For x1 = IIf(x = 0, 0, x - 1) To IIf(x = 3, 3, x + 1)
For y1 = IIf(y = 0, 0, y - 1) To IIf(y = 3, 3, y + 1)
Mina(4 * x1 + y1).Caption = Mina(4 * x1 + y1).Tag
Next y1
Next x1
End If
Il significato di questo codice dovreste essere in grado di capirlo da soli, tenendo conto di come funziona il vero "Campo Minato". Comunque la prossima volta lo spiegherò in dettaglio
Corso di Visual Basic: approfondimento sul controllo 'Barra di avanzamento'
Il controllo Barra di avanzamento consente di visualizzare graficamente l'avanzamento nel tempo di un determinato processo.
Quando si installa un'applicazione infatti viene mostrata una barra blu che mostra (unitamente alla percentuale) a che punto si trova il processo di installazione. La figura qui sotto ne è un esempio:
Il processo in cui utiilzzare il controllo può essere di diversa natura comunque è solitamente un'operazione piuttosto lunga: non sarebbe infatti particolarmente utile far conoscere all'utente lo stato dell'operazione se questa si risolvesse in pochi secondi.
Alcuni esempi possono essere, oltre la già citata installazione di applicazioni, gestita però solitamente da un programma apposito, la lettura di file di grosse dimensioni o l'inserimento in essi di una grande quantità di dati.
Oppure il controllo può essere utilizzato in attesa di compiere un'operazione su un'immagine (rotazione, trasformazione geometrica e così via).
In tutti questi casi è però necessario conoscere il tempo massimo utilizzato dal processo poichè ogni punto raggiunto dalla barra di avanzamento non è che una proporzione tra il totale delle operazioni da compiere (ad esempio lettura di tutti i dati da un file) e l'attuale operazione in corso (lettura di un dato specifico).
Se infatti teniamo presente l'esempio della lettura di informazioni da un file, se consideriamo TOT il totale dei dati da leggere (ad esempio TOT=140) e DATO il dato che l'applicazione sta leggendo correntemente (ad esempio il 21esimo), in una scala che va da 0% a 100%, saremo in una posizione 100 : 140 = x : 21 => x= (100*21)/140 = 15%.
Infatti se 15% è 21 allora 100% è 21 * 6,6 periodico = 139.9999999 periodico
Per poter utilizzare il controllo barra di avanzamento (o barra di progresso) in un'applicazione è necessario aprire la finestra Componenti, cliccando sul menu principale la voce 'Progetto' e selezionando l'opzione 'Componenti'.
Si aprirà una finestra dalla quale scegliere i componenti (ossia gli oggetti di immediato utilizzo sviluppati da altri) da importare nel progetto. La barra di avanzamento è compresa nel gruppo di componenti denominato 'Microsoft Windows Common Controls X.0' dove X indica il numero di versione di Visual Basic di cui si dispone:
Il valore minimo di una barra di avanzamento è espresso dalla proprietà Min:
ProgressBar1.Max = valore_minimo
mentre il valore massimo sarà espresso dalla proprietà Max.
ProgressBar1.Max = valore_massimo
Se ad esempio si imposta Min = 0 e Max = 100 si potrà utilizzare una scala 0-100 molto comoda per il calcolo della percentuale che indica lo stato dell'avanzamento.
E tale stato di avanzamento viene descritto dalla proprietà Value:
ProgressBar1.Max = valore_corrente
Il valore corrente è valido sia in lettura che in scrittura cioè è possibile sia impostarlo sia leggerlo semplicemente.
Ad esempio se Min = 0 e Max = 100 e Value = 50 allora il controllo si posizionerà sul 50% cioè a metà della barra:
mentre se si utilizza una scala differente, ad esempio Min = 0 e Max = 140, il valore 50 sarà poco prima di metà se la scala è più ampia:
o poco dopo metà se la scala ha un intervallo di valori minore.
Un altro parametro di cui tenere conto è la lunghezza del controllo, impostabile grazie alla proprietà Width:
ProgressBar1.Width = lunghezza
Un controllo più lungo aumenta la precisione nella rappresentazione dell'avanzamento: se infatti si imposta una lunghezza tale per cui vengono visualizzati solamente due blocchi, il controllo mostrerà solamente le fasi 0%-50% e 51%-100%.
Naturalmente ci si accorge a prima vista della differenza di visualizzazione tra la barra di avanzamento mostrata nella prima immagine e quella a blocchi delle figure qui sopra.
Una barra a blocchi è solo una delle impostazioni che possono essere assegnate al controllo: si può infatti decidere di utilizzare una barra continua utilizzando la proprietà Scrolling:
ProgressBar1.Scrolling = ccScrollingSmooth
caso in cui si otterrà un effetto simile a quello mostrato nell'immagine sotto:
oppure di ritornare ad una barra a blocchi:
ProgressBar1.Scrolling = ccScrollingStandard
Anche il verso del controllo può essere modificato in modo da ottenere l'effetto che si preferisce attraverso la proprietà Orientation. Le impostazioni sono: orizzontale, caso in cui la barra di progresso viene visualizzata come una barra orizzontale, proprio come mostrato nelle immagini precedenti, oppure verticale, caso in cui il controllo verrà visualizzato dall'alto al basso:
Corso di Visual Basic: esercizi svolti
In questa pagina verranno proposti alcuni esercizi per prendere maggiore confidenza con i concetti esaminati durante il corso.
Un'attento studio di questi esercizi a difficoltà crescente può rivelarsi molto utile per mettere a punto tecniche di programmazione sempre più efficaci.
Esercizio 1:
1) Visualizzare all'avvio di Form1 una finestra di messaggio con il testo "Vuoi proseguire?" dotata di pulsanti come mostra la seguente figura:
2) alla pressione del pulsante Si l'utente deve poter visualizzare una seconda finestra di messaggio con il testo "Hai proseguito". Alla pressione del pulsante 'No' l'applicazione dovrà essere chiusa ed alla pressione di 'Annulla' la finestra deve scomparire ma l'applicazione non deve chiudersi.
Soluzione:
Siccome la finestra di messaggio deve essere visualizzata non appena l'applicazione viene avviata è necessario inserire la funzione MsgBox che mostra tale finestra, nella sottoprocedura Form_Load che contiene le istruzioni eseguite dal programma alla sua apertura.
La funzione MsgBox è composta di cinque parametri dei quali solitamente ne vengono utilizzati tre: il testo da visualizzare (nel nostro caso "Vuoi proseguire?", una o più costanti che adesso vedremo ed infine il nome dell'applicazione che può essere indicato o meno (la figura sopra è stata ottenuta senza indicare il nome dell'applicazione così Visual Basic ha assegnato il nome "Progetto1").
La serie di costanti da indicare specifica infine il tipo di finestra di messaggio: vbYesNo include due pulsanti: 'Si' e 'No', vbOkCancel include 'Ok' e 'Annulla' e così via. Ma possono essere impostate anche le icone che appaiono sulla sinistra indicando ad esempio vbCritical oppure vbInformation. E'utile quindi sperimentare uno dopo l'altro queste costanti.
Inoltre è possibile unire le costanti attraverso l'operatore '+': ad esempio vbYesNo & vbCritical visualizza una finestra con i pulsanti 'Si' e 'No' con l'icona della x bianca su sfondo rosso (che corrisponde ad un errore critico).
Per ottenere il risultato della figura sopra bisognerà quindi indicare:
Private Sub Form_Load()
MsgBox "Vuoi proseguire?", vbYesNoCancel + vbExclamation
End Sub
Adesso bisogna considerare la gestione dei vari casi 'pressione dei tasti'. Per far questo si deve assegnare ad una variabile (di tipo Integer) il risultato della funzione. Questa variabile indicherà quale tasto ha premuto l'utente. Con l'assegnazione ad una variabile del risultato di una funzione, si deve racchiudere quest'ultima entro parentesi tonde:
Private Sub Form_Load()
Pulsante_Premuto = MsgBox("Vuoi proseguire?", vbYesNoCancel + vbExclamation)
End Sub
E adesso la gestione dei casi vera e propria: risulta più utile utilizzare una forma del tipo Select Case ma siccome non è stata ancora introdotta nel corso si può vedere come gestire la situazione con If...End If: Diciamo che se l'utente ha premuto il pulsante 'Si' (la costante che indica solo il pulsante 'Si' è vbYes) si visualizza una seconda finestra di messaggio col testo "Hai proseguito" questa volta senza indicare la funzione MsgBox tra parentesi tonde in quanto non c'interessa sapere quale pulsante ha premuto l'utente su questa seconda finestra di tipo semplice:
If Pulsante_Premuto = vbYes Then
MsgBox "Hai proseguito"
End If
Il secondo caso è la pressione del tasto 'No': utilizziamo allora la costante vbNo ed inseriamo un End per chiudere completamente l'applicazione:
If Pulsante_Premuto = vbNo Then
End
End If
ultimo caso: pressione del tasto 'Annulla' (costante vbCancel). L'applicazione rimane attiva ma non succede niente; semplicemente usciremo dalla sottoprocedura in modo indolore:
If Pulsante_Premuto = vbCancel Then
Exit Sub
End If
Codice completo per questo primo esercizio:
Private Sub Form_Load()
Pulsante_Premuto = MsgBox("Vuoi proseguire?", vbYesNoCancel + vbExclamation)
If Pulsante_Premuto = vbYes Then
MsgBox "Hai proseguito"
End If
If Pulsante_Premuto = vbNo Then
End
End If
If Pulsante_Premuto = vbCancel Then
Exit Sub
End If
End Sub
Esercizio 2:
1) Alla pressione di un pulsante denominato Command1 visualizzare la finestra CommonDialog Apri che visualizzi tutti i file di testo .txt e visualizzare in una finestra di messaggio il nome del file aperto:
Soluzione:
Prima di tutto bisogna importare il controllo Microsoft CommonDialog x.0 attraverso la finestra 'Componenti' cliccando su Progetto nella barra menu principale e selezionando la voce Componenti:
In secondo luogo si dovrà importare sul piano un controllo CommandButton. Una volta eseguite queste due operazioni nella sottoprocedura che individua l'evento 'pressione del pulsante CommandButton1' ossia Command1_Click si dovrà richiamare la finestra 'Apri' utilizzando il metodo ShowOpen dell'oggetto CommonDialog:
Private Sub Command1_Click()
CommonDialog1.ShowOpen
End Sub
Adesso ci si deve preoccupare dei filtri che permettano di visualizzare (e quindi di aprire) un solo tipo di file. Per questo esempio ci servirà impostare un filtro per l'estensione .txt. Questo viene fatto attraverso la proprietà Filter del controllo CommonDialog. E' però necessario specificare il filtro prima di aprire la finestra 'Apri' col metodo ShowOpen:
Private Sub Command1_Click()
CommonDialog1.Filter = "Documenti di testo (*.txt)|*.txt"
CommonDialog1.ShowOpen
End Sub
La parte prima del simbolo "|" indica il testo che viene visualizzato nella casella 'Tipo file' mentre la seconda è il filtro vero e proprio.
E' inoltre possibile indicare vari tipi di file ad esempio quelli di testo e quelli con estensione .doc:
CommonDialog1.Filter = "Documenti di testo (*.txt)|*.txt|Documenti Word (*.doc)|*.doc"
Per visualizzare il nome del file si dovrà utilizzare la proprietà FileName all'interno di una funzione MsgBox:
MsgBox CommonDialog1.FileName
Codice completo per questo esercizio:
Private Sub Command1_Click()
CommonDialog1.Filter = "Documenti di testo (*.txt)|*.txt"
CommonDialog1.ShowOpen
MsgBox CommonDialog1.FileName
End Sub
Esercizio 3:
1) Dichiarare una matrice di 10 variabili alle quale assegnare con un unico ciclo For...Next un valore pari al doppio di quello della variabile precedente ad esempio: Variabile1 = 2, Variabile2 = 4, Variabile3 = 6 eccetera.
2) Visualizzare in una finestra di messaggio la media delle variabili.
Soluzione:
Dichiariamo prima di tutto la variabile che conterrà il valore medio e la matrice di variabili:
Private Sub Form_Load()
Dim TotVar As Integer 'il valore medio
Dim Variabile(1 To 10) As Integer 'la matrice non dinamica
End Sub
Quindi per i che va da 1 all'elemento massimo della matrice (definito con la funzione Ubound), assegnamo il valore alla variabile i:
For i = 1 To UBound(Variabile())
Variabile(i) = i * 2
ed aggiorniamo la variabile TotVar aggiungendo il valore dell'i-esima variabile, chiudendo il ciclo con un Next i:
TotVar = TotVar + Variabile(i)
Next i
e finalmente si può visualizzare la media:
MsgBox TotVar / UBound(Variabile())
Codice completo per questo esercizio:
Private Sub Form_Load()
Dim TotVar As Integer 'il valore medio
Dim Variabile(1 To 10) As Integer 'la matrice non dinamica
For i = 1 To UBound(Variabile())
Variabile(i) = i * 2
TotVar = TotVar + Variabile(i)
Next i
MsgBox TotVar / UBound(Variabile())
End Sub
Diciassettesima lezione - Campo Minato: gli ultimi accorgimenti (a cura di Giorgio Abraini)
Nell'ultima lezione vi avevo lasciato con la routine che gestisce il click sui pulsanti: quando viene premuto un pulsante, bisogna innanzitutto verificare se esso nasconde una mina oppure no, e per questo bisogna controllare la sua proprietà Tag. Se essa è maggiore di 0, significa che sotto di esso non ci sono mine e bisogna mostrare al giocatore quante mine circondano quel pulsante.
E' quello che fa la prima diramazione della If tramite l'aggiornamento della proprietà Caption. Se il Tag non è maggiore di 0, potrebbe essere uguale a -1: in tal caso, il pulsante nasconde una mina e lo facciamo capire all'utente visualizzando una "M" sul pulsante; il vero Campo Minato mostra l'icona della mina, ma noi per semplicità ci accontentiamo di modificare opportunamente la Caption dei pulsanti.
Trovata la mina, il gioco deve terminare e quindi interrompiamo il conteggio dei secondi disabilitando il Timer e mostrando al giocatore la posizione di tutte le mine: per questo motivo con un ciclo aggiorniamo la Caption di tutti i pulsanti con le mine, e non solo di quella trovata dal giocatore. La variabile contatore utilizzata nel ciclo è x, che di per sé dovrebbe essere usata per calcolare la riga in cui si trova il pulsante premuto: volendo essere diligenti, avremmo dovuto dichiarare un'altra variabile apposita, ma dato che l'utilizzo di x viene comodo e non crea problemi con le altre diramazioni della If, possiamo benissimo utilizzarla risparmiando così qualche byte. La terza diramazione della If serve per l'ultimo caso, quello in cui
mina(Index).Tag = 0
Quando ciò si verifica, Campo Minato mostra tutta la "frontiera" dei pulsanti che si trovano nelle vicinanze di una mina.
Per ottenere lo stesso risultato, noi dovremmo usare una funzione ricorsiva (cioè una funzione che chiama se stessa un numero indefinito di volte) con opportuni controlli per evitare di bloccare l'applicazione, e siccome tutto ciò potrebbe rivelarsi complesso, per ora ci accontentiamo di mostrare il numero di mine vicine ai pulsanti che circondano quello premuto.
Ora il nostro Campo Minato è quasi pronto: possiamo già fare una prima partita, avviate il progetto, selezionate "Nuova" dal menù "Partita" e premete i pulsanti; ricordate di selezionare il menù, perché altrimenti i pulsanti non sono ancora inizializzati (o, se preferite, il campo non è ancora minato).
Ora che avete giocato la vostra prima partita, provate a farne un'altra: noterete che le Caption dei pulsanti rimangono invariate. Infatti ingenuamente ci siamo dimenticati di inizializzare anche quelle: per la prima partita non ce n'era bisogno, perché in fase di progettazione avevamo eliminato le proprietà Caption, ma per le partite successive dobbiamo ripristinare le condizioni iniziali. Pertanto aggiungiamo queste semplici righe nella routine mnuNew_click (all'inizio o anche alla fine, come preferite):
For i = 0 To 15
Mina(i).Caption = ""
Next i
Ora possiamo giocare quante partite vogliamo, ma non abbiamo ancora finito: innanzitutto dobbiamo scrivere un bel "Unload me" in mnuExit_click; e poi sarebbe bene congratularsi col giocatore quando vince la partita:
E' intuitivo che una partita a campo minato viene vinta quando il giocatore "scopre" tutti i pulsanti tranne quelli che nascondono le mine (nel nostro caso 13 pulsanti); avremo quindi bisogno di un contatore che tenga traccia di quanti pulsanti sono stati scoperti.
Il luogo più naturale in cui fare questi conti è la routine Mina_Click:
If Len(Mina(Index).Caption) = 0 Then
ContaMine = ContaMine + 1
End If
If ContaMine = MaxContaMine Then
Msgbox "HAI VINTO!"
End If
La variabile ContaMine andrebbe logicamente dichiarata a livello di modulo, nella sezione delle dichiarazioni generali del Form; tuttavia si potrebbe anche dichiararla nella medesima routine Mina_Click usando la parola chiave Static.
La variabile MaxContaMine, invece, va necessariamente dichiarata a livello di modulo, perché non è utilizzata solo da Mina_Click: essa infatti deve essere inizializzata al valore 13 (il numero massimo di pulsanti scopribili) all'avvio dell'applicazione, cioè in Form_Load; dal momento che abbiamo un solo livello di gioco (e non tre come il vero Campo Minato), potremmo fare direttamente:
If ContaMine = 13 Then ...
ma questo non è un buon metodo, perché limita la possibilità di ulteriori aggiornamenti ed espansioni dell'applicazione: se infatti un giorno volessimo aumentare il numero di pulsanti, dovremmo cambiare manualmente tutte le occorrenze del valore 13; per non parlare del caso in cui volessimo aggiungere un livello di gioco.
Invece utilizzando una variabile apposita, dovremmo preoccuparci soltanto di modificarla in fase di inizializzazione.
Ricordate inoltre che l'utilizzo di una variabile apposita rende più chiaro e leggibile il codice, anche a voi stessi. Ricordate anche di inserire l'istruzione:
ContaMine = 0
nella routine mnuNew_Click, perché in ogni nuova partita il contatore deve partire da 0.
Per quanto riguarda la prima If che abbiamo inserito, c'è da dire che il contatore va aggiornato solo se il pulsante è stato premuto per la prima volta (altrimenti uno potrebbe "vincere" premendo per 13 volte lo stesso pulsante!), ovvero se la sua Caption è ancora una stringa nulla.
Il controllo non viene effettuato confrontando la Caption con "", ma esaminando la lunghezza della Caption stessa: questo perché le operazioni con i numeri (la funzione Len restituisce appunto la lunghezza della stringa, cioè un numero) sono più veloci di quelle con le stringhe.
La seconda If è così ovvia che se non l'avete capita dovreste ricominciare il corso daccapo...
L'aggiornamento del contatore però non deve essere fatto solo per il pulsante premuto, ma per qualunque pulsante sia stato "scoperto": in altri termini dobbiamo aggiornare il contatore anche quando si preme un pulsante che non è circondato da mine, perché in questo caso il gioco visualizzerà il numero di mine vicine ai pulsanti che circondano quello premuto.
Quindi le istruzioni:
If Len(Mina(Index).Caption) = 0 Then
ContaMine = ContaMine + 1
End If
vanno inserite nella prima e nella terza diramazione della If contenuta nella routine Mina_Click, che pertanto è diventata così:
If Mina(Index).Tag > 0 Then
If Len(Mina(Index).Caption) = 0 Then
ContaMine = ContaMine + 1
End If
Mina(Index).Caption = Mina(Index).Tag
ElseIf Mina(Index).Tag = -1 Then
Mina(Index).Caption = "M"
For x = 0 To 2
Mina(PosMine(x)).Caption = "M"
Next x
Timer1.Enabled = False
Else
x = Int(Index / 4) 'riga in cui si trova il pulsante premuto
y = Index Mod 4 'colonna in cui si trova il pulsante premuto
For x1 = IIf(x = 0, 0, x - 1) To IIf(x = 3, 3, x + 1)
For y1 = IIf(y = 0, 0, y - 1) To IIf(y = 3, 3, y + 1)
If Len(Mina(4 * x1 + y1).Caption) = 0 Then
ContaMine = ContaMine + 1
End If
Mina(4 * x1 + y1).Caption = Mina(4 * x1 + y1).Tag
Next y1
Next x1
End If
If ContaMine = MaxContaMine Then
MsgBox "HAI VINTO!"
End If
Non resta che aggiornare l'etichetta lblMine: a che serve? Serve a tenere il conto delle mine non ancora trovate; come saprete, cliccando col tasto destro su uno dei pulsanti del vero Campo Minato, su di esso apparirà una bandiera ad indicare che in quella posizione c'è (o pensiamo che ci sia) una mina, e quel pulsante diventerà insensibile al clic sinistro, almeno finchè su di esso resterà la bandierina.
Noi al posto della bandiera visualizzeremo una "x", ma non è questo il punto: la cosa importante è capire come intercettare il clic col tasto destro, ed è una cosa che impareremo più avanti. Per concludere questa lezione, vorrei tornare al punto da cui siamo partiti: la matrice di controlli. Dovreste esservi resi ormai conto dell'utilità dei vettori e delle matrici di variabili: con una sola dichiarazione avete a disposizione numerose variabili dello stesso tipo, collegate tra loro da un nesso logico, da una medesima funzione, da un medesimo scopo; potete modificarle tutte insieme con un semplice ciclo sfruttando la possibilità di accedere ad esse attraverso un indice. Per una matrice di controlli i vantaggi sono gli stessi: i pulsanti del nostro campo minato svolgono tutti la stessa funzione, cioè visualizzano quante mine ci sono nelle vicinanze o interrompono il gioco se nascondono una mina; grazie al fatto che appartengono a una matrice, possiamo accedere ad ognuno di essi tramite un indice che, nel caso dell'evento click, ci viene fornito direttamente da Visual Basic, con la conseguenza che possiamo scrivere il codice solo una volta, perché una sola è la routine Mina_Click; se non avessimo usato una matrice di pulsanti, avremmo dovuto crearne 16 diversi e indipendenti, e ripetere le istruzioni della routine per l'evento click di ognuno dei 16 pulsanti: immaginate se avessimo usato 480 pulsanti, come nel terzo livello del vero Campo Minato! Le matrici di controlli semplificano tutto questo.

Esempio



  


  1. Ednilson

    No. Some of the books will be discontinued and not giielble for the buyback. The others will be bought back for less than you paid. Don't expect more than 4

  2. coluccia

    mi servirebbe sapere come si usano i vettori

  3. salah mk

    consultazione corsi vusual basic