Programmazione.it: Il portale Italiano sull`Information

Transcript

Programmazione.it: Il portale Italiano sull`Information
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 1
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
- Votanti: 34 - Lettori: 18858 - Livello: Medio
Le matrici possono essere usate in alternativa ai vettori. Le matrici, infatti, non sono altro che
vettori a più dimensioni (o meglio, i vettori sono matrici a una sola dimensione). Supponiamo,
ad esempio, 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
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
o anche:
Login
Dim Scatole(99,2) As Integer
Login
Password
Login
In entrambi i casi (se non avete inserito l'istruzione Option Base 1) avremo una matrice con
100 * 3 = 300 elementi, in altre parole 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:
Dimenticata la password?
Dim Var() As Long 'dichiarazione della matrice:non è indicata la dimensione
Clicca qui
della matrice
Non sei ancora iscritto?
Clicca qui
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
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
Mailing List
Visual Basic
E' importante ricordare che il ridimensionamento di una matrice riguarda esclusivamente il
numero di elementi, non il loro tipo, né tanto meno 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
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1430&idArea=10 (1 of 4)12/09/2003 12.10.02
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
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
Waitec 15'' LCD
Olidata 17"
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 esempio, 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.
Esistono vari tipi di cicli 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 vi basti
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.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1430&idArea=10 (2 of 4)12/09/2003 12.10.02
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
Costruiamo il Campo Minato con VB
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.
Figua 1
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 ctrl+v. Apparirà un messaggio di questo tipo:
Figura 2
Clicchiamo su 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 l'unico 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.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1430&idArea=10 (3 of 4)12/09/2003 12.10.02
Programmazione.it: Il portale Italiano sull'Information Technology
Figura 3
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 subordinate rispetto al menù "Partita". Resta ancora da inserire un timer,
che trovate naturalmente sulla casella degli strumenti (è quello con un orologio come icona):
potete metterlo dove volete (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.
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1430&idArea=10 (4 of 4)12/09/2003 12.10.02
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 2
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
Login
Login
Password
Login
- Votanti: 34 - Lettori: 18858 - Livello: Medio
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 altre parole, 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)
Dimenticata la password?
Clicca qui
Non sei ancora iscritto? 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
Clicca qui
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
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
Mailing List
Visual Basic
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 dislocare 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. 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)
http://www.programmazione.it/index.php?entity=earticl...&idArea=10&PHPSESSID=bcdd1ca2b1968ae6acff46b6b1929603 (1 of 3)12/09/2003 12.11.27
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
Project 2000
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:
Waitec 15'' LCD
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é tanto meno con l'evento Timer). Siccome 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 esempio, 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
http://www.programmazione.it/index.php?entity=earticl...&idArea=10&PHPSESSID=bcdd1ca2b1968ae6acff46b6b1929603 (2 of 3)12/09/2003 12.11.27
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
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 ad 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 finche non si trova un numero diverso da quello memorizzato in
PosMine(0).
Poi si passa alla terza ed 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.
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticl...&idArea=10&PHPSESSID=bcdd1ca2b1968ae6acff46b6b1929603 (3 of 3)12/09/2003 12.11.27
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 3
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
- Votanti: 34 - Lettori: 18858 - Livello: Medio
Nelle precedenti lezioni abbiamo reso il programma cosciente delle posizioni delle mine: ogni
pulsante sa, grazie alla proprietà tag, se nasconde una mina oppure no. E’ ora necessario
implementare l’algoritmo fondamentale del gioco. Cosa succede quando il giocatore preme
un pulsante? Semplicemente se sotto il pulsante premuto si trova una mina, il gioco termina;
altrimenti viene visualizzato il numero di mine circostanti.
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
Login
Login
Password
Login
Dimenticata la password?
Figura 1 – Il nostro bel giochetto
Clicca qui
Non sei ancora iscritto?
Clicca qui
Bisognerà quindi effettuare un controllo del genere:
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
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. Aggiorniamo quindi la routine mnuNew_Click.
Mailing List
Visual Basic
L’algoritmo
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.
Come escogitare un algoritmo semplice ed efficiente per fare questi calcoli? Solitamente si
usa un grafico (il cosiddetto diagramma di flusso) per descrivere un algoritmo (ovvero il
flusso delle operazioni), ma in questa sede è più conveniente optare per un elenco numerato.
Un algoritmo per calcolare il numero di mine circostanti un pulsante potrebbe essere questo:
•
prendo in considerazione un pulsante (chiamiamolo "x");
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1453&idArea=10 (1 of 3)12/09/2003 12.11.59
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
Videoproiettore NEC
Paint Shop Pro 7
•
prendo in considerazione tutti i pulsanti che lo circondano, uno alla volta
(chiamiamo questo pulsante "y");
se y nasconde una mina, incremento un contatore c, altrimenti non faccio nulla;
•
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);
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, aggiornando il contatore delle mine.
Esistono altri algoritmi più semplici ed efficienti? Certamente: il programma conosce il
numero totale di mine e dove si trovano: perché allora creare due cicli con queste funzioni?
Ci basterà aggiornare le proprietà Tag dei pulsanti che circondano quelli che nascondono
una mina.
Procediamo così:
Inizializzo le proprietà tag di tutti i pulsanti a 0;
•
Prendo in esame il pulsante con indice PosMine(i) (chiamiamolo "x"; inizialmente i
•
= 0);
Pongo x.tag = -1 e aggiorno le proprietà tag di tutti i pulsanti che circondano x;
•
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
y = PosMine(i) Mod 4 'colonna in
For x1 = IIf(x = 0, 0, x - 1) To
For y1 = IIf(y = 0, 0, y - 1)
If Mina(4 * x1 + y1).Tag >
Mina(4 * x1 + y1).Tag =
End If
Next y1
Next x1
Next i
Timer1.Enabled = True
cui si trova la mina
cui si trova la mina
IIf(x = 3, 3, x + 1)
To IIf(y = 3, 3, y + 1)
-1 Then
Mina(4 * x1 + y1).Tag + 1
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
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1453&idArea=10 (2 of 3)12/09/2003 12.11.59
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
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 abbiamo 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
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1453&idArea=10 (3 of 3)12/09/2003 12.11.59
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 4
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
- Votanti: 34 - Lettori: 18858 - Livello: Medio
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.
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
Login
Login
Password
Login
Figura 1
Dimenticata la password?
Clicca qui
Non sei ancora iscritto? 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
Clicca qui
Mailing List
Visual Basic
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: ad essere diligenti, avremmo
dovuto dichiarare un'altra variabile, 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.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1572&idArea=10 (1 of 4)12/09/2003 12.13.16
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
Visual Basic .NET Corso di
programmazione
Antifurto Satellitare
GSM Viasat
Figura 2
Backup PCMCIA
2Gb
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:
Figura 3
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
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1572&idArea=10 (2 of 4)12/09/2003 12.13.16
Programmazione.it: Il portale Italiano sull'Information Technology
(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
y = Index Mod 4 'colonna in cui si trova il
For x1 = IIf(x = 0, 0, x - 1) To IIf(x = 3,
For y1 = IIf(y = 0, 0, y - 1) To IIf(y =
If Len(Mina(4 * x1 + y1).Caption) = 0
ContaMine = ContaMine + 1
End If
pulsante premuto
pulsante premuto
3, x + 1)
3, 3, y + 1)
Then
Mina(4 * x1 + y1).Caption = Mina(4 * x1 + y1).Tag
Next y1
Next x1
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1572&idArea=10 (3 of 4)12/09/2003 12.13.16
Programmazione.it: Il portale Italiano sull'Information Technology
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 sapete, 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.
Figura 4
Noi al posto della bandiera visualizzeremo una "x". E’ 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. Poiché appartengono ad una matrice, possiamo accedere
ad ognuno di essi tramite un indice che, nel caso dell'evento click, ci viene fornito
direttamente da Visual Basic. Così 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.
Scarica qui il progetto completo.
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1572&idArea=10 (4 of 4)12/09/2003 12.13.16
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 5
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
- Votanti: 34 - Lettori: 18858 - Livello: Medio
Nella lezione 1 abbiamo avuto un assaggio del ciclo For, di sicuro il più utilizzato nell'ambito
dei cicli disponibili in Visual Basic. Questo ciclo è identificato da due istruzioni, poste all'inizio
e alla fine di esso:
For contatore = inizio To fine
[istruzioni]
Next contatore
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
Login
dall'istruzione Next fino a raggiungere il valore finale (fine). Nella sintassi standard, le
istruzioni contenute all'interno del ciclo sono eseguite fintantoché contatore risulta minore o
Login
uguale a fine: ciò significa che, quando il ciclo termina, la variabile contatore ha un valore
pari a fine + 1, perché nell'ultima esecuzione del ciclo risulta contatore = fine e l'istruzione
Next incrementa ulteriormente il contatore. Questa è una caratteristica tipica dei cicli "For",
Password
che si riscontra anche negli altri linguaggi di programmazione: non potrebbe essere
altrimenti, poiché l'esecuzione del ciclo termina proprio quando contatore supera il valore
fine.
Login
Nella sintassi standard appena descritta, l'incremento del contatore è di un'unità ad ogni
Dimenticata la password?
esecuzione del ciclo, ma è possibile modificare il valore dell'incremento usando la clausola
Clicca qui
Non sei ancora iscritto? Step dopo il valore fine:
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
Clicca qui
For contatore = inizio To fine Step incremento
[istruzioni]
Next contatore
Mailing List
Visual Basic
All'inizio del ciclo il valore del contatore è uguale a inizio, alla seconda esecuzione del ciclo è
uguale a inizio+incremento, alla terza esecuzione è uguale a inizio+incremento+incremento e
così via fino a che il contatore diventa maggiore del valore fine. Il valore di incremento viene
sommato algebricamente al valore corrente del contatore: ciò significa che l'incremento può
anche essere negativo, determinando così una diminuzione progressiva del valore del
contatore: in questo caso il ciclo cesserà di essere eseguito quando il contatore diventa
minore del valore finale specificato. Affinchè il ciclo funzioni correttamente, quindi, il valore
iniziale dovrà essere maggiore del valore finale, ad es. il ciclo seguente viene eseguito tre
volte, per i = 10, i = 8, i = 6; all'uscita dal ciclo sarà i = 4:
Dim i as Integer 'contatore
For i = 10 To 5 Step –2
[istruzioni]
Next i
Si è detto che il contatore è una variabile numerica, senza specificare il suo tipo: infatti,
benché esso sia solitamente un Integer, è possibile anche utilizzare valori frazionari come
Single o Double, e lo stesso vale per i valori inizio, fine e incremento. Tuttavia, è sempre
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1823&idArea=10 (1 of 4)12/09/2003 12.13.33
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
preferibile utilizzare valori interi per motivi di precisione: le variabili di tipo Single o Double,
infatti, commettono errori di precisione che, per quanto piccoli, non sono affatto trascurabili;
basta un errore di un milionesimo per far credere a Visual Basic che il valore finale sia stato
superato e determinare anzitempo la fine del ciclo.
Fujitsu 325A
Si consideri ad esempio il ciclo seguente:
Dim i as Single
For i = 0.6 To 0.8 Step 0.1
Debug.Print i
Next i
Antifurto Satellitare
GSM Viasat
Debug.Print i
In teoria esso dovrebbe essere eseguito per tre volte, con il contatore i che vale 0.6, 0.7 e
0.8; invece esso è eseguito soltanto due volte, per i = 0.6 e i = 0.7: quando i viene
ulteriormente incrementato non vale 0.8, bensì 0.8000001 che, essendo maggiore di 0.8,
determina l'interruzione del ciclo. Per questo motivo è sempre buona norma usare valori
interi sia per il contatore che per i valori di inizio, fine e incremento, in quanto con valori interi
il microprocessore non commette quegli errori di imprecisione che possono determinare
comportamenti anomali.
L’inizio e la fine del ciclo
Naturalmente i valori iniziale e finale del contatore, ovvero il numero di iterazioni, non devono
necessariamente essere stabiliti esplicitamente durante la scrittura del codice: essi possono
essere determinati durante l'esecuzione del programma semplicemente utilizzando delle
variabili aggiuntive: ad esempio, il programma potrebbe chiedere all'utente quali debbano
essere i valori di inizio e fine del contatore e memorizzare questi valori in due variabili da
usare nell'istruzione For:
Dim i as Integer 'contatore
Dim intInizio as Integer, intFine as Integer 'valori iniziale e finale
intInizio=Val(InputBox("Inserisci il valore iniziale del contatore:"))
intFine=Val(InputBox("Inserisci il valore finale del contatore:"))
For i = intInizio To intFine
Debug.Print i
Next i
La funzione Val e la funzione InputBox
In questo esempio si è fatto uso della funzione InputBox, che richiede all'utente l'inserimento
di un valore in una casella di testo: tale valore è quello restituito dalla funzione e, poiché è di
tipo stringa, esso deve essere convertito in numero attraverso la funzione Val. È chiaro che
se l'utente inserisce nella casella un valore che non può essere convertito in numero (ad es.
"a56bc"), sarà generato un errore causato dalla funzione Val.
La nidificazione di più cicli For
Come per altri blocchi di istruzioni, anche per i blocchi For…Next è possibile usare la
nidificazione, ovvero includere un blocco For all'interno di un altro blocco For:
For i = 1 To 10
For k = 5 To 22
[istruzioni]
Next k
Next i
Un dettaglio Importante…
La cosa importante è non intersecare mai due cicli distinti, ovvero non fare mai quanto
segue:
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1823&idArea=10 (2 of 4)12/09/2003 12.13.33
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
For i = 1 To 10 'inizio PRIMO ciclo
For k = 5 To 22 'inizio SECONDO ciclo
[istruzioni]
Next i 'fine PRIMO ciclo
Next k 'fine SECONDO ciclo
Così facendo si genera, infatti, un errore di sintassi, perché l'istruzione "Next i" non
corrisponde all'istruzione "For k" che la precede: il secondo ciclo non è interamente
contenuto nel primo, ma si sovrappone ad esso generando confusione. Volendo, è possibile
anche omettere il nome del contatore nell'istruzione Next, e in tal caso Visual Basic
assumerà automaticamente che esso si riferisce all'istruzione For immediatamente
precedente.
For i = 1 To 10
For k = 5 To 22
[istruzioni]
Next ' Visual Basic assume che l'istruzione sia: Next k
Next ' Visual Basic assume che l'istruzione sia: Next i
L’interruzione improvvisa del ciclo
Spesso è necessario interrompere l'esecuzione di un ciclo se si verifica una determinata
condizione: Visual Basic mette a disposizione del programmatore un'istruzione apposita: Exit
For, che sposta il punto di esecuzione del programma all'istruzione immediatamente
successiva all'istruzione Next relativa al ciclo interrotto. Solitamente questa istruzione si fa
dipendere da un blocco If che controlla il verificarsi di una "condizione di uscita":
For i = 1 To 10
If i = 8 Then Exit For
Debug.Print i
Next i
Debug.Print "ciclo interrotto"
Il ciclo precedente, che semplicemente visualizza il valore del contatore, teoricamente
dovrebbe essere eseguito dieci volte ma in realtà è eseguito solo otto volte, perché all'ottava
esecuzione la condizione i = 8 risulta verificata determinando l'uscita dal ciclo (senza
stampare il valore del contatore) e la visualizzazione del messaggio "ciclo interrotto". Si noti
che questo messaggio sarebbe visualizzato in ogni caso, al termine del ciclo. I "puristi" della
programmazione non vedono di buon occhio l'utilizzo dell'istruzione Exit For, perché in
questo caso il ciclo non termina solo con l'ultima istruzione "Next", come ci si potrebbe
aspettare in condizioni normali, ma potrebbe terminare anche all'interno del ciclo stesso: ciò
determina un'eccezione alla normale esecuzione del codice e pertanto potrebbe complicarne
la comprensione e la modifica.
Un altro modo di interrompere l’esecuzione del ciclo for
In effetti utilizzare l'istruzione Exit For non è l'unico modo per interrompere un ciclo: sapendo
che esso termina quando il contatore supera il valore finale, la soluzione più intuitiva sarebbe
quella di assegnare al contatore un valore maggiore del limite finale in modo che il ciclo non
prosegua oltre:
For i = 1 To 10
If i = 8 Then i = 11
Debug.Print i
Next i
Debug.Print "ciclo interrotto"
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1823&idArea=10 (3 of 4)12/09/2003 12.13.33
Programmazione.it: Il portale Italiano sull'Information Technology
Nell'esempio precedente, quando i = 8 il programma assegna al contatore il valore 11, cioè
un valore superiore al limite di 10 specificato nell'istruzione For; in questo modo il ciclo non è
interrotto direttamente, e infatti l'istruzione Debug.Print i è eseguita normalmente anche
quando i = 8, però l'istruzione Next i verifica che il contatore ha superato il limite e quindi
determina la fine del ciclo e la visualizzazione del messaggio "ciclo interrotto". Tuttavia, da un
punto di vista stilistico, questa soluzione è ancora peggiore della precedente, perché il valore
del contatore non dovrebbe mai essere modificato direttamente all'interno del ciclo stesso:
un metodo del genere può complicare molto il debug e l'eventuale modifica del codice perché
può generare errori imprevisti e generalmente difficili da identificare.
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=1823&idArea=10 (4 of 4)12/09/2003 12.13.33
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 6
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
- Votanti: 34 - Lettori: 18858 - Livello: Medio
Tra le strutture iterative disponibili in Visual Basic esiste anche il ciclo While…Wend, che è
certamente il meno conosciuto e il meno utilizzato. In effetti il funzionamento di questo tipo di
ciclo è del tutto analogo a quello di un ciclo Do While…Loop: le istruzioni contenute nel ciclo
sono eseguite finché la condizione espressa dopo l'istruzione While è vera:
While condizione
[istruzioni]
Wend
Inoltre, il normale ciclo Do While…Loop è più flessibile del ciclo While…Wend: già di per sé
questo giustifica lo scarsissimo uso del ciclo While…Wend.
Torniamo ai cicli in generale. Quando si deve utilizzare una struttura iterativa bisogna fare
Login
attenzione in particolare a due cose: la prima, che abbiamo già visto, è fornire una
condizione di uscita per evitare cicli infiniti; la seconda riguarda la corretta gestione della
prima e dell'ultima iterazione. La questione non è banale come sembra: soprattutto all'inizio
Password
può portare a errori grossolani. Prendiamo ad esempio il caso di una ricerca ricorsiva di una
stringa all'interno di un testo.
Spesso capita che sia necessario conoscere, oltre al nome di un file, anche il suo percorso
Login
(path, in inglese), ovvero la posizione esatta all'interno dell'albero delle directory contenute
Dimenticata la password?
nell'hard disk; e a volte è utile suddividere il percorso in tanti "pezzi" quante sono le cartelle
Clicca qui
Non sei ancora iscritto? che bisogna attraversare per giungere fino al file: per far ciò occorre contare quanti sono i
separatori di path presenti nella stringa che identifica il percorso, ovvero quanti backslash
Clicca qui
("\") ci sono. Per trovare una stringa all'interno di un'altra è sufficiente usare la comoda
istruzione instr, la cui sintassi è:
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
Login
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
instr([start], string1, string2, [compare])
Mailing List
Visual Basic
Il primo parametro è un numero che indica la posizione dalla quale cominciare a cercare la
stringa da trovare: ad esempio, se stiamo cercando un backslash all'interno di un path
completo, è inutile cominciare a cercare dal primo carattere del percorso, perché questo
comincia con la lettera di un'unità seguita dai due punti (ad es. "C:"): è possibile iniziare la
ricerca direttamente dal terzo carattere. Il parametro è facoltativo e, se non viene specificato,
la ricerca partirà dal primo carattere. I parametri string1 e string2 identificano rispettivamente
la stringa in cui bisogna cercare (nel nostro caso il percorso) e la stringa da cercare (nel
nostro caso il backslash). L'ultimo parametro indica come va effettuata la ricerca; nel nostro
caso i valori interessanti sono due: 0, espresso anche dalla costante vbBinaryCompare, e 1,
espresso dalla costante vbTextCompare. Nel primo caso il confronto tra le due stringhe è
effettuato in modalità binaria, il che, tradotto un po' rozzamente, significa che il confronto è
"case sensitive", ovvero sensibile alla differenza tra maiuscole e minuscole. Nel secondo
caso, invece, la differenza tra maiuscole e minuscole è ignorata, perché il confronto è
effettuato in semplice modalità testuale. Per chiarire la differenza basta scrivere nella finestra
immediata:
? instr(1, "Pippo", "p", vbBinaryCompare)
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2040&idArea=10 (1 of 4)12/09/2003 12.14.00
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
oppure
instr(1, "Pippo", "p", 0)
Canon Eos 3
che è la stessa cosa. La funzione restituirà il valore 3.
Se invece si scrive:
? instr(1, "Pippo", "p", vbTextCompare) 'oppure instr(1, "Pippo", "p", 1)
Paint Shop Pro 7
la funzione restituirà 1.
Come avrete intuito, il valore restituito dalla funzione è la posizione della prima "p" trovata
nella stringa "Pippo": ma mentre nel primo caso cerchiamo una p minuscola abilitando la
ricerca "case sensitive", nel secondo caso la ricerca è solo in modalità testo e, quindi, la
funzione restituisce la posizione della prima "p" che trova, maiuscola o minuscola che sia. Se
la stringa cercata non si trova in quella in cui è fatta la ricerca (se ad esempio cerchiamo una
"z" in "pippo" o anche una "O" in "Pippo", abilitando la ricerca binaria), la funzione instr
restituisce zero.
Tornando al nostro esempio, per contare i backslash in un path basta un semplice ciclo do…
loop, utilizzando un contatore da incrementare ogni volta che instr trova un "\". Dobbiamo,
però, fare attenzione a spostare sempre in avanti il punto di inizio della ricerca (il parametro
start), perché, se cerchiamo sempre a partire dal primo carattere, il ciclo non si fermerà mai e
restituirà sempre la posizione del primo "\". Una soluzione comoda è quella di utilizzare
un'altra variabile che memorizzi la posizione dell'ultimo "\" trovato, ovvero che memorizzi il
valore restituito da instr: la ricerca successiva dovrà partire dal carattere successivo:
Dim intPos as Integer
Dim strPath as String
strPath="C:\documenti\immagini\esempio.jpg"
Do
intPos=Instr(intPos+1, strPath, "\")
Loop
intPos memorizza la posizione dell'ultimo backslash trovato: poiché essa è inizializzata a 0,
alla prima iterazione la ricerca partirà dal primo carattere, come avviene nella maggior parte
dei casi (se vogliamo farla partire dal terzo carattere, perché i primi due contengono il nome
dell'unità, basta inizializzare intPos=2 prima del Do); nelle iterazioni successive, partirà dal
carattere successivo all'ultimo backslash trovato nel path. Il parametro "compare" è stato
tralasciato perché non ha alcuna influenza: non esiste un "\" maiuscolo e un "\" minuscolo;
comunque l'impostazione predefinita è vbBinaryCompare. La variabile strPath è inizializzata
a un percorso qualunque (compreso il nome di un file) solo per far funzionare il codice: in
realtà dovrebbe essere ignoto a priori, ad esempio potrebbe essere inserito dall'utente in fase
di esecuzione. Nel ciclo appena descritto manca una cosa: la condizione di uscita. Questa
dovrebbe verificare se la funzione instr ha restituito un valore positivo oppure no: nel primo
caso significa che ha trovato un "\" e quindi possiamo continuare la ricerca; nel secondo caso
vuol dire che non ha trovato nulla e quindi la ricerca deve essere fermata perché non ci sono
ulteriori "\" nell'ultima parte del percorso. Pertanto la condizione potrebbe essere:
while intPos>0
oppure:
until intPos=0
Bisogna decidere dove mettere la condizione, se all'inizio o alla fine del ciclo: se intPos è
inizializzata a 0, certamente la condizione non dovrà essere posta all'inizio, altrimenti il ciclo
non eseguirebbe neppure un'istruzione:
Dim intPos as Integer
Dim strPath as String
strPath="C:\documenti\immagini\esempio.jpg"
Do While intPos>0 'intPos è =0, quindi il ciclo non è eseguito
intPos=Instr(intPos+1,strPath,"\")
Loop
D'altra parte, mettere la condizione alla fine comporta la perdita di una preziosa
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2040&idArea=10 (2 of 4)12/09/2003 12.14.00
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
informazione: la posizione dell'ultimo backslash esistente nel path, che solitamente indica
anche l'inizio del nome vero e proprio del file (oppure il nome della cartella in cui risiede il file,
a seconda dei casi):
Dim intPos as Integer
Dim strPath as String
strPath="C:\documenti\immagini\esempio.jpg"
Do
intPos=Instr(intPos+1,strPath,"\")
Loop While intPos>0
Questo ciclo esegue almeno una volta la ricerca e prosegue fino a trovare tutti i "\", ma
all'uscita dal ciclo intPos vale 0 e noi non sappiamo più qual era la posizione dell'ultimo
backslash; magari è un'informazione che non ci interessa, ma in caso contrario dobbiamo
trovare una soluzione alternativa. Ad esempio si potrebbe utilizzare un'altra variabile per
memorizzare il valore precedente di intPos:
Dim intPos as Integer
Dim intPosPrec as Integer
Dim strPath as String
strPath="C:\documenti\immagini\esempio.jpg"
Do
intPosPrec=intPos
intPos=Instr(intPos+1, strPath, "\")
Loop While intPos>0
All'uscita dal ciclo intPosPrec conterrà la posizione dell'ultimo "\". Oppure si può utilizzare
come condizione di uscita non "intPos>0" bensì "Instr(…)>0", ma in questo modo la funzione
Instr viene eseguita due volte per ogni ricerca: una volta per controllare se ci sono altri "\" e
un'altra volta per sapere dove si trovano:
Dim intPos as Integer
Dim strPath as String
strPath="C:\documenti\immagini\esempio.jpg"
Do While Instr(intPos+1, strPath, "\")
intPos= Instr(intPos+1, strPath, "\")
Loop
In questo caso la condizione può essere spostata all'inizio del ciclo: se questo non viene
eseguito neppure una volta, significa che non ci sono "\" nel percorso; infatti instr() restituisce
zero, che viene valutato come valore logico False, e quindi la condizione per eseguire il ciclo
non è verificata. Una soluzione analoga può essere adottata inizializzando a un valore
positivo la variabile intPos (ad esempio al valore 2, come suggerito prima):
Dim intPos as Integer
Dim strPath as String
strPath="C:\documenti\immagini\esempio.jpg"
IntPos=2
Do While intPos
IntPos = Instr(intPos+1, strPath, "\")
Loop
Insomma, quando usate i cicli state attenti non solo a quando interromperli (condizione di
uscita), ma anche a quale valore assumono le variabili coinvolte in essi (in particolare prima
della prima iterazione e dopo l'ultima iterazione). Se non siete ancora convinti, proviamo a
memorizzare le varie parti del percorso in un vettore: ogni elemento del vettore deve
contenere una delle cartelle appartenenti al path in ordine gerarchico. Poiché non sappiamo
a priori quante cartelle sono coinvolte, possiamo usare un vettore dinamico: ogni volta che
troviamo un "\" dobbiamo aggiungere un elemento al vettore e assegnargli il nome della
cartella che precede questo "\"
Dim strCartelle() as String
Dim intPos as Integer
Dim intPosPrec as Integer
Dim strPath as String
strPath="C:\documenti\immagini\esempio.jpg"
Do
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2040&idArea=10 (3 of 4)12/09/2003 12.14.00
Programmazione.it: Il portale Italiano sull'Information Technology
IntPosPrec=intPos
IntPos=instr(intPos+1, strPath, "\")
Redim Preserve strCartelle(Ubound(strCartelle)+1)
StrCartelle(Ubound(strCartelle))=Mid$(strPath, intPosPrec+1, intPosintPosPrec)
Loop While intPos
Il vettore strCartelle contiene i nomi delle cartelle presenti nel path: è un vettore dinamico,
che viene ridimensionato all'interno del ciclo ogni volta che viene trovato un "\". Il
ridimensionamento, che avevamo brevemente già visto (parte II, lez. 1), avviene in base al
valore della funzione Ubound, che restituisce il limite superiore attuale del vettore: se il
vettore ha tre elementi (da 0 a 2), Ubound restituisce 2; attenzione: se Ubound restituisce
zero non significa che il vettore non ha alcun elemento, ma che ne ha uno solo con indice 0.
Aggiungere un elemento al vettore significa quindi ridimensionarlo assegnandogli un
elemento in più di quelli che già possiede (ubound()+1). La parola chiave Preserve serve a
mantenere in memoria i valori già assegnati: infatti l'istruzione Redim usata da sola non fa
altro che riallocare lo spazio di memoria disponibile per il vettore, reinizializzando tutti i suoi
elementi e quindi di fatto cancellando i valori precedentemente assegnati. Il nome di ogni
cartella è estratto attraverso la funzione Mid, che prende da una stringa (il primo parametro)
un intervallo di caratteri definito da una posizione iniziale (il secondo parametro) e da una
lunghezza (il terzo parametro). Ovviamente il secondo parametro dev'essere positivo,
altrimenti si verifica un errore; il terzo invece può anche essere zero (in tal caso è restituita la
stringa vuota ""), basta che sia non negativo. Nel nostro caso la posizione iniziale del nome
della cartella è naturalmente il carattere successivo al penultimo backslash trovato, mentre la
lunghezza del nome è calcolabile tramite la distanza tra il penultimo e l'ultimo backslash. Il
ciclo appena descritto presenta alcuni inconvenienti; anzi, sono veri e propri errori: prima di
tutto, la funzione Ubound deve avere come argomento un vettore (o matrice) con dimensioni
definite, altrimenti genera un errore e, siccome alla prima iterazione strCartelle non ha una
dimensione definita, indovinate un po' che succede…
In secondo luogo, all'ultima iterazione intPos è uguale a 0, come si è visto prima; in questo
caso ad andare in errore è la funzione Mid, perché il terzo parametro risulterebbe negativo.
Nella prossima lezione spiegheremo come risolvere gli errori.
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2040&idArea=10 (4 of 4)12/09/2003 12.14.00
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 7
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Cerca!
Vai alla ricerca
avanzata
Login
Login
Password
Login
Hosting
Telexa.net
Data: 04-07-2003 - Voto:
- Votanti: 34 - Lettori: 18858 - Livello: Medio
DnsHosting.it
EuroLogon.com
Introduzione
Nell'ultima lezione ci eravamo lasciati con un paio di errori da correggere; questo era il codice Dominiando.it
"sbagliato":
FuturaHost.com
HostingSolutions.it
Dim strCartelle() as String
Consultingweb.it
Dim intPos as Integer
Dim intPosPrec as Integer
Olimont.com
Dim strPath as String
strPath="C:\documenti\immagini\esempio.jpg"
Do
IntPosPrec=intPos
IntPos=instr(intPos+1, strPath, "\")
Redim Preserve strCartelle(Ubound(strCartelle)+1) as String
StrCartelle(Ubound(strCartelle))=Mid$(strPath, intPosPrec+1, intPosintPosPrec)
Loop While intPos
Se proviamo ad eseguire questo codice (ad esempio inserendolo nella routine dell'evento
Load di un form), otterremo questo errore:
Dimenticata la password?
Clicca qui
"Indice non compreso nell'intervallo"
Non sei ancora iscritto?
Clicca qui
L'errore si verifica nella riga in cui si ridimensiona il vettore strCartelle, perché nella prima
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
iterazione del ciclo esso non ha una dimensione definita, pertanto ad andare in errore è la
funzione Ubound(strCartelle): risolvere questo errore è molto semplice: basta inizializzare il
vettore prima del ciclo Do. Poiché all'inizio il vettore deve essere vuoto, possiamo
ridimensionarlo assegnandogli un solo elemento in questo modo:
Redim strCartelle(0) as String
Mailing List
Visual Basic
Così facendo, però, sprechiamo un elemento del vettore (l'elemento zero, appunto), perché
nel ciclo il ridimensionamento avviene aumentando di 1 l'indice superiore del vettore: quindi il
primo elemento del vettore riempito con il nome di una cartella sarebbe l'elemento 1, mentre
l'elemento 0 resterebbe inutilizzato. Per rimediare a questo piccolo inconveniente (che, a dire
il vero, potremmo anche trascurare) basta invertire le ultime due istruzioni del ciclo, in modo
che prima si assegni un valore all'ultimo elemento del vettore e poi si proceda al
ridimensionamento di quest'ultimo:
Dim strCartelle() as String
Dim intPos as Integer
Dim intPosPrec as Integer
Dim strPath as String
strPath="C:\documenti\immagini\esempio.jpg"
Redim strCartelle(0) as String
Do
IntPosPrec=intPos
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2209&idArea=10 (1 of 4)12/09/2003 12.14.16
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
IntPos=instr(intPos+1, strPath, "\")
StrCartelle(Ubound(strCartelle))=Mid$(strPath, intPosPrec+1, intPosintPosPrec)
Redim Preserve strCartelle(Ubound(strCartelle)+1) as String
Loop While intPos
Waitec 15'' LCD
Il primo "bug" è risolto, ma ce n'è ancora un altro: se proviamo ad eseguire il ciclo appena
modificato, otterremo il seguente messaggio:
"Chiamata di routine o argomento non valido"
Canon Eos 3
Questa volta l'errore non si verifica all'inizio, ma alla fine del ciclo: infatti, quando sono finiti i
"\" da cercare, la variabili intPos sarà uguale a zero, mentre la variabile intPosPrec è diversa
da zero; pertanto il terzo parametro della funzione Mid$() è negativo e questo genera l'errore
appena visto. Anche in questo caso la risoluzione è abbastanza semplice, basta usare il
costrutto if…then:
Dim strCartelle() as String
Dim intPos as Integer
Dim intPosPrec as Integer
Dim strPath as String
strPath="C:\documenti\immagini\esempio.jpg"
Redim strCartelle(0) as String
Do
IntPosPrec=intPos
IntPos=instr(intPos+1, strPath, "\")
If intPos Then
StrCartelle(Ubound(strCartelle))=Mid$(strPath, intPosPrec+1, intPosintPosPrec)
Else
StrCartelle(Ubound(strCartelle))=Mid$(strPath, intPosPrec+1)
End If
Redim Preserve strCartelle(Ubound(strCartelle)+1) as String
Loop While intPos
Se intPos è diversa da zero (cioè se è stato trovato un ulteriore "\"), si prendono solo i
caratteri tra il penultimo e l'ultimo backslash, altrimenti si prendono tutti i caratteri dall'ultimo
backslash in poi. Questa soluzione, benché semplice, è però poco elegante, perché
costringe ad effettuare un controllo che risulta sostanzialmente inutile in tutte le iterazioni
tranne che nell'ultima; se il ciclo dovesse eseguire numerose iterazioni, il rallentamento
derivante da questo controllo potrebbe farsi consistente. Una possibile alternativa è quella di
spostare la ricerca del successivo backslash alla fine del ciclo, anziché porla all'inizio: così
quando non ci sono più backslash il ciclo termina direttamente; in questo caso però occorre
effettuare la ricerca del primo backslash prima dell'inizio del Do e assegnare l'ultimo
elemento del vettore dopo la sua fine, in questo modo:
Dim strCartelle() as String
Dim intPos as Integer
Dim intPosPrec as Integer
Dim strPath as String
strPath="C:\documenti\immagini\esempio.jpg"
Redim strCartelle(0) as String
IntPos=instr(intPos+1, strPath, "\")
Do
StrCartelle(Ubound(strCartelle))=Mid$(strPath, intPosPrec+1, intPosintPosPrec)
Redim Preserve strCartelle(Ubound(strCartelle)+1) as String
IntPosPrec=intPos
IntPos=instr(intPos+1, strPath, "\")
Loop While intPos
StrCartelle(Ubound(strCartelle))=Mid$(strPath, intPosPrec+1)
For intPos=0 to Ubound(strCartelle)
Debug.Print strCartelle(intPos)
Next intPos
Il piccolo ciclo For finale serve solo a visualizzare i nomi delle cartelle che abbiamo
estrapolato dal percorso originale: come noterete, tutti i nomi delle cartelle (tranne il nome del
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2209&idArea=10 (2 of 4)12/09/2003 12.14.16
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
file) terminano con "\", perché alla funzione Mid abbiamo chiesto di prendere tutti i caratteri
compresi tra il penultimo e l'ultimo backslash, e in questi caratteri c'è anche l'ultimo
backslash. Questo non è un vero e proprio errore, dipende più che altro dall'uso che
vogliamo fare del vettore strCartelle; comunque, se volessimo solo il nome della cartella puro
e semplice dovremmo chiamare la funzione Mid in questo modo:
StrCartelle(Ubound(strCartelle))=Mid$(strPath, intPosPrec+1, intPosintPosPrec-1)
Il "-1" aggiunto al terzo parametro fa sì che l'ultimo backslash sia escluso dai caratteri da
assegnare agli elementi del vettore strCartelle. Ci possono essere vari modi per risolvere
problemi come quelli visti fin qui: l'importante è sapersi ingegnare e trovare la soluzione più
adatta alle proprie esigenze.
L’istruzione select Case
Terminato il discorso sui cicli, passiamo a considerare un'altra istruzione utilizzata spesso per
controllare il flusso del programma: l'istruzione Select case. Essa analizza un'espressione ed
esegue istruzioni differenti in base al suo valore. Detto così sembra che l'istruzione Select
case sia del tutto simile all'istruzione If e in effetti è così: infatti è possibile esprimere una
Select case come un blocco If con un adeguato numero di clausole ElseIf. La scelta tra le
due di solito dipende da considerazioni di convenienza e di leggibilità del codice, perché
spesso un blocco Select risulta strutturalmente più semplice di un blocco If. La sintassi
dell'istruzione Select Case è la seguente:
Select Case espressione
Case valore-1
Istruzioni-1
Case valore-2
Istruzioni-2
…
[Case Else
istruzioni-else ]
End Select
“Espressione” è una variabile, una proprietà o una qualunque espressione che può assumere
diversi valori e che va confrontata coi valori elencati nei vari "casi"; nel caso in cui il valore
dell'espressione sia valore-1, saranno eseguite le istruzioni istruzioni-1 (che ovviamente
possono essere più di una); se il valore è valore-2, saranno eseguite le istruzioni istruzioni-2,
e così via per tutti i casi elencati. Se espressione assume un valore che non rientra in quelli
specificati, sarà eseguito il blocco di istruzioni istruzioni-else, se è presente: infatti, come per
l'istruzione If, anche nella Select Case il blocco Else è facoltativo; inoltre, come si è visto più
volte, anche per questa istruzione vale la regola di indicarne la fine con l'istruzione End
Select. Ecco un esempio di istruzione Select case:
Select Case lblEtichetta.Caption
Case "Testo1"
lblEtichetta.FontSize = 12
Case Else
lblEtichetta.FontName = "Arial"
lblEtichetta.FontSize = 11
End Select
Nel caso in cui fosse necessario eseguire lo stesso blocco di istruzioni in corrispondenza di
più valori di espressione, l'elenco di questi valori va separato con la virgola; ad esempio, il
blocco precedente può essere modificato in questo modo:
Select Case lblEtichetta.Caption
Case "Testo1", "Testo2", ""
lblEtichetta.FontSize = 12
Case Else
lblEtichetta.FontName = "Arial"
lblEtichetta.FontSize = 11
End Select
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2209&idArea=10 (3 of 4)12/09/2003 12.14.16
Programmazione.it: Il portale Italiano sull'Information Technology
L'elenco di valori con cui va confrontata l'espressione può assumere anche altre forme, che
risultano particolarmente utili quando l'espressione da controllare è di tipo numerico: è
possibile infatti indicare un intervallo di valori usando l'operatore To o usando l'operatore Is
seguito da un operatore di confronto, come nell'esempio seguente:
Select Case lblEtichetta.FontSize
Case Is < 8, 12 To 14, 18
lblEtichetta.FontSize = 12
Case Else
lblEtichetta.FontName = "Arial"
End Select
In questo esempio la dimensione del carattere dell'etichetta sarà impostato a 12 punti se
esso è minore di 8, compreso tra 12 e 14 (estremi inclusi) oppure uguale a 18; altrimenti la
dimensione del font non sarà modificata, ma il tipo di carattere diventerà Arial. La clausola
Else, pur non essendo obbligatoria, è comunque opportuna per gestire i casi "imprevisti", ad
esempio generando un messaggio di errore per notificare all'utente che il valore
dell'espressione non rientra in quelli previsti. Infine, come per l'istruzione If, anche le
istruzioni Select Case possono essere nidificate: ovvero, dopo ogni clausola Case le
istruzioni da eseguire possono includere un altro blocco Select Case, che a sua volta ne può
contenere un altro, e così via.
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2209&idArea=10 (4 of 4)12/09/2003 12.14.16
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 8
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
Login
Login
Password
- Votanti: 34 - Lettori: 18858 - Livello: Medio
Introduzione
Oggi cominciamo un argomento al quale non sempre viene riservata la giusta importanza nei
manuali di programmazione: il debug del codice. Spesso i manuali trattano il debug in uno
degli ultimi capitoli, come se fosse una delle ultime cose che un programmatore deve sapere
per svolgere bene il proprio lavoro; invece è vero proprio il contrario: avere difficoltà ad
eseguire un corretto ed efficiente debug significa avere difficoltà a creare applicazioni valide
ed affidabili, anche se un programmatore conoscesse a memoria tutte le istruzioni di un
linguaggio. Infatti il debug è la procedura che permette a uno sviluppatore di scovare e
correggere i "bug", ovvero gli errori e le imperfezioni presenti nel codice sorgente di
un'applicazione: poiché anche in applicazioni semplici è quasi impossibile evitare del tutto
errori o semplici sviste, è necessario sempre rivedere il funzionamento del programma
sviluppato con l'aiuto degli strumenti di debug per verificare che tutto funzioni correttamente.
Anzi, nella stragrande maggioranza dei casi le operazioni di debug sono molto più lunghe e
complesse dello sviluppo vero e proprio dell'applicazione e la complessità del debug è
direttamente proporzionale (se non addirittura più che proporzionale) alla complessità
dell'applicazione da testare.
Login
Dimenticata la password? Visual Basic e il Debug
Nell'IDE di Visual Basic un intero menù è dedicato al debug del codice: al suo interno ci sono
Clicca qui
Non sei ancora iscritto? varie voci che consentono allo sviluppatore di eseguire la propria applicazione istruzione
Clicca qui
dopo istruzione, di vedere in ogni momento il valore di tutte le variabili utilizzate
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
dall'applicazione, di interrompere o modificare il flusso di esecuzione delle istruzioni. Altre
funzionalità relative al debug si trovano nel menù Visualizza ed è possibile anche utilizzare
una barra degli strumenti dedicata al debug: basta cliccare col pulsante destro del mouse
sulla barra degli strumenti standard e selezionare la voce "debug". Finora noi ci siamo limitati
a una sorta di primitivo debug utilizzando la finestra Immediata per verificare il valore di certe
variabili dopo l'esecuzione di alcune istruzioni: ora vedremo come sia possibile fare la stessa
cosa in modo molto più potente ed efficiente.
Le voci del menù Debug
Mailing List
Visual Basic
Innanzitutto consideriamo le prime quattro voci del menù "Debug": la prima, "Esegui
istruzione", indica la possibilità di eseguire l'applicazione un'istruzione alla volta, secondo la
modalità comunemente definita "passo-passo" (single step), cioè appunto un passo alla
volta; ad essa è associata il tasto F8, che premuto in sequenza permette di evidenziare man
mano (in giallo, secondo l'impostazione standard modificabile nelle opzioni dell'IDE) la riga di
istruzioni che sta per essere eseguita. Mentre col tasto F5 si dà avvio all'esecuzione
dell'intera applicazione, senza interruzioni (a meno che si verifichi un errore), col tasto F8 si
entra in modalità "interruzione" (visibile nella barra del titolo dell'IDE di Visual Basic) perché
l'esecuzione del codice viene interrotta dopo ogni istruzione fino a quando non si preme
nuovamente F8, il tasto F5 o altri tasti con analoga funzione. Solitamente, premendo per la
prima volta F8 viene evidenziata in giallo la riga di dichiarazione della routine dell'evento load
del form principale dell'applicazione: infatti questo è normalmente il primo evento che viene
generato all'avvio di un'applicazione; è quello che succede, ad esempio, se provate ad
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2277&idArea=10 (1 of 4)12/09/2003 12.14.42
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
Backup PCMCIA
2Gb
Project 2000
eseguire passo-passo il progetto del "campo minato" affrontato qualche lezione fa. Quando si
è in modalità interruzione è possibile conoscere il valore di ogni variabile posizionando
semplicemente il puntatore del mouse sul nome della variabile che interessa: comparirà un
tooltip col valore corrente della variabile. Poiché quando una riga di codice è evidenziata
l'esecuzione è momentaneamente interrotta, è anche possibile modificare le istruzioni per
vedere come cambia la situazione: in questo modo è molto semplice correggere errori banali
come quelli di battitura dei valori da assegnare alle variabili. Errori che, per quanto facili da
risolvere, talvolta possono essere molto difficili da individuare: gli strumenti di debug
forniscono un valido aiuto a questo scopo. Il debug passo-passo può risultare spesso molto
noioso, perché costringe ad eseguire un'istruzione alla volta anche nelle porzioni di codice
che abbiamo già controllato e che sappiamo funzionare bene, prima di arrivare al punto in cui
pensiamo che si verifichi un errore e che quindi va controllato attentamente. Consideriamo
ad esempio il seguente codice:
Private Sub Form_Load()
Dim i as Integer
Call Inizializza
Call Ridimensiona
i = Conta()
MsgBox i, "Contatore"
End Sub
Nell'evento load del form sono richiamate due subroutine e una funzione, la quale restituisce
un integer assegnato alla variabile i: il valore di tale variabile è poi visualizzato tramite
un'istruzione MsgBox; a prescindere dalle subroutine, si vede subito che l'ultima istruzione
contiene un errore, perché il secondo parametro dovrebbe essere una costante numerica
mentre la variabile nel codice è una stringa: ci aspettiamo quindi un errore "Tipo non
corrispondente", o "Type Mismatch". In apparenza, basta premere sei volte il tasto F8 per
giungere all'istruzione incriminata; in realtà, quando viene evidenziata una riga che richiama
una routine, premendo F8 si dà inizio all'esecuzione di quella routine e saranno man mano
evidenziate le righe di codice di quella routine: è chiaro che se le routine "inizializza",
"ridimensiona" e "conta" contengono migliaia di righe di codice oppure cicli con migliaia di
iterazioni, passerà parecchio tempo prima di riuscire a giungere, premendo il tasto F8,
all'ultima istruzione dell'evento load. Per evitare questo inconveniente è possibile usare la
modalità step over (menù "Esegui istruzione/routine"), attivabile con la combinazione di tasti
maiusc+F8: con questa modalità l'IDE di Visual Basic non esegue un'istruzione alla volta, ma
esegue un'istruzione o una routine; se la riga evidenziata in giallo è un'istruzione semplice,
sarà eseguita quell'istruzione come nella modalità passo-passo; se invece è una chiamata a
una funzione o subroutine, premendo F8 (single step) il debug passerà in rassegna le singole
istruzioni della routine, mentre premendo maiusc+F8 (step over) sarà eseguita in un colpo
solo l'intera routine. Se ci troviamo col debug nel bel mezzo di una routine e vogliamo tornare
subito all'istruzione successiva alla sua chiamata, possiamo premere ctrl+maiusc+F8 (menù
"Esci da istruzione/routine"): ciò fa sì che tutte le istruzioni comprese tra quella che sta per
essere eseguita (evidenziata in giallo) e l'uscita dalla routine siano eseguite in un colpo solo.
Spesso capita che si voglia esaminare con attenzione una piccola parte di una routine
(solitamente, per la legge di Murphy, questa piccola parte si trova verso la fine della
routine…), così che la modalità step over non possa essere utilizzata (altrimenti verrebbe
eseguita tutta in una volta l'intera routine e la modalità single step risulterebbe decisamente
noiosa. Per giungere subito al punto del codice che ci interessa esaminare senza consumare
il dito premendo F8 esiste una soluzione molto semplice: impostare un punto di interruzione
(breakpoint) sulla riga iniziale del blocco di codice di cui vogliamo fare il debug; in questo
modo sarà sufficiente premere F5 per avviare il progetto e quando l'esecuzione arriverà al
punto di interruzione l'IDE di Visual Basic passerà in modalità interruzione evidenziando in
giallo la riga su cui abbiamo impostato il breakpoint, consentendoci così di effettuare il debug
senza ulteriori perdite di tempo. Il tasto per attivare un punto di interruzione sulla riga su cui
si trova il cursore è F9, mentre una seconda pressione del tasto F9 rimuove il breakpoint
appena inserito; c'è poi la combinazione ctrl+maiusc+F9 che rimuove in una volta sola tutti i
punti di interruzione presenti nel progetto. Un altro modo ancora per ottenere lo stesso
risultato consiste semplicemente nel posizionare il cursore sulla riga in cui l'esecuzione deve
interrompersi e premere la combinazione ctrl+F8: ciò equivale alla voce di menù "Esegui fino
al cursore" ed è come se su quella riga ci fosse un punto di interruzione. Mentre però il
cursore può trovarsi solo su una riga, i punti di interruzione possono essere molteplici, anche
su righe successive: essi rappresentano quindi uno strumento più flessibile.
Non tutte le righe di codice possono essere contrassegnate da un breakpoint: ad esempio
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2277&idArea=10 (2 of 4)12/09/2003 12.14.42
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
non possono esserlo le etichette utilizzate in combinazione con l'istruzione GoTo, i commenti
oppure le istruzioni di dichiarazione di variabili. Il motivo è che queste righe identificano
"codice non eseguibile": mentre per i commenti e le etichette di riga questo è abbastanza
ovvio, lo è un po' meno per le dichiarazioni di variabili; le dichiarazioni rappresentano codice
"non eseguibile" perché il loro unico scopo è quello di assegnare un nome e determinate
caratteristiche alle variabili dichiarate: lo spazio di assegnazione però è già archiviato
all'avvio dell'applicazione (o della routine se si tratta di variabili locali), infatti, anche se una
variabile è dichiarata in fondo a una routine oppure all'interno di un costrutto if…then, essa è
comunque creata all'inizio della routine (anche se non si verifica la condizione della if). Del
resto non avrebbe senso eseguire più volte la dichiarazione di variabile: la variabile
dichiarata resterebbe sempre la stessa, non possono esserne create più copie distinte ma
con lo stesso nome. L'unica istruzione di dichiarazione che può essere eseguita è l'istruzione
Redim: in questo caso, però, l'istruzione deve anche allocare lo spazio di memoria da
riservare alle matrici dichiarate, quindi è ovvio che si tratta di codice eseguibile.
Il gruppo di voci ”Espressioni di controllo”
Tornando al nostro menù "Debug", il secondo gruppo di voci è quello relativo alle espressioni
di controllo: queste sono espressioni che possono essere aggiunte o rimosse da una lista
visualizzabile nella finestra omonima (vedi menù Visualizza) e consentono di tenere sempre
sotto controllo il loro valore; le espressioni possono essere anche molto complesse e
dipendere da più variabili. Supponiamo ad esempio di voler eseguire il debug di questo
costrutto (tratto dalla routine mnuNew_Click del nostro Campo Minato):
If Mina(4 * x1 + y1).Tag > -1 Then
Mina(4 * x1 + y1).Tag = Mina(4 * x1 + y1).Tag + 1
End If
Per sapere se la condizione del ciclo if è verificata occorre conoscere il valore di x1, di y1 e
della proprietà Tag dell'elemento 4*x1+y1 del vettore Mina: per capire se è corretto che la
condizione si verifichi oppure no potremmo ogni volta controllare il valore di x1 e di y1,
calcolare il risultato dell'espressione 4*x1+y1 e infine verificare che la proprietà Tag della
mina corrispondente sia maggiore di –1. Un modo semplice per evitare tutto ciò è
selezionare l'intera espressione "Mina(4 * x1 + y1).Tag > -1" e aggiungerla alle espressioni di
controllo: nella finestra omonima vedremo in tempo reale il valore assunto da questa
espressione, sapendo così subito se l'istruzione sottesa dal ciclo If sarà eseguita oppure no.
Ognuna di queste espressioni può essere modificata tramite l'apposito comando di menù ed
è inoltre possibile avere un riscontro immediato del valore di un'espressione utilizzando il
comando "controllo immediato", che però non aggiunge l'espressione selezionata alla lista di
espressioni di controllo esistente.
I Contesti
Finora abbiamo usato la finestra immediata per raggiungere lo scopo a cui è destinata la
finestra delle espressioni di controllo. Ogni espressione è caratterizzata da un "contesto",
definito quando si aggiunge l'espressione alla lista e che dipende sostanzialmente dall'area
di validità delle variabili contenute nell'espressione: nei punti in cui tali variabili non sono
definite l'espressione diventa "fuori contesto" e quindi non è possibile conoscere il valore
dell'espressione. Volendo, è possibile aggiungere come espressione di controllo anche una
semplice variabile, ma è possibile tenere sotto controllo tutte le variabili definite, ad esempio,
nel form corrente semplicemente usando la finestra "variabili locali", che permette
agevolmente di navigare la struttura delle matrici, di copiare o modificare il valore di ogni
singola variabile definita in un form o in un modulo. Nella parte alta della finestra è indicata la
routine alla quale appartengono le variabili visualizzate nella finestra (le variabili locali,
appunto) e c'è un pulsante con tre puntini tramite il quale è possibile accedere allo stack di
chiamate. Lo stack delle chiamate è sostanzialmente l'elenco delle routine di cui l'esecuzione
è cominciata ma non è ancora finita: quando in un punto del codice si verifica la chiamata a
una routine, questa comincia ad essere eseguita, ma la routine dalla quale è partita la
chiamata non è ancora terminata, per cui si ritrovano ad essere entrambe sullo stack; se
nella seconda routine c'è una chiamata ad una terza routine, anche quest'ultima sarà
aggiunta allo stack, che funziona come un magazzino in cui l'ultimo lotto aggiunto è il primo
ad essere prelevato (in gergo si dice che lo stack è di tipo LIFO: Last In, First Out): infatti
prima di terminare l'esecuzione della prima routine è necessario che termini la seconda
routine, ma prima che termini quest'ultima è necessario che termini la terza routine, ovvero
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2277&idArea=10 (3 of 4)12/09/2003 12.14.42
Programmazione.it: Il portale Italiano sull'Information Technology
l'ultima di cui è cominciata l'esecuzione. Visualizzando lo stack di chiamate, è possibile
scegliere quale delle routine presenti nello stack deve essere "attivata" per poterne
consultare le variabili locali. Lo stack di chiamate è visibile anche indipendentemente dalla
finestra delle variabili locali, scegliendo l'apposita voce dal menù "Visualizza" o premendo ctrl
+L.
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2277&idArea=10 (4 of 4)12/09/2003 12.14.42
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 9
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
Login
Login
- Votanti: 34 - Lettori: 18858 - Livello: Medio
Le ultime due voci del menù Debug permettono di cambiare, in fase di esecuzione, (o,
meglio, in fase di interruzione) l'ordine delle istruzioni: è una opzione che risulta molto
comoda quando ad esempio ci si accorge di un pezzo di codice difettoso, in cui il difetto
consiste nell'aver eseguito troppo presto (o troppo tardi) una certa istruzione. L'ultima voce
del menù, "Mostra istruzione successiva", permette di tornare subito alla routine in cui il
codice è stato interrotto, mostrando l'istruzione evidenziata in giallo, che è appunto
l'istruzione "successiva" ad essere eseguita; questo comando è utile quando, in modalità
interruzione, si va a controllare altre porzioni di codice perdendo di vista il punto di
interruzione. La penultima voce del menù, invece, serve a "impostare" l'istruzione successiva
da eseguire, ottenendo il risultato descritto prima di modificare l'ordine delle istruzioni;
prendiamo ad esempio in considerazione il codice seguente:
Dim nElementCount as Integer
Password
Redim sNomi(nElementCount) as String
Login
nElementCount=5
Dimenticata la password?
Supponiamo che l'istruzione successiva sia il ridimensionamento del vettore di stringhe
Clicca qui
Non sei ancora iscritto? sNomi: tale ridimensionamento avviene in base alla variabile nElementCount, che
Clicca qui
palesemente dovrebbe contenere il numero di elementi da assegnare al vettore. Peccato
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
però che essa sia inizializzata DOPO il ridimensionamento e non PRIMA, come sarebbe
logico. Questo non genera alcun errore, perché all'esecuzione dell'istruzione Redim la
variabile vale zero e, quindi, il vettore conterrebbe un elemento (quello con indice zero,
appunto): può anche darsi che in certe situazioni sia giusto procedere in questo modo, ma
ragionando astrattamente verrebbe da pensare che l'ordine corretto delle istruzioni sia:
nElementCount=5
Redim sNomi(nElementCount) as String
Mailing List
Visual Basic
Quando, procedendo col debug, arriviamo all'istruzione Redim e ci accorgiamo del nostro
errore, possiamo posizionare il cursore sull'istruzione nElementCount=5 e premere ctrl+F9 (o
l'equivalente comando "Imposta istruzione successiva"): automaticamente questa istruzione
sarà evidenziata in giallo ad indicare che essa è diventata l'istruzione successiva da
eseguire. Dopo averla eseguita premendo F8 possiamo impostare come istruzione
successiva quella di ridimensionamento: a questo punto la variabile nElementCount è già
inizializzata a 5 e, quindi, il nostro vettore di stringhe sarà creato correttamente con sei
elementi (da 0 a 5) premendo ancora F8. Naturalmente, dopo aver verificato che in questo
modo il programma viene eseguito correttamente potremo interrompere l'esecuzione e
invertire l'ordine in cui le istruzioni sono scritte, in modo che ad una successiva esecuzione
non dovremo più preoccuparci di impostare manualmente l'ordine delle istruzioni coi comandi
del menù Debug. Un altro modo per ottenere lo stesso risultato è quello di trascinare la
freccia gialla che compare nella barra a sinistra nella finestra del codice rilasciandola
all'altezza dell'istruzione da impostare come successiva; l'unica limitazione è che l'istruzione
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2347&idArea=10 (1 of 4)12/09/2003 12.15.03
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
successiva da eseguire deve essere compresa nella stessa routine in cui il codice è stato
interrotto.
Quando un progetto viene avviato dall'IDE di Visual Basic tramite il tasto F5 e si verifica un
errore è lo stesso IDE a interrompere l'esecuzione del codice mostrando il punto in cui si
verifica l'errore e consentendo, quindi, di effettuare il debug di quella porzione di codice;
ovviamente, però, questo non accade se l'errore si verifica quando il programma è già stato
compilato: infatti in tal caso non c'è l'IDE di Visual Basic a supportare i comandi di debug e,
Videoproiettore NEC se non è stato previsto un modo per intercettare, e possibilmente correggere gli errori di runtime, l'unico esito possibile è il blocco dell'applicazione. Occorre quindi escogitare un modo
per permettere al programma stesso di sapere quando si verifica un eventuale errore e
provvedere, se possibile, alla sua correzione permettendo la normale continuazione
dell'esecuzione del codice: si tratta del cosiddetto error handling, ovvero intercettazione degli
errori. Visual Basic mette a disposizione una comoda istruzione per implementare l'error
handling: l'istruzione On error goto.
Canon Eos 3
Questa istruzione ordina al programma di saltare a un certo punto del codice, identificato da
un'etichetta, quando si verifica un errore; solitamente la struttura di una routine con l'error
handling è simile alla seguente:
Sub Prova()
'dichiarazioni di variabili
'…
'…
On Error GoTo ErrorHandling
'istruzioni proprie della routine
'…
'…
'…
Exit Sub
ErrorHandling:
'codice destinato all'intercettazione dell'errore
'…
'…
End Sub
Si possono notare diverse cose: innanzitutto, l'istruzione che permette l'intercettazione degli
errori (On Error GoTo) è la prima istruzione eseguibile della routine subito dopo le
dichiarazioni di variabili; questo perché è importante intercettare un errore in qualunque
punto del codice avvenga, infatti l'intercettazione viene abilitata solo dall'istruzione On Error
GoTo in poi: se tale istruzione fosse posta, supponiamo, a metà routine, un eventuale errore
che si verificasse prima di essa non sarebbe intercettato; invece ponendola all'inizio della
routine si ha la sicurezza che qualunque errore si verifichi durante l'esecuzione della routine
sarà intercettato. L'istruzione On Error GoTo avverte che quando si verifica un errore
l'esecuzione del programma deve saltare al punto identificato dall'etichetta "ErrorHandling",
che si trova in fondo alla routine, subito dopo l'istruzione Exit Sub: dopo l'etichetta ci sono le
istruzioni che esaminano l'errore verificato (tipicamente con una Select Case, come vedremo
in seguito) e tentano di risolverlo. L'etichetta è preceduta dall'istruzione Exit Sub per un
motivo molto semplice: evitare che la parte di codice destinata a intercettare gli errori (ovvero
le istruzioni successive all'etichetta) sia eseguita ad ogni esecuzione della routine. Senza Exit
Sub, infatti, le istruzioni di error handling sarebbero eseguite in ogni caso, indipendentemente
dal verificarsi o meno di un errore (si tratta pur sempre di istruzioni come le altre); questo può
comportare a sua volta degli errori, oltre ad essere logicamente scorretto. Per assicurarsi
quindi che quelle istruzioni siano eseguite solo quando effettivamente si verifica un errore
occorre farle precedere da un'istruzione che interrompa la routine nel caso in cui non ci sia
stato alcun errore: l'istruzione Exit Sub, per l'appunto.
È bene precisare a questo punto che le istruzioni per l'intercettazione e la gestione degli
errori riguardano soltanto gli errori di run-time, non quelli di compilazione o i cosiddetti errori
logici: infatti gli errori di compilazione sono, in parole povere, gli errori di sintassi, che
vengono intercettati dall'IDE di Visual Basic sin dall'avvio dell'applicazione o al momento di
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2347&idArea=10 (2 of 4)12/09/2003 12.15.03
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
eseguire una procedura (ovvero durante la compilazione del programma); gli errori logici,
invece, sono quelli più insidiosi perché non causano un errore vero e proprio, ma fanno sì
che il programma non si comporti nel modo previsto dallo sviluppatore. Un banale esempio di
errore logico potrebbe essere quello in cui ci si dimentica di convertire in modo appropriato
un'unità di misura generando risultati assurdi (ad esempio una performance del 567%
anziché del 5,67%): il programma funziona normalmente, nel senso che non si verifica alcun
errore che ne blocchi l'esecuzione, però non fa quello che dovrebbe, genera risultati sbagliati
a causa di una o più istruzioni impostate in modo scorretto. Gli errori di run-time, invece,
sono quelli che interrompono l'esecuzione del programma e, se non sono gestiti
correttamente, possono mandarlo in crash senza troppi scrupoli.
Quando si verifica un errore di run-time, Visual Basic imposta le proprietà dell'oggetto Err,
che devono essere esaminate al fine di capire cosa è successo e come reagire: ogni errore
standard definito in Visual Basic è identificato da un numero e da una descrizione (proprietà
"Number" e "Description" dell'oggetto Err); vediamo ad esempio questo pezzo di codice:
Dim nRisultato as Integer
nRisultato= CLng(txtNumeratore(mnIndex).Text)/CLng(txtDenominatore(mnIndex).
Text)
dove la variabile mnIndex c’è un Integer dichiarata a livello di modulo che indica quali
elementi delle due matrici di controlli TextBox "txtNumeratore" e "txtDenominatore" occorre
considerare; per il momento non ci interessa sapere come viene calcolato mnIndex né cosa
indichino esattamente le variabili coinvolte, basta sapere che anche in un'istruzione semplice
come questa i possibili errori sono almeno quattro:
mnIndex non è compreso tra i limiti inferiore e superiore delle matrici di controlli:
•
"indice non compreso nell'intervallo" (errore 9);
il contenuto dei TextBox non è un numero, di conseguenza la funzione CLng()
•
fallisce: "Tipo non corrispondente" (errore 13);
txtDenominatore(mnIndex).Text è uguale a "0": "divisione per zero" (errore 11);
•
il rapporto tra i numeri contenuti nei due TextBox è esterno all'intervallo –32768/
•
+32767 valido per gli integer: "Overflow" (errore 6).
Per intercettare un'eventuale errore scriviamo allora un'apposita routine:
Dim nRisultato as Integer
On Error GoTo Errore
nRisultato= CLng(txtNumeratore(mnIndex).Text)/CLng(txtDenominatore(mnIndex).
Text)
Exit Sub 'oppure Exit Function, a seconda dei casi
Errore:
Select Case Err.Number
Case 6 'Overflow
MsgBox "Il numeratore è troppo grande", vbOkOnly
Case 9 ' indice non compreso nell'intervallo
MsgBox "Non esiste un TextBox con indice " & CStr(mnIndex), vbOkOnly
Case 11 ' divisione per zero
MsgBox "Il valore inserito in txtDenominatore deve essere diverso da
zero", vbOkOnly
Case 13 ' Tipo non corrispondente
MsgBox "Non hai inserito un valore numerico", vbOkOnly
End Select
End Sub 'termine della routine
In questo semplice caso ci siamo limitati a visualizzare il tipo di errore riscontrato, senza
tentare di risolverlo; d'altra parte sarebbe un compito abbastanza arduo da fare
automaticamente, senza ricorrere all'aiuto dell'utente. In altri casi invece è più semplice
risolvere in modo automatico l'errore. Supponiamo, ad esempio, di voler aprire un file
inesistente in sola lettura:
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2347&idArea=10 (3 of 4)12/09/2003 12.15.03
Programmazione.it: Il portale Italiano sull'Information Technology
Open "c:\pippo.txt" For Input Access Read as #1
Poiché l'apertura del file è in sola lettura, se esso non esiste non sarà automaticamente
creato (cosa che invece accadrebbe se il file fosse aperto anche in scrittura); per risolvere il
problema possiamo intercettare l'errore:
On Error GoTo Errore
Open "c:\pippo.txt" For Input Access Read as #1
Exit Sub
Errore:
If Err.Number=53 Then ' descrizione: impossibile trovare il file
Open "c:\pippo.txt" For Output Access Write as #1
Close 1
Resume
End If
End Sub
In questo caso, se il file non esiste viene creato tentando di aprirlo in modalità di scrittura,
dopodiché il controllo torna all'istruzione che ha generato l'errore grazie all'istruzione
"Resume", che vedremo meglio nella prossima lezione. Per ora basti notare che esiste un
modo più semplice ed elegante di evitare questo errore banale, ovvero controllare in anticipo
se il file esiste, prima della sua apertura:
If Len(Dir$("C:\pippo.txt"))=0 Then
Open "c:\pippo.txt" For Output Access Write as #1
Close 1
End If
Open "c:\pippo.txt" For Input Access Read as #1
La funzione Dir() restituisce il nome del file indicato come argomento: se restituisce la stringa
nulla (di lunghezza zero), significa che il file non esiste: quindi viene aperto in scrittura (cioè
creato) e subito chiuso, dopodiché viene normalmente aperto in lettura; se invece esiste già,
viene aperto soltanto in lettura.
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2347&idArea=10 (4 of 4)12/09/2003 12.15.03
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 10
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
- Votanti: 34 - Lettori: 18858 - Livello: Medio
Nelle routine di gestione degli errori viste nella lezione precedente abbiamo considerato
alcuni errori specifici, che sapevamo avrebbero potuto verificarsi durante l'esecuzione
dell'applicazione; ovviamente non si può prevedere tutto, pertanto è necessario gestire
anche gli errori non previsti. Un modo banale, ma che ha il vantaggio di impedire la chiusura
inaspettata del programma, è quello di visualizzare un messaggio con la descrizione
dell'errore: la clausola "else" dell'istruzione Select Case risulta particolarmente utile:
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
Sub Prova()
On Error GoTo error_handler
Login
Login
…
error_handler:
Password
Select Case Err.Number
Case 9
…
Case 13
Login
…
Dimenticata la password?
Case
Else 'qualunque errore non gestito nei casi precedenti
Clicca qui
MsgBox Err.Description, vbOkOnly, "Errore" & Str$(Err.Number)
Non sei ancora iscritto?
End Select
Clicca qui
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
End Sub
Mailing List
Visual Basic
Un altro sistema per gestire gli errori di run-time è quello di utilizzare l'oggetto Debug,
corrispondente alla finestra Immediata, già utilizzata in varie occasioni durante le lezioni
precedenti. L'oggetto Debug dispone di due metodi: il primo è il metodo Print, che visualizza
nella finestra Immediata il valore del parametro passato alla funzione; il secondo, che forse
non tutti conoscono, è il metodo Assert, che sospende l'esecuzione del codice se si verifica
una determinata condizione. Col metodo Assert il programmatore fa un'asserzione che, nel
caso in cui risulti non verificata, determina l'interruzione del programma in modo che si possa
capire il motivo per cui la condizione espressa è risultata falsa. Ad es., supponiamo che alla
centesima iterazione di un ciclo si verifichi un errore di cui non capiamo l'origine; potremmo
impostare un punto di interruzione all'inizio del ciclo e procedere passo-passo fino alla
centesima iterazione: soluzione alquanto noiosa. Potremmo anche scrivere una routine di
gestione degli errori e impostare un breakpoint su questa routine, ma così il codice sarebbe
interrotto dopo che si è verificato l'errore, mentre potrebbe essere utile intercettare l'errore
prima che si verifichi, sfruttando il fatto che sappiamo che l'errore si verifica alla centesima
iterazione. Una soluzione conveniente è allora quella di usare l'istruzione Debug.Assert:
For i = 0 to 1000
Debug.Assert i<99
… 'istruzioni da eseguire ciclicamente
Next i
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2430&idArea=10 (1 of 4)12/09/2003 12.15.19
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
Frontpage 2000
Sveglia Orolà
Il metodo Assert dell'oggetto Debug controlla se il contatore del ciclo è minore di 99: in caso
affermativo, l'esecuzione del codice prosegue normalmente, altrimenti l'IDE di Visual Basic
interromperà l'esecuzione evidenziando in giallo la riga "Debug.Assert i<99"; infatti, la prima
volta che la condizione espressa risulta falsa è quando i=99, ovvero quando siamo alla
centesima iterazione del ciclo, e sappiamo che in questa iterazione si verifica il misterioso
errore. Procedendo passo passo con F8 potremo finalmente capire cosa origina l'errore.
L'istruzione Debug.Assert è equivalente all'istruzione Stop condizionata a un'istruzione If:
For i = 0 to 1000
If i>=99 then
Stop
End If
… 'istruzioni da eseguire ciclicamente
Next i
L'oggetto Debug è utilizzabile esclusivamente dall'ambiente di progettazione, poiché una
volta che il programma è compilato le istruzioni Print e Assert vengono automaticamente
eliminate; infatti, quando avviamo un eseguibile già compilato (un file *.exe per intenderci)
non abbiamo a disposizione una finestra Immediata in cui vedere il contenuto di variabili o
espressioni né abbiamo modo di interrompere il codice a nostro piacimento. L'oggetto Debug
è utilizzabile solo nella fase di debug dell'applicazione, pertanto solo all'interno dell'ambiente
di progettazione, quando il codice può essere interpretato istruzione per istruzione anziché
essere compilato tutto in una volta come quando si crea l'eseguibile. La differenza
sostanziale è che quando il codice è interpretato esso è eseguito per il tramite dell'ambiente
di progettazione, che si occupa di tradurre ogni istruzione in codice macchina eseguibile dal
processore; invece, quando il codice è compilato in un file eseguibile viene tradotto subito in
codice macchina, perciò la velocità di esecuzione del codice compilato è maggiore di quella
del codice interpretato, poiché si elimina il passaggio intermedio dell'interprete. In realtà per
Visual Basic (almeno fino alla versione 6) il discorso è un po' più complesso, perché è
possibile scegliere se compilare un progetto in "codice nativo" o in "p-code" (pseudo-codice):
il codice nativo è quello eseguito direttamente dal processore, mentre il p-code è un codice
intermedio che necessita di un'ulteriore elaborazione da parte delle librerie di run-time di
Visual Basic, prima fra tutte la “famigerata” msvbvm60.dll (per la versione 6). Questa dll
contiene routine di utilità generale, ad es. per l'avvio e la chiusura dell'applicazione oppure
per la conversione dei tipi di dati; anche l'interfaccia dell'oggetto Err, visto nella lezione
precedente, è fornita da questa libreria. Essa si occupa, tra l'altro, anche di convertire lo
pseudo-codice in codice nativo: in effetti lo pseudo-codice è un codice interpretato; tuttavia
anche le applicazioni compilate direttamente in codice nativo hanno bisogno dei servizi offerti
da questa libreria: ecco il motivo per cui quando si vuole distribuire un'applicazione creata
con Visual Basic è necessario distribuire anche le librerie di run-time, che non sempre
risultano già installate nei pc destinatari dell'applicazione.
Passiamo ora alla descrizione di alcune funzioni per la manipolazione dei tipi di dati,
cominciando dalle stringhe; alcune funzioni le abbiamo già incontrate strada facendo, ma ora
è il caso di descriverle con qualche dettaglio in più. Innanzitutto è bene chiarire una
differenza che spesso non viene percepita da alcuni programmatori: per la manipolazione
delle stringhe esistono funzioni che terminano con il simbolo del dollaro $ e funzioni con lo
stesso nome ma senza questo simbolo; ad es., esiste la funzione Left() e la funzione Left$().
La differenza dovrebbe essere chiara per coloro che hanno avuto la possibilità di
programmare con le vecchie versioni del basic: il simbolo del dollaro, infatti, era utilizzato (e
può essere utilizzato ancora adesso, per compatibilità) come carattere di dichiarazione del
tipo String; in altre parole, scrivere:
Dim sStringa$
è equivalente a scrivere:
Dim sStringa as String
Pertanto, le funzioni Left() e Left$() svolgono esattamente lo stesso compito, differenziandosi
unicamente per il tipo di dati restituito e per il tipo di argomenti: Variant la prima, String la
seconda. Questo implica che la funzione Left$() sia più efficiente della corrispondente Left()
perché evita l'implicita conversione delle variabili coinvolte da Variant a String: questo è il
motivo per cui solitamente si consiglia di usare la versione "specializzata" della funzione al
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2430&idArea=10 (2 of 4)12/09/2003 12.15.19
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
posto della versione più generica. La funzione Left$(), dunque, restituisce i primi n caratteri di
una determinata stringa, che viene passata alla funzione come argomento insieme al numero
n di caratteri:
Left$("pippo", 3)
restituisce "pip". Se il numero di caratteri è 0, la funzione restituisce una stringa nulla; se
invece è maggiore della lunghezza della stringa originale, la funzione restituisce tutta la
stringa: Left$("pippo", 6) restituisce "pippo". Analogamente a Left$() c'è la funzione Right$()
che restituisce invece gli ultimi n caratteri (o, se si preferisce, i primi n caratteri da destra):
Right$("pippo", 0) restituisce ""
Right$("pippo", 3) restituisce "ppo"
Right$("pippo", 6) restituisce "pippo"
E se si vuole estrarre una parte intermedia di una stringa? Si usa la funzione Mid$(), che a
differenza di Left$() e Right$() necessita di un ulteriore parametro che rappresenta la
posizione iniziale della stringa da estrarre:
Mid$(sStringa as String, lStart as Long, [Lunghezza])
Ad es.:
Mid$("pippo va a sciare", 7, 4) restituisce "va a"
Il secondo parametro indica la posizione del primo carattere della sottostringa da estrarre,
mentre il terzo specifica la lunghezza della sottostringa; è chiaro che il secondo parametro
deve essere maggiore di zero, ma non è necessario che sia minore della lunghezza della
stringa originale: nel caso in cui il parametro lStart sia maggiore della lunghezza di sStringa,
la funzione restituirà la stringa nulla. La lunghezza della sottostringa da estrarre non è un
parametro obbligatorio, poiché ove mancasse la funzione Mid$() restituirebbe tutti i caratteri
a partire da lStart; è un po' come usare la funzione Right$(), con la differenza che non
occorre sapere quanti caratteri estrarre. Tuttavia le seguenti espressioni sono equivalenti:
Right$("pippo va a sciare", Len("pippo va a sciare") – 11)
Mid$("pippo va a sciare", 12)
Se sappiamo che la sottostringa che ci interessa (in questo caso "sciare") comincia al
dodicesimo carattere, possiamo usare la funzione Right$() specificando come secondo
parametro la lunghezza della stringa originale meno la posizione iniziale meno uno, poiché
prima di sciare ci sono 12 – 1 = 11 caratteri da ignorare. Spesso non si conosce a priori la
posizione iniziale della sottostringa da estrarre né quella finale (ovvero la sua lunghezza); in
molti casi si possono ottenere queste informazioni con la funzione InStr(), che ricerca una
stringa all'interno di un'altra; la sintassi completa è:
Instr(lStart, sStringaOriginale, sStringaCercata, Confronto)
Il primo parametro determina la posizione da cui iniziare la ricerca, il secondo indica la
stringa originale in cui cercare una particolare sequenza di caratteri specificata dal terzo
parametro; l'ultimo parametro determina il tipo di ricerca: come già accennato nella lezione 7,
è possibile specificare la costante vbTextCompare per una ricerca case insensitive, che cioè
ignora la differenza tra minuscole e maiuscole; oppure la costante vbBinaryCompare per una
ricerca case sensitive, che tiene conto della differenza tra maiuscole e minuscole perché
considera il valore binario di ogni carattere delle stringhe confrontate, valore determinato dal
corrispondente codice ANSI. Esiste poi anche la costante vbDatabaseCompare, ma è usata
solo con Microsoft Access. Se questo parametro viene omesso, l'impostazione di default è
quella di usare un confronto binario (quindi case sensitive), a meno che sia specificata
l'istruzione Option Compare Text nella sezione delle dichiarazioni del form o del modulo in
cui è utilizzata la funzione InStr(). L'istruzione Option Compare è analoga all'istruzione Option
Explicit, ma determina a livello di modulo l'impostazione predefinita per confrontare le
stringhe: come la funzione InStr(), anche Option Compare accetta alternativamente tre tipi di
confronto:
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2430&idArea=10 (3 of 4)12/09/2003 12.15.19
Programmazione.it: Il portale Italiano sull'Information Technology
Option Compare Text 'confronto testuale
Option Compare Binary 'confronto binario
Option Compare Database
La funzione InStr() restituisce quindi la posizione iniziale della stringa cercata all'interno di
quella originale: si tratta ovviamente di un numero intero positivo; infatti, se la ricerca fallisce,
la funzione restituisce 0, interpretabile come valore logico False nelle espressioni
condizionali. Un tipico esempio in cui si fa ricorso alla funzione InStr() è quello di separare il
nome di un file dal suo percorso, come già visto nella lezione 7; talvolta però risulta più
comodo usare la funzione InStrRev(), del tutto analoga a InStr(), ma che ricerca la
sottostringa a partire dalla fine della stringa iniziale anziché dall'inizio. Così, se voglio estrarre
il nome di un file dal suo percorso, basterà scrivere:
sNomeFile = Mid$(sPath, InStrRev(sPath, "\")+1)
La funzione InStrRev() restituisce la posizione del primo backslash a partire dal fondo di
sPath (quindi in effetti la posizione dell'ultimo backslash); ottenuta questa informazione, la
funzione Mid$() senza il terzo argomento restituisce tutto ciò che segue l'ultimo backslash,
ovvero il nome del file; notate che se non avessimo aggiunto 1 al valore restituito da InStrRev
(), la funzione Mid$() avrebbe restituito anche l'ultimo backslash seguito dal nome del file.
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2430&idArea=10 (4 of 4)12/09/2003 12.15.19
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 11
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
- Votanti: 34 - Lettori: 18858 - Livello: Medio
Le opzioni dell'istruzione Option Compare, viste nella lezione precedente, influenzano il
comportamento non solo delle funzioni InStr e InStrRev, ma di qualunque confronto tra
stringhe. Solitamente, quando si vogliono confrontare due stringhe a prescindere dal
carattere minuscolo o maiuscolo, si usava lo stratagemma di convertirle entrambe nella
stessa forma (minuscola o maiuscola) usando le funzioni Lcase$() e Ucase$()
(rispettivamente Lower e Upper Case); in questo modo l'utente avrebbe potuto evitare di
scrivere correttamente la stringa da cercare, ad es. un cognome in un elenco telefonico:
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
Do Until Ucase$(sCognome)=Ucase$(sElenco(lCount))
LCount=lCount+1
Loop
Login
Login
Password
Fare ricerche più complesse, ad es. utilizzando i caratteri jolly (* e ?), richiede un uso
articolato delle funzioni viste fin qui, ovvero Left$, Right$, Mid$; ad es., per cercare un
cognome del tipo Abr?i* si potrebbe implementare un ciclo di questo tipo:
Do Until (Left$(sCognome,3)=Left$(sElenco(lCount),3) and _
Mid$(sCognome,5,1)= Mid$(sElenco(lCount),5,1))
Login
lCount=lCount+1
Dimenticata la password? Loop
Clicca qui
Non sei ancora iscritto?
Esiste però un metodo più semplice, che consiste nell'uso dell'operatore Like: questo
Clicca qui
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
operatore, in base all'impostazione definita con l'istruzione Option Compare (l'impostazione
predefinita è sempre Binary), restituisce un valore booleano che indica la corrispondenza tra
due stringhe, permettendo anche l'uso dei caratteri jolly; ad es.:
"Abraini" like "Abr?i*" restituisce True
"Abraini" like "abr?i*" restituisce True (se si è specificato Option Compare
Text)
I caratteri jolly possono comparire solo nella stringa di destra dell'operatore like, ovvero nel
"criterio" con cui si confronta la stringa:
Mailing List
Visual Basic
sStringa Like sCriterio
Ovviamente, caratteri come asterisco e punto di domanda possono comparire anche
nell'espressione a sinistra, ma non vengono interpretati come caratteri jolly; per chi non lo
sapesse, il preciso significato di questi particolari caratteri è il seguente:
zero o più caratteri ("" Like "*" restituisce True, come anche "abc" Like "*c")
•
? un qualunque carattere (uno e uno solo però: "abc" Like "?abc" restituisce
•
False)
Vediamo qualche altro esempio:
"abc" Like "*abc": Risultato: True
•
"abc*" Like "*abc": Risultato: False (l'asterisco a sinistra di Like è un carattere
•
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2593&idArea=10 (1 of 5)12/09/2003 12.15.36
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
come gli altri)
"abc*" Like "*abc?": Risultato: True
•
"*" Like "?": Risultato: True
•
"" Like "?": Risultato: False
•
Fujitsu 325A
Esiste poi un altro carattere "jolly", utilizzabile con l'operatore Like (l'asterisco e il punto di
domanda hanno validità universale, questo no):
"#"
Project 2000
una e una sola cifra da 0 a 9
Ad es. "abc0k" Like "*#?" restituisce True, come anche "abc0k" Like "*??" oppure anche
"abc0k" Like "*": una cifra è pur sempre un carattere, pertanto è riconosciuto dai caratteri jolly
* e ?, ma all'occorrenza è possibile identificarlo come cifra usando #.
Infine, è possibile specificare un determinato intervallo di caratteri tra parentesi quadre; ad
es., al posto di usare # per le cifre è possibile usare l'intervallo [0-9]:
"abc0k" Like "*[0-9]?" restituisce true
C'è tutta una sintassi particolare, e anche un po' complessa, per usare compiutamente le
opportunità offerte dall'operatore Like, ma anche un uso semplice con i tre caratteri jolly più
utilizzati (*, ?, #) è già molto potente. Ad es., per verificare che un codice alfanumerico
corrisponda (almeno nella forma) a un codice fiscale basta scrivere:
sCodice Like "[A-Z][A-Z][A-Z][A-Z][A-Z][A-Z]##[A-Z]##[A-Z]###[A-Z]"
Effettuare la stessa verifica usando Left(), Mid(), e If sarebbe stato molto più complesso.
Per mettere in pratica le nozioni imparate in queste due lezioni riprendiamo in mano il
progetto sul nostro blocco note interrotto nell'undicesima lezione della prima parte e
aggiungiamo un menu "Cerca" del tutto simile a quello del blocco note di Windows, anzi con
qualcosa in più: la ricerca con caratteri jolly. I nomi e le caption delle voci di menù sono
naturalmente a discrezione del programmatore; io seguirò questo standard:
Caption Cerca; Name mnuCerca
Caption Trova; Name mnuTrova
Caption Trova Successivo; mnuTrovaAncora
MnuCerca è quello che compare sulla barra dei menu, gli altri sono sottomenu.
Per permettere all'utente di indicare la parola da cercare basterebbe un banale inputbox, ma
vale la pena costruire una finestra un po' più sofisticata, con una textbox (txtTrova) per
scrivere la stringa da cercare, un pulsante per avviare la ricerca (cmdTrova), uno per
chiudere la finestra (cmdChiudi), tre optionbutton (optSu, optGiu, optTutto) per scegliere la
direzione di ricerca, una checkbox per abilitare la ricerca case sensitive (chkCaseSens). Il
form si può chiamare frmTrova.
Il codice di ricerca della stringa sarà tutto racchiuso nel nuovo form appena creato, cosicché
il codice del menu "Cerca" sarà molto semplice: la voce mnuTrova dovrà soltanto richiamare
la finestra frmTrova:
Private Sub mnuTrova_Click()
frmTrova.Show vbModal
End Sub
La costante vbModal indica che il form frmTrova è modale (si dice anche "a scelta
obbligatoria") rispetto al form che lo richiama (frmNotePad), cioè non è possibile tornare al
form originale prima di aver compiuto qualche scelta (anche la sola chiusura) con la finestra
in primo piano.
La voce mnuTrovaAncora dovrà invece richiamare la routine del pulsante cmdTrova, che
scriveremo in seguito:
Private Sub mnuTrovaAncora_Click()
frmTrova.cmdTrova_Click
End Sub
Per fare ciò, però, è necessario che la routine cmdTrova_Click sia pubblica, quindi visibile
anche da frmNotePad, perciò occorre sostituire "Private" con "Public" nella dichiarazione
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2593&idArea=10 (2 of 5)12/09/2003 12.15.36
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
dell'evento Click:
Public Sub cmdTrova_Click()
.....
End Sub
Non è questa un'operazione molto raccomandabile, perché va a modificare delle
dichiarazioni generate direttamente dall'IDE di Visual Basic; in realtà non ha molto senso che
la routine di un evento sia "pubblica": la generazione dell'evento avviene privatamente
rispetto al form che contiene il controllo a cui l'evento si riferisce. Richiamare la routine non
corrisponde propriamente alla generazione dell'evento, anche se in buona sostanza le due
cose sono uguali. In alternativa, è possibile non modificare la dichiarazione dell'evento Click
e impostare a True il valore del pulsante cmdTrova:
Private Sub mnuTrovaAncora_Click()
frmTrova.cmdTrova.Value=True
End Sub
Possiamo ora dedicarci al codice del form frmTrova, cominciando dalla cosa più semplice: la
chiusura del form:
Private Sub cmdChiudi_Click()
Me.Hide
End Sub
Il form viene solo nascosto e non completamente scaricato, perché è sempre possibile
continuare la ricerca della stringa specificata usando il menu mnuTrovaAncora, che deve
poter accedere alla proprietà Text di txtTrova: se il form venisse scaricato, il contenuto di
txtTrova andrebbe perso.
In alternativa, si può memorizzare il contenuto del textbox in una variabile pubblica di
frmNotePad, il che permetterebbe comodamente di scaricare frmTrova.
All'apertura del form sarebbe bene che il pulsante cmdTrova sia disabilitato, perché il
txtTrova è vuoto: se fosse abilitato e l'utente lo premesse subito, bisognerebbe cercare una
stringa nulla o visualizzare un messaggio di errore che avverta di indicare la stringa da
cercare; è possibile evitare tutto ciò semplicemente disabilitando per default (ovvero in fase
di progettazione) il pulsante e abilitarlo quando il txtTrova contiene qualche carattere,
sfruttando l'evento Change:
Private Sub txtTrova_Change()
If Len(txtTrova.Text) Then
cmdTrova.Enabled = True
Else
cmdTrova.Enabled = False
End If
End Sub
Prima di scrivere la routine di ricerca è opportuno specificare l'istruzione Option Compare
Binary nella sezione delle dichiarazioni del form; questo perché è semplice impedire un
confronto case sensitive usando le funzioni Lcase o Ucase, ma è complicato tornare a un
confronto case sensitive con l'operatore Like se l'impostazione di default è Option Compare
Text.
Veniamo ora alla routine cmdTrova_Click: volendo scrivere una routine abilitata alla ricerca
con caratteri jolly (per semplicità gli stessi usati dall'operatore like: *, ?, #), è opportuno
procedere in questo modo: data l'impossibilità di cercare direttamente una stringa che
contenga caratteri jolly, occorre suddividere il testo cercato in più parti, isolando il testo
"puro" dai caratteri jolly. Ad es., se l'utente vuole cercare "abc*", non bisognerà trovare
esattamente "abc*", bensì "abc": qualunque stringa cominci con "abc" soddisferà i requisiti
indicati dall'utente. Più complesso è il caso di una stringa del tipo "##a*123??": in questo
caso bisognerà cercare la lettera "a" oppure i numeri "123" e successivamente verificare che
i caratteri circostanti corrispondano al criterio indicato. È ovvio che specificare soltanto "*" o
"?" come testo da cercare non ha molto senso…
Come prima cosa occorre eliminare gli asterischi iniziali e finali: cercare "*pippo*" è del tutto
equivalente a cercare "pippo", ma il secondo caso è per noi molto più facile da trattare:
Public Sub cmdTrova_Click()
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2593&idArea=10 (3 of 5)12/09/2003 12.15.36
Programmazione.it: Il portale Italiano sull'Information Technology
Dim
Dim
Dim
Dim
sTestoPuro(1) As String
lCount As Long
lInizio As Long
lFine As Long
Do
lInizio = lInizio + 1
Loop While Mid$(txtTrova.Text, lInizio, 1) = "*"
lFine = Len(txtTrova.Text)
Do While Mid$(txtTrova.Text, lFine, 1) = "*"
lFine = lFine - 1
Loop
txtTrova.Text = Mid$(txtTrova.Text, lInizio, lFine - lInizio + 1)
End Sub
Prima si cerca il primo carattere non-asterisco, poi si cerca l'ultimo carattere non-asterisco e
infine si estrae il testo compreso tra i primi e gli ultimi asterischi (asterischi esclusi): questo
sarà il vero testo da cercare.
A questo punto occorre isolare il testo "puro" dai caratteri jolly:
Public Sub cmdTrova_Click()
Dim sTestoPuro(1) As String
Dim lCount As Long
Dim lInizio As Long
Dim lFine As Long
Do
lInizio = lInizio + 1
Loop While Mid$(txtTrova.Text, lInizio, 1) = "*"
lFine = Len(txtTrova.Text)
Do While Mid$(txtTrova.Text, lFine, 1) = "*"
lFine = lFine - 1
Loop
txtTrova.Text = Mid$(txtTrova.Text, lInizio, lFine - lInizio + 1)
'primo testo "puro"
lInizio = 0
Do
lInizio = lInizio + 1
Loop While Mid$(txtTrova.Text, lInizio, 1) Like "[?#]"
lFine = lInizio
Do
lFine = lFine + 1
Loop Until Mid$(txtTrova.Text, lFine, 1) Like "[*?#]" Or lFine >= Len
(txtTrova.Text)
sTestoPuro(0) = Mid$(txtTrova.Text, lInizio, lFine - lInizio)
'ultimo testo "puro"
lInizio = lFine
Do
lInizio = lInizio + 1
Loop While Mid$(txtTrova.Text, lInizio, 1) Like "[*?#]"
lFine = lInizio
Do
lFine = lFine + 1
Loop Until Mid$(txtTrova.Text, lFine, 1) Like "[*?#]" Or lFine >= Len
(txtTrova.Text)
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2593&idArea=10 (4 of 5)12/09/2003 12.15.36
Programmazione.it: Il portale Italiano sull'Information Technology
sTestoPuro(1) = Mid$(txtTrova.Text, lInizio, lFine - lInizio - 1)
End Sub
Per cercare una sequenza di testo "puro" si cerca il primo carattere non-jolly, poi si va avanti
fino a trovare un altro carattere jolly o ad arrivare alla fine della stringa; infine si estrae il testo
trovato.
Le sequenze di testo "puro" da cercare sono due: la prima e l'ultima. Quello che sta in mezzo
in fondo non ci interessa, perché una volta trovati gli estremi ci basterà confrontare il testo
compreso tra questi estremi con il criterio indicato dall'utente: l'operatore Like ci renderà
molto semplice questo confronto.
Se ad es. l'utente cerca "pippo*abc*def*carlotta", a noi basta cercare "pippo" e "carlotta",
dopodiché confronteremo l'intero testo compreso tra "pippo" e "carlotta" con la stringa
"pippo*abc*def*carlotta" ricercata dall'utente. Se la sequenza di testo "puro" è solo una, la
ricerca potrebbe complicarsi un po' nel caso in cui siano presenti degli asterischi (ad es.
"#pippo*?#").
Complicazioni possono sorgere anche nel caso in cui non esiste alcun testo "puro" nella
stringa specificata dall'utente: la stringa da cercare infatti conterrebbe solo caratteri jolly; in
tali condizioni bisogna distinguere il caso in cui occorre cercare cifre dagli altri casi. Infatti, se
può avere un senso cercare ad es. "#*#", non ha molto senso cercare "*?*", che si ridurrebbe
banalmente a "*", ovvero tutto il testo del file e qualunque suo sottoinsieme!
Nella prossima lezione vedremo meglio queste eccezioni.
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2593&idArea=10 (5 of 5)12/09/2003 12.15.36
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 12
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
Login
Login
Password
Login
- Votanti: 34 - Lettori: 18858 - Livello: Medio
Cominciamo questa lezione correggendo quella precedente: nella routine cmdTrova_Click
era infatti nascosto (anzi, era abbastanza evidente) un bug di cui avreste dovuto accorgervi:
la parte di codice destinata a cercare l'ultimo testo puro in realtà non trova l'ultimo, ma quello
successivo al primo. La correzione è molto semplice:
'ultimo testo "puro"
lFine = Len(txtTrova.Text)
Do While Mid$(txtTrova.Text, lFine, 1) Like "[*?#]" And lFine > 1
lFine = lFine - 1
Loop
lInizio = IIf(lFine > 1, lFine, 2)
Do
lInizio = lInizio - 1
Loop Until Mid$(txtTrova.Text, lInizio, 1) Like "[*?#]" Or lInizio <= 1
If Mid$(txtTrova.Text, lInizio, 1) Like "[*?#]" Then
sTestoPuro(1) = Mid$(txtTrova.Text, lInizio + 1, lFine - lInizio)
Else
sTestoPuro(1) = Mid$(txtTrova.Text, lInizio, lFine - lInizio + 1)
End If
Dimenticata la password?
La soluzione consiste naturalmente nel partire "dal fondo" e risalire all'indietro alla ricerca di
Clicca qui
Non sei ancora iscritto? un carattere jolly che delimiti l'ultimo testo puro: la ricerca giunge sino all'inizio della stringa,
se occorre, e a tal fine è stata inserita la condizione "and lFine>1" nel primo loop; se infatti
Clicca qui
Mailing List
Visual Basic
l'utente cerca solo caratteri jolly, lFine arriverebbe fino a zero generando un errore nella
funzione Mid$(); per lo stesso motivo la variabile lInizio è inizializzata a 2 se lFine è uguale a
1.
Alla fine del secondo ciclo, quando la variabile lFine ha valore uno, anche la variabile lInizio
ha lo stesso valore, quindi il secondo elemento del vettore sTestoPuro è vuoto.
La If finale serve a distinguere il caso in cui c'è un solo testo puro: in tal caso la ricerca si
ferma quando lInizio=1 e per estrarre correttamente il testo puro occorre capire se il primo
carattere è un carattere jolly (quindi il testo puro parte dal secondo carattere) oppure no
(quindi il testo puro parte dal primo carattere); se invece la ricerca si ferma prima (ovvero
dopo il primo carattere, dato che si parte dal fondo) significa che essa è terminata perché
abbiamo incontrato un carattere jolly: non ci sarebbe bisogno di eseguire la If, ma per evitarlo
avremmo dovuto introdurre un ulteriore controllo sul valore di lInizio (If lInizio=1 then …).
La nostra routine trova sempre il primo e l'ultimo testo puro, anche nel caso in cui questi
coincidano (ad es. quando l'utente vuole cercare "?pippo#" o semplicemente "pippo"): se ciò
si verifica, per evitare problemi nella ricerca del testo è opportuno impostare l'ultimo testo
puro alla stringa nulla, in modo da essere consapevoli che di testo puro effettivamente ce n'è
uno solo:
If InStr(1, txtTrova.Text, sTestoPuro(0), vbBinaryCompare) = _
InStrRev(txtTrova.Text, sTestoPuro(1), , vbBinaryCompare) Then
'il primo e l'ultimo testo puro coincidono
sTestoPuro(1) = ""
End If
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2672&idArea=10 (1 of 7)12/09/2003 12.17.16
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
Power Point 2000
Antifurto Satellitare
GSM Viasat
Per verificare la coincidenza dei due testi non basta che siano uguali (l'utente potrebbe
cercare, ad es., "pippo*pippo"): condizione necessaria e sufficiente affinché i due testi siano
coincidenti è che la loro posizione all'interno della stringa inserita dall'utente sia la stessa; il
primo testo lo cerchiamo a partire dall'inizio della stringa con la funzione InStr, l'ultimo lo
cerchiamo a partire dal fondo con la funzione InStrRev: se le due posizioni sono uguali, i due
testi coincidono.
Fatto ciò, potremmo cominciare a cercare il testo, ma non abbiamo ancora considerato il
caso in cui il primo e l'ultimo testo puro siano rispettivamente preceduti o seguiti da caratteri
jolly; in tal caso occorre infatti verificare se l'utente cerca anche una cifra oppure no.
Gli asterischi, potendo rappresentare anche zero caratteri, non influiscono concretamente
sulla ricerca; i punti di domanda non danno indicazioni su quale carattere cercare, pertanto
basta verificare che ci siano tanti caratteri quanti sono i punti di domanda specificati nella
stringa da cercare.
Invece, i cancelletti rappresentano una cifra e non possono essere liquidati tanto facilmente:
la cosa più conveniente è trattarli come se fossero "testo puro": quindi se l'utente specifica,
ad es., "??*#pippo*?piero##", anziché limitarsi a trovare "pippo" e "piero" occorre trovare un
"pippo" preceduto da una cifra e un "piero" seguito da due cifre. In realtà, le cose sono un po'
più semplici: basta cercare due cifre all'interno del testo, assicurarsi che la prima cifra sia
preceduta da almeno due caratteri e confrontare l'intero testo contenuto tra questi limiti con la
stringa cercata dall'utente: l'operatore like ci dirà se i due testi corrispondono oppure no.
Utilizziamo un vettore lCifra(1) per indicare quanti caratteri devono precedere la prima cifra e
quanti devono seguire l'ultima: nell'esempio, lCifra(0) sarebbe uguale a 2, mentre lCifra(1)=0;
si tratta del minimo numero di caratteri per soddisfare la sequenza cercata dall'utente, in
realtà la prima cifra potrebbe essere preceduta anche da 10 caratteri o l'ultima seguita da 10
caratteri. Specifichiamo il valore –1 per indicare che non dobbiamo cercare cifre: ad es. nel
caso "?pippo*piero*#" sarebbe lCifra(0)= -1, lCifra(1)=0.
Analogamente, utilizziamo un vettore lCarattere(1) per indicare quanti caratteri devono
precedere il testo puro iniziale o seguire il testo puro finale: questa informazione ci servirà
solo nel caso in cui la stringa da cercare non contenga numeri, ovvero caratteri "#". In un
caso come "?pippo#", sarà lCifra(0)=-1, lCifra(1)=0, lCarattere(0)=1, lCarattere(0)=0; se non
usassimo lCarattere, non potremmo sapere che prima di "pippo" ci deve essere almeno un
carattere, anche se non è una cifra; lCarattere(1) non sarà utilizzato in quanto già lCifra(1) ci
dà tutte le informazioni necessarie.
Ora possiamo cercare il testo. L'algoritmo è relativamente semplice: se il primo testo puro è
preceduto da una cifra, cerchiamo una cifra, altrimenti cerchiamo il testo puro iniziale; una
volta trovato, cerchiamo da quel punto in poi il testo puro finale o la cifra che lo deve seguire.
A questo punto confrontiamo il testo contenuto tra gli estremi iniziale e finale e lo
confrontiamo con la stringa definita dall'utente: se l'esito è negativo, continuiamo la ricerca
del testo puro finale o della cifra finale, ricorsivamente, fino alla fine del file. Se invece l'esito
è positivo, evidenziamo il testo trovato e ci prepariamo a un'ulteriore ricerca.
lInizio = 0: lFine = 0
lTipoRicerca = IIf(chkCaseSens.Value = vbChecked, vbBinaryCompare,
vbTextCompare)
With frmNotePad.txtFile
Do
If lCifra(0) >= 0 Then
'cerca la prima cifra
Do
lInizio = lInizio + 1
Loop Until Mid$(.Text, lInizio, 1) Like "#" Or lInizio > Len(.Text)
If lInizio > Len(.Text) Then
lInizio = 0
Else
lFine = lInizio
End If
Else
If Len(sTestoPuro(0)) Then
'cerca il testo puro iniziale
lInizio = InStr(lInizio + 1, .Text, sTestoPuro(0), lTipoRicerca)
End If
lFine = lInizio + Len(sTestoPuro(0))-1
End If
If lInizio Then
Do
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2672&idArea=10 (2 of 7)12/09/2003 12.17.16
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
If lCifra(1) >= 0 Then
'cerca l'ultima cifra
Do
lFine = lFine + 1
Loop Until Mid$(.Text, lFine, 1) Like "#" Or lFine > Len(.Text)
If lFine > Len(.Text) Then lFine = 0
Else
LFine = lFine+1
'cerca il testo puro finale
If Len(sTestoPuro(1)) Then
lFine = InStr(lInizio + 1, .Text, sTestoPuro(1), lTipoRicerca)
End If
End If
If lFine = 0 Then
Exit Do
Else
lInizioTemp = lInizio -IIf(lCifra(0)<0,IIf(lCarattere(0)<0,0,
lCarattere(0)), lCifra(0))
If lCifra(1) >= 0 Then
lFineTemp = lFine + lCifra(1)
Else
lFineTemp = lFine + Len(sTestoPuro(1)) + IIf(lCarattere(1) < 0,
0, lCarattere(1)) - 1
End If
End If
DoEvents
sTestoTrovato = Mid$(.Text, lInizioTemp, lFineTemp – lInizioTemp+1)
Loop Until IIf(lTipoRicerca = vbBinaryCompare, (sTestoTrovato Like
txtTrova.Text), _ UCase$(sTestoTrovato) Like UCase$(txtTrova.Text))
If lFine Then
.SelStart = lInizioTemp - 1
.SelLength = lFineTemp – lInizioTemp+1
End If
Else
'cifra o testo iniziale non trovati
MsgBox "Testo non trovato", vbOKOnly + vbExclamation, App.Title
End If
Loop While (lFine = 0) And (lInizio < Len(.Text))
End With
Prima di tutto inizializziamo i due contatori lInizio e lFine, poi per comodità memorizziamo in
lTipoRicerca se la ricerca deve essere case sensitive oppure no; dopo aver cercato la prima
cifra o il primo testo puro, parte un ciclo per la ricerca dell'ultima cifra o dell'ultimo testo puro:
se il testo non viene trovato, la variabile lFine è posta a 0 e il ciclo termina.
Altrimenti, gli estremi iniziali e finali del testo vengono aggiustati per tener conto dei caratteri
identificati dai punti di domanda nella stringa specificata dall'utente; dopodiché, l'operatore
like ci dice se il testo corrisponde a quello cercato oppure no: in quest'ultimo caso, la ricerca
continua a partire dal punto in cui siamo arrivati. Se invece il testo corrisponde, esso viene
evidenziato nella finestra frmNotePad. Se il testo non viene trovato per tutto il file, viene
visualizzato un messaggio di errore: la proprietà App.Title indica il titolo dell'applicazione (si
imposta tramite le proprietà del progetto, nella scheda "crea"). Nella routine ci sono due loop
annidati, che idealmente hanno il compito di cercare il testo puro iniziale (il loop esterno) e
quello finale (il loop interno); essi corrispondono a questa logica:
se c'è un testo puro iniziale, lo cerco: se lo trovo, passo al punto 2, altrimenti
•
genero un errore;
se c'è un testo puro finale, lo cerco: se lo trovo, passo al punto 3, altrimenti torno
•
al punto 1 cercando il successivo testo puro iniziale;
se il testo contenuto tra i due testi puri corrisponde a quello cercato dall'utente, lo
•
seleziono; altrimenti torno al punto 2 e cerco il successivo testo puro finale.
Detto in altri termini, prima cerco il testo puro iniziale: se lo trovo, procedo da quel punto in
poi cercando il testo puro finale finché trovo un testo che globalmente corrisponda a quello
cercato dall'utente (loop interno); se non lo trovo, torno all'inizio (loop esterno) e cerco il testo
puro iniziale successivo a quello trovato nell'iterazione precedente, dopodiché ricomincio la
ricerca del testo puro finale, a partire dalla posizione del testo puro iniziale appena trovato.
L'errore "testo non trovato" viene perciò generato solo quando non trovo più un testo puro
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2672&idArea=10 (3 of 7)12/09/2003 12.17.16
Programmazione.it: Il portale Italiano sull'Information Technology
iniziale entro la fine del file, altrimenti devo continuare la ricerca del testo puro finale. Questa
situazione è controllata dalla condizione del loop esterno: (lFine = 0) And (lInizio < Len(.
Text)); se non ho trovato un testo puro finale (lFine=0) e se non ho ancora cercato per tutto il
file (lInizio
Per verificare la congruenza del testo trovato con quello cercato dall'utente l'operatore Like
viene utilizzato in due modalità diverse a seconda che la ricerca sia case sensitive oppure
no: se vi ricordate, nella lezione scorsa avevo indicato l'opportunità di specificare l'istruzione
Option Compare Binary nel form frmTrova; pertanto, se la ricerca è case sensitive è
sufficiente usare l'operatore like, ma se la ricerca non è case sensitive, occorre ignorare la
specifica Option Compare Binary usando la funzione Ucase$(); ecco perché la condizione
del loop interno dipende dall'istruzione:
IIf(lTipoRicerca=vbBinaryCompare…).
L'istruzione With…End With indica che all'interno del blocco è "sottinteso" l'oggetto
frmNotePad.txtFile: è un modo per rendere più chiaro e leggibile il codice e per rendere più
veloce il riferimento alle proprietà di quell'oggetto.
La routine appena vista consente di cercare il testo dall'inizio del file, il che significa che
premendo più volte il pulsante "trova", la stringa trovata sarà sempre la stessa, la prima
dall'inizio del file; per fare in modo che si possano cercare anche le successive occorrenze
del testo basta fare una semplicissima modifica: anziché inizializzare lInizio a zero, bisogna
inizializzarlo alla posizione corrente del cursore nel file. Questa posizione è data dalle
proprietà SelStart e SelLength dell'oggetto TextBox, che indicano rispettivamente il numero
del carattere iniziale del testo selezionato e il numero di caratteri selezionati: se ad es. nel
testo "pippo e topolino" seleziono "po e to", risulterà SelStart=3 e SelLength=7; infatti
SelStart inizia a contare da zero, nel senso che quando il cursore è proprio all'inizio del file
aperto (prima della prima lettera), SelStart vale zero. Tornando al nostro algoritmo, basta
modificare la prima riga in questo modo:
With frmNotePad.txtFile
lInizio = .SelStart + .SelLength: lFine = 0
…
La riga di inizializzazione è stata portata all'interno del blocco With per comodità; lInizio è
inizializzato alla posizione finale della selezione, se esiste un testo selezionato: questo
perché l'algoritmo, quando trova il testo cercato dall'utente e lo seleziona. Se facessimo
semplicemente lInizio=.SelStart, la ricerca partirebbe dal primo carattere della selezione e
quindi troverebbe ancora il testo già selezionato e non quello successivo: in altre parole, non
avremmo risolto il nostro problema. Se fate qualche prova vi accorgerete che anche il vero
blocco note e word funzionano allo stesso modo.
In conclusione, la nostra routine cmdTrova_Click è la seguente:
Public Sub cmdTrova_Click()
Dim lTipoRicerca As Long
Dim lCifra(1) As Long
Dim lCarattere(1) As Long
Dim lInizio As Long
Dim lFine As Long
Dim lInizioTemp As Long
Dim lFineTemp As Long
Dim sTestoPuro(1) As String
Dim sTestoTrovato As String
lInizio = 0: lFine = 0
Erase sTestoPuro
Erase lCifra
CmdTrova.Enabled = False
cmdChiudi.Enabled = False
Me.MousePointer = vbHourglass
Do
lInizio = lInizio + 1
Loop While Mid$(txtTrova.Text, lInizio, 1) = "*"
lFine = Len(txtTrova.Text)
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2672&idArea=10 (4 of 7)12/09/2003 12.17.16
Programmazione.it: Il portale Italiano sull'Information Technology
Do While Mid$(txtTrova.Text, lFine, 1) = "*"
lFine = lFine - 1
Loop
txtTrova.Text = Mid$(txtTrova.Text, lInizio, lFine - lInizio + 1)
'primo testo "puro"
lInizio = 0
Do
lInizio = lInizio + 1
Loop While Mid$(txtTrova.Text, lInizio, 1) Like "[*?#]"
lFine = lInizio
Do
lFine = lFine + 1
Loop Until Mid$(txtTrova.Text, lFine, 1) Like "[*?#]" Or lFine > Len
(txtTrova.Text)
sTestoPuro(0) = Mid$(txtTrova.Text, lInizio, lFine - lInizio)
lCifra(0) = InStr(1, Replace(Left$(txtTrova.Text, lInizio - 1), "*",
""), "#") - 1
lCarattere(0) = InStr(1, Replace(txtTrova.Text, "*", ""), sTestoPuro(0))
- 1
'ultimo testo "puro"
lFine = Len(txtTrova.Text)
Do While Mid$(txtTrova.Text, lFine, 1) Like "[*?#]" And lFine > 1
lFine = lFine - 1
Loop
LInizio = IIf(lFine > 1, lFine, 2)
Do
lInizio = lInizio - 1
Loop Until Mid$(txtTrova.Text, lInizio, 1) Like "[*?#]" Or lInizio <= 1
If Mid$(txtTrova.Text, lInizio, 1) Like "[*?#]" Then
sTestoPuro(1) = Mid$(txtTrova.Text, lInizio + 1, lFine - lInizio)
Else
sTestoPuro(1) = Mid$(txtTrova.Text, lInizio, lFine - lInizio + 1)
End If
lCifra(1) = Len(Replace(Mid$(txtTrova.Text, lFine + 1), "*", "")) - _
InStrRev(Replace(Mid$(txtTrova.Text, lFine + 1), "*", ""), "#")
If lCifra(1) = Len(Replace(Mid$(txtTrova.Text, lFine + 1), "*", ""))
Then
lCifra(1) = -1
End If
lCarattere(1) = Len(Replace(txtTrova.Text, "*", "")) - _
InStrRev(Replace(txtTrova.Text, "*", ""), sTestoPuro(1)) - Len
(sTestoPuro(1))
If InStr(1,txtTrova.Text,sTestoPuro(0),vbBinaryCompare)=InStrRev
(txtTrova.Text,sTestoPuro(1),, _
vbBinaryCompare) Then
'il primo e l'ultimo testo puro coincidono
sTestoPuro(1) = ""
End If
With frmNotePad.txtFile
lInizio = .SelStart + .SelLength: lFine = 0
lTipoRicerca = IIf(chkCaseSens.Value = vbChecked, vbBinaryCompare,
vbTextCompare)
Do
If lCifra(0) >= 0 Then
'cerca la prima cifra
Do
lInizio = lInizio + 1
Loop Until Mid$(.Text, lInizio, 1) Like "#" Or lInizio > Len(.
Text)
If lInizio > Len(.Text) Then
lInizio = 0
Else
lFine = lInizio
End If
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2672&idArea=10 (5 of 7)12/09/2003 12.17.16
Programmazione.it: Il portale Italiano sull'Information Technology
Else
If Len(sTestoPuro(0)) Then
'cerca il testo puro iniziale
lInizio = InStr(lInizio + 1, .Text, sTestoPuro(0),
lTipoRicerca)
End If
lFine = lInizio + Len(sTestoPuro(0)) - 1
End If
If lInizio Then
Do
If lCifra(1) >= 0 Then
'cerca l'ultima cifra
Do
lFine = lFine + 1
Loop Until Mid$(.Text, lFine, 1) Like "#" Or lFine > Len(.
Text)
If lFine > Len(.Text) Then lFine = 0
Else
lFine = lFine + 1
'cerca il testo puro finale
If Len(sTestoPuro(1)) Then
lFine = InStr(lInizio + 1, .Text, sTestoPuro(1),
lTipoRicerca)
End If
End If
If lFine = 0 Then
Exit Do
Else
lInizioTemp = lInizio - IIf(lCifra(0) < 0, IIf
(lCarattere(0) < 0, 0, _
lCarattere(0)), lCifra(0))
If lCifra(1) >= 0 Then
lFineTemp = lFine + lCifra(1)
Else
lFineTemp = lFine + Len(sTestoPuro(1)) + IIf
(lCarattere(1) < 0, 0, _
lCarattere(1)) - 1
End If
End If
DoEvents
sTestoTrovato = Mid$(.Text, lInizioTemp, lFineTemp lInizioTemp + 1)
Loop Until IIf(lTipoRicerca = vbBinaryCompare, (sTestoTrovato
Like txtTrova.Text), _
UCase$(sTestoTrovato) Like UCase$(txtTrova.Text))
If lFine Then
.SelStart = lInizioTemp - 1
.SelLength = lFineTemp - lInizioTemp + 1
End If
Else
'cifra o testo iniziale non trovati
MsgBox "Testo non trovato", vbOKOnly + vbExclamation, App.Title
End If
Loop While (lFine = 0) And (lInizio < Len(.Text))
End With
cmdTrova.Enabled = True
cmdChiudi.Enabled = True
Me.MousePointer = vbDefault
txtTrova.SetFocus
End Sub
Restano ancora da definire un paio di dettagli: sfruttare gli optionbutton OptSu, OptGiu,
OptTutto e permettere la ricerca dei caratteri usati come jolly; intendo dire proprio i caratteri
"#", "?" e "*" e non il testo che essi, in quanto caratteri jolly, rappresentano. Se l'utente,
specificando ad es. "pippo*", volesse cercare proprio "pippo*" e non "pippo" seguito da un
qualunque numero di caratteri? La nostra routine non glielo consentirebbe. Nella prossima
lezione vedremo come risolvere il problema.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2672&idArea=10 (6 of 7)12/09/2003 12.17.16
Programmazione.it: Il portale Italiano sull'Information Technology
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2672&idArea=10 (7 of 7)12/09/2003 12.17.16
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 13
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Cerca!
Vai alla ricerca
avanzata
Login
Hosting
Telexa.net
Data: 04-07-2003 - Voto:
- Votanti: 34 - Lettori: 18858 - Livello: Medio
DnsHosting.it
L'ultima lezione si era conclusa con alcuni dettagli da definire a proposito della ricerca di
EuroLogon.com
testo: ad esempio occorre tener conto della direzione di ricerca scelta dall'utente. Per farlo
Dominiando.it
dobbiamo introdurre un'altra variabile che ci indichi la direzione: risulta comodo che questa
FuturaHost.com
variabile sia di tipo numerico e che abbia valore +1/-1, a seconda che la direzione di ricerca
HostingSolutions.it
sia verso il basso o verso l'alto. Questo perché per cercare un testo verso il basso ci è venuto
Consultingweb.it
naturale incrementare un contatore che indichi la posizione corrente all'interno del testo;
analogamente, per cercare un testo verso l'alto sarà sufficiente decrementare quel contatore, Olimont.com
senza modificare pesantemente l'algoritmo di ricerca. Il flag di direzione può essere
impostato in questo modo:
lDirezione = IIf(optSu.Value = True, -1, 1)
Login
Password
Se la direzione scelta dall'utente è verso l'alto, il flag assume valore –1, in modo che
l'incremento del contatore possa essere modificato semplicemente passando da un loop del
tipo:
'cerca la prima cifra
Do
Login
lInizio = lInizio + 1
Dimenticata la password? Loop Until (Mid$(.Text, lInizio, 1) Like "#") Or (lInizio > Len(.Text))
Clicca qui
Non sei ancora iscritto?
a uno del tipo:
Clicca qui
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
'cerca la prima cifra
Do
lInizio = lInizio + 1 * lDirezione
Loop Until (Mid$(.Text, lInizio, 1) Like "#") Or (lInizio > Len(.Text)) Or
(lInizio < 1)
Come si diceva, se la direzione scelta è verso l'alto, il flag lDirezione sarà –1 e quindi il
contatore (in questo caso lInizio) sarà decrementato anziché incrementato. Se la direzione
scelta è "tutto", siamo in una situazione analoga alla direzione "giù", solo che bisognerà
cercare dall'inizio anche se il cursore si trova a metà del testo:
Mailing List
Visual Basic
If optTutto.Value Then
lInizio = 0
Else
lInizio = .SelStart + .SelLength
End If
Quando invece il testo è cercato non incrementando un contatore, ma usando la funzione
InStr, nel caso di ricerca verso l'alto basterà utilizzare la funzione InStrRev:
'cerca il testo puro iniziale
If lDirezione = 1 Then
lInizio = InStr(lInizio + 1, .Text, sTestoPuro(0), lTipoRicerca)
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2933&idArea=10 (1 of 7)12/09/2003 12.17.40
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
Else
lInizio = InStrRev(.Text, sTestoPuro(0), lInizio, lTipoRicerca)
End If
Backup PCMCIA
2Gb
Antifurto Satellitare
GSM Viasat
Dobbiamo però ricordare che, se il testo va cercato verso l'alto, non va bene impostare il
punto di partenza aggiungendo SelLength a SelStart, perché dovendo cercare "all'indietro"
continueremmo a trovare sempre lo stesso testo: infatti il punto di partenza della ricerca
sarebbe successivo al testo da cercare.
Quando il testo andava cercato verso il basso dovevamo impostare il punto di partenza a una
posizione successiva a SelStart; se il testo va cercato verso l'alto, il punto di partenza deve
precedere SelStart. Pertanto possiamo scrivere:
If optSu.Value Then
lDirezione = -1
lInizio = .SelStart
Else
lDirezione = 1
If optGiu.Value Then
lInizio = .SelStart + .SelLength
Else 'optTutto
lInizio = 0
End If
End If
In questo ciclo If abbiamo distinto i tre casi (direzione su, giù, tutto) e, nel caso in cui la
direzione scelta sia "tutto", abbiamo impostato a zero la variabile lInizio; questo perché, come
si è detto prima, la ricerca deve coinvolgere tutto il testo del file, indipendentemente da dove
si trova il cursore. Tuttavia, ciò è vero solo quando si procede alla prima ricerca del testo:
quando si cerca l'occorrenza successiva sarebbe sbagliato impostare di nuovo a zero il punto
di partenza, altrimenti la routine continuerebbe a trovare sempre lo stesso testo (la prima
occorrenza dall'inizio del file). Si potrebbe usare un flag per indicare se la ricerca su "tutto" il
file è all'inizio o sta continuando, tuttavia in questo caso potrebbe risultare comodo sfruttare
l'evento Click dell'OptionButton in questo modo:
Private Sub optTutto_Click()
frmNotePad.txtFile.SelStart = 0
End Sub
Quando l'utente seleziona la direzione di ricerca "tutto", il cursore viene automaticamente
impostato all'inizio del file, pertanto non c'è più bisogno dell'istruzione lInizio=0 nella routine
di ricerca:
If optSu.Value Then
lDirezione = -1
lInizio = .SelStart
Else
lDirezione = 1
lInizio = .SelStart + .SelLength
End If
Così, con qualche piccola modifica, abbiamo implementato la ricerca in tutte le direzioni.
Resta da trattare il caso della ricerca dei caratteri jolly. Per permettere all'utente di cercare
uno dei caratteri utilizzati come jolly (*, ?, #) occorre far capire alla routine di ricerca che il
carattere inserito va preso come tale e non come simbolo che rappresenta un set di caratteri.
La tecnica tradizionale per ottenere questo scopo è quella di abilitare una "sequenza di
escape"; le sequenze escape sono sequenze di caratteri che attribuiscono ai caratteri
successivi un significato differente da quello normale. Ad esempio, un problema comune che
si incontra in Visual Basic è quello di assegnare a una variabile stringa una stringa
comprensiva di virgolette; se io scrivo:
sStringa="ciao"
la variabile sStringa conterrà le lettere "c", "i", "a", "o", ma non le virgolette che racchiudono
queste lettere; questo perché le virgolette in Visual Basic (come in altri linguaggi) identificano
proprio le stringhe; se non usassimo le virgolette, Visual Basic penserebbe che "ciao" è il
nome di una variabile o una funzione o un'istruzione. Ma se io volessi assegnare a sStringa
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2933&idArea=10 (2 of 7)12/09/2003 12.17.40
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
la parola "ciao" CON le virgolette, come potrei fare? Un modo è quello di concatenare le
stringhe utilizzando il codice ascii delle virgolette:
sStringa=chr$(34) & "ciao" & chr$(34)
Un'altra soluzione è quella di usare le virgolette in un modo particolare, ovvero:
sStringa="""ciao"""
Ogni sequenza di tre virgolette rappresenta le virgolette ripetute una sola volta. Ripeterle solo
due volte non basterebbe, perché sarebbero confuse con la stringa nulla "", perciò occorre
ripeterle tre volte: è come se le prime virgolette rappresentassero l'inizio della stringa e le
seconde virgolette fossero un modo per dire: "Guarda che il carattere successivo (cioè le
terze virgolette) vanno intese come testo e non come carattere di chiusura della stringa"; lo
stesso vale per le tre virgolette finali. In altre parole, in questo contesto le virgolette vengono
usate come sequenza di escape, che alterano il normale significato attribuito alle virgolette.
Un problema analogo si incontra nel linguaggio C quando occorre specificare il carattere "\"
come testo: infatti questo carattere è solitamente utilizzato come carattere di escape per
rappresentare particolari sequenze di caratteri.
Tornando a noi, dobbiamo inventare un carattere di escape che inibisca l'uso dei caratteri
jolly; supponiamo di usare proprio il backslash "\" come carattere di escape: il testo "pippo*"
significa come al solito "pippo" seguito da qualunque cosa, ma "pippo\*" significa
semplicemente "pippo*", dove l'asterisco questa volta non è un carattere jolly, ma è un
carattere come gli altri. Tenete presente che si potrebbe utilizzare il carattere di escape
anche per altri scopi, ad esempio per indicare il ritorno a capo: potremmo decidere cioè che
la sequenza "\a" indichi il ritorno a capo, quindi: "pippo\apiero" non sarebbe altro che:
"pippo
piero"
Questa è una soluzione che risulta comoda data la difficoltà di rappresentare certi caratteri
su una sola riga o perché il tasto corrispondente ("invio" nel caso del ritorno a capo) è
associato ad altre funzioni, ad esempio alla pressione di un pulsante. Ad ogni modo, per ora
limitiamoci ai caratteri jolly. Ripercorriamo l'analisi della stringa immessa dall'utente nel
campo "trova": dapprima si cercano (per eliminarli) gli asterischi iniziali e finali; prima di
eliminare gli asterischi finali, però, occorre controllare che non siano preceduti dal carattere
escape "\":
lFine = Len(txtTrova.Text)
Do While Mid$(txtTrova.Text, lFine, 1) = "*"
lFine = lFine - 1
Loop
If Mid$(txtTrova.Text, lFine, 1) = "\" Then 'carattere escape
lFine = lFine + 1
End If
txtTrova.Text = Mid$(txtTrova.Text, lInizio, lFine - lInizio + 1)
Il puntatore lFine viene spostato in avanti per non perdere l'ultimo asterisco. L'analisi
prosegue poi cercando i testi puri e verificando se sono preceduti o seguiti da una cifra o da
un carattere; nel caso in cui siano presenti caratteri escape, la routine confonderebbe il
carattere "\" con un carattere qualunque, "distorcendo" la ricerca dei "veri" testi puri; ad
esempio, se la stringa cercata dall'utente fosse: "*\#pippo*\?piero" i testi puri trovati
sarebbero "\" e "piero" anziché "#pippo" e "?piero". In teoria dovremmo "aggiustare" la stringa
da cercare in modo che la routine estragga correttamente i testi puri, ma in realtà sembra più
conveniente lasciare l'aggiustamento a quando i testi puri sono già stati estratti: tutto quello
che occorre fare è sostituire i caratteri escape con il carattere successivo:
If Right(sTestoPuro(0), 1) = "\" Then
Mid$(sTestoPuro(0), Len(sTestoPuro(0)), 1) = Mid$(txtTrova.Text, _
InStr(1, txtTrova.Text, sTestoPuro(0)) + Len(sTestoPuro(0)), 1)
End If
If Right(sTestoPuro(1), 1) = "\" Then
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2933&idArea=10 (3 of 7)12/09/2003 12.17.40
Programmazione.it: Il portale Italiano sull'Information Technology
Mid$(sTestoPuro(1), Len(sTestoPuro(1)), 1) = Mid$(txtTrova.Text, _
InStr(1, txtTrova.Text, sTestoPuro(1)) + Len(sTestoPuro(1)), 1)
End If
Inoltre, bisogna reimpostare l'elemento 1 dei vettori lCifra e lCarattere, perché se l'ultimo
testo puro contiene un carattere escape significa che un "?" o un "#" sono stati erroneamente
interpretati come caratteri jolly:
If Right(sTestoPuro(1), 1) = "\" Then
Mid$(sTestoPuro(1), Len(sTestoPuro(1)), 1) = Mid$(txtTrova.Text, _
InStr(1, txtTrova.Text, sTestoPuro(1)) + Len(sTestoPuro(1)), 1)
If Right$(sTestoPuro(1), 1) = "#" Then
lCifra(1) = lCifra(1) - 1
ElseIf Right$(sTestoPuro(1), 1) = "?" Then
lCarattere(1) = lCarattere(1) - 1
End If
End If
Prima di iniziare la ricerca occorre infine modificare opportunamente il contenuto del campo
trova, poiché è quello che, tramite l'operatore Like, determina se il testo trovato corrisponde
effettivamente al testo da cercare:
txtTrova.Text = Replace(txtTrova.Text, "\*", "[*]")
txtTrova.Text = Replace(txtTrova.Text, "\#", "[#]")
txtTrova.Text = Replace(txtTrova.Text, "\?", "[?]")
L'uso delle parentesi quadre permette all'operatore Like di considerare i caratteri "jolly" come
caratteri normali; le parentesi quadre rappresentano quindi una sorta di sequenza escape per
l'operatore Like. Occorre anche salvare la stringa di ricerca inserita dall'utente per evitare che
nel campo "trova" restino le modifiche appena fatte: utilizziamo allo scopo una variabile
sTestoCercato. La nostra routine di ricerca è quindi diventata:
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
lTipoRicerca As Long
lCifra(1) As Long
lCarattere(1) As Long
lInizio As Long
lFine As Long
lInizioTemp As Long
lFineTemp As Long
lDirezione As Long
sTestoPuro(1) As String
sTestoTrovato As String
sTestoCercato As String
lInizio = 0: lFine = 0
Erase sTestoPuro
Erase lCifra
cmdTrova.Enabled = False
cmdChiudi.Enabled = False
Me.MousePointer = vbHourglass
sTestoCercato = txtTrova.Text
Do
lInizio = lInizio + 1
Loop While Mid$(txtTrova.Text, lInizio, 1) = "*"
lFine = Len(txtTrova.Text)
Do While Mid$(txtTrova.Text, lFine, 1) = "*"
lFine = lFine - 1
Loop
If Mid$(txtTrova.Text, lFine, 1) = "\" Then 'carattere escape
lFine = lFine + 1
End If
txtTrova.Text = Mid$(txtTrova.Text, lInizio, lFine - lInizio + 1)
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2933&idArea=10 (4 of 7)12/09/2003 12.17.40
Programmazione.it: Il portale Italiano sull'Information Technology
'primo testo "puro"
lInizio = 0
Do
lInizio = lInizio + 1
Loop While Mid$(txtTrova.Text, lInizio, 1) Like "[*?#]"
lFine = lInizio
Do
lFine = lFine + 1
Loop Until Mid$(txtTrova.Text, lFine, 1) Like "[*?#]" Or lFine > Len
(txtTrova.Text)
'If Mid$(txtTrova.Text, lFine - 1, 1) = "\" Then
' Mid$(txtTrova.Text, lFine - 1, 1) = Mid$(txtTrova.Text, lFine, 1)
'End If
sTestoPuro(0) = Mid$(txtTrova.Text, lInizio, lFine - lInizio)
lCifra(0) = InStr(1, Replace(Left$(txtTrova.Text, lInizio - 1), "*", ""),
"#") - 1
lCarattere(0) = InStr(1, Replace(txtTrova.Text, "*", ""), sTestoPuro(0)) - 1
'ultimo testo "puro"
lFine = Len(txtTrova.Text)
Do While Mid$(txtTrova.Text, lFine, 1) Like "[*?#]" And lFine > 1
lFine = lFine - 1
Loop
lInizio = IIf(lFine > 1, lFine, 2)
Do
lInizio = lInizio - 1
Loop Until Mid$(txtTrova.Text, lInizio, 1) Like "[*?#]" Or lInizio <= 1
If Mid$(txtTrova.Text, lInizio, 1) Like "[*?#]" Then
sTestoPuro(1) = Mid$(txtTrova.Text, lInizio + 1, lFine - lInizio)
Else
sTestoPuro(1) = Mid$(txtTrova.Text, lInizio, lFine - lInizio + 1)
End If
lCifra(1) = Len(Replace(Mid$(txtTrova.Text, lFine + 1), "*", "")) - _
InStrRev(Replace(Mid$(txtTrova.Text, lFine + 1), "*", ""), "#")
If lCifra(1) = Len(Replace(Mid$(txtTrova.Text, lFine + 1), "*", "")) Then
lCifra(1) = -1
End If
lCarattere(1) = Len(Replace(txtTrova.Text, "*", "")) - _
InStrRev(Replace(txtTrova.Text, "*", ""), sTestoPuro(1)) - Len(sTestoPuro
(1))
If Right(sTestoPuro(0), 1) = "\" Then
Mid$(sTestoPuro(0), Len(sTestoPuro(0)), 1) = Mid$(txtTrova.Text, _
InStr(1, txtTrova.Text, sTestoPuro(0)) + Len(sTestoPuro(0)), 1)
End If
If Right(sTestoPuro(1), 1) = "\" Then
Mid$(sTestoPuro(1), Len(sTestoPuro(1)), 1) = Mid$(txtTrova.Text, _
InStrRev(txtTrova.Text, sTestoPuro(1)) + Len(sTestoPuro(1)), 1)
If Right$(sTestoPuro(1), 1) = "#" Then
lCifra(1) = lCifra(1) - 1
ElseIf Right$(sTestoPuro(1), 1) = "?" Then
lCarattere(1) = lCarattere(1) - 1
End If
End If
If InStr(1, txtTrova.Text, sTestoPuro(0), vbBinaryCompare) = _
InStrRev(txtTrova.Text, sTestoPuro(1), , vbBinaryCompare) Then
'il primo e l'ultimo testo puro coincidono
sTestoPuro(1) = ""
End If
txtTrova.Text = Replace(txtTrova.Text, "\*", "[*]")
txtTrova.Text = Replace(txtTrova.Text, "\#", "[#]")
txtTrova.Text = Replace(txtTrova.Text, "\?", "[?]")
With frmNotePad.txtFile
If optSu.Value Then
lDirezione = -1
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2933&idArea=10 (5 of 7)12/09/2003 12.17.40
Programmazione.it: Il portale Italiano sull'Information Technology
lInizio = .SelStart
Else
lDirezione = 1
lInizio = .SelStart + .SelLength
End If
lFine = 0
lTipoRicerca = IIf(chkCaseSens.Value = vbChecked, vbBinaryCompare,
vbTextCompare)
Do
If lCifra(0) >= 0 Then
'cerca la prima cifra
Do
lInizio = lInizio + 1 * lDirezione
Loop Until (Mid$(.Text,lInizio, 1) Like "#") Or (lInizio > Len(.
Text)) Or (lInizio < 1)
If lInizio > Len(.Text) Then
lInizio = 0
Else
lFine = lInizio
End If
Else
If Len(sTestoPuro(0)) Then
'cerca il testo puro iniziale
If lDirezione = 1 Then
lInizio = InStr(lInizio + 1, .Text, sTestoPuro(0), lTipoRicerca)
Else
lInizio = InStrRev(.Text, sTestoPuro(0), lInizio, lTipoRicerca)
End If
End If
lFine = lInizio + Len(sTestoPuro(0)) - 1
End If
If lInizio Then
Do
If lCifra(1) >= 0 Then
'cerca l'ultima cifra
Do
lFine = lFine + 1
Loop Until (Mid$(.Text,lFine, 1) Like "#") Or (lFine>Len(.Text))
Or (lFine < 1)
If lFine > Len(.Text) Then lFine = 0
Else
lFine = lFine + 1
'cerca il testo puro finale
If Len(sTestoPuro(1)) Then
lFine = InStr(lFine + 1, .Text, sTestoPuro(1), lTipoRicerca)
End If
End If
If lFine = 0 Then
Exit Do
Else
lInizioTemp = lInizio - IIf(lCifra(0) < 0, IIf(lCarattere(0) < 0,
0, _
lCarattere(0)), lCifra(0))
If lCifra(1) >= 0 Then
lFineTemp = lFine + lCifra(1)
Else
lFineTemp = lFine + Len(sTestoPuro(1)) + IIf(lCarattere(1) < 0,
0, _
lCarattere(1)) - 1
End If
End If
DoEvents
sTestoTrovato = Mid$(.Text, lInizioTemp, lFineTemp - lInizioTemp +
1)
Loop Until IIf(lTipoRicerca = vbBinaryCompare, (sTestoTrovato Like
txtTrova.Text), _
UCase$(sTestoTrovato) Like UCase$(txtTrova.Text))
If lFine Then
.SelStart = lInizioTemp - 1
.SelLength = lFineTemp - lInizioTemp + 1
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2933&idArea=10 (6 of 7)12/09/2003 12.17.40
Programmazione.it: Il portale Italiano sull'Information Technology
End If
Else
'cifra o testo iniziale non trovati
MsgBox "Testo non trovato", vbOKOnly + vbExclamation, App.Title
End If
Loop While (lFine = 0) And (lInizio < Len(.Text)) And (lInizio > 0)
End With
cmdTrova.Enabled = True
cmdChiudi.Enabled = True
Me.MousePointer = vbDefault
txtTrova.Text = sTestoCercato
If frmTrova.Visible Then
txtTrova.SetFocus
End If
End Sub
Per le prove che mi è stato possibile fare sembra che la routine funzioni a dovere.
Naturalmente è possibile tutto un lavoro di ottimizzazione e di "ripulitura" del codice per
renderlo più snello ed efficiente, ma per ora mi fermo qui.
Per scaricare i nuovi sorgenti del notepad clicca qui .
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2933&idArea=10 (7 of 7)12/09/2003 12.17.40
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 14
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
Login
Login
Password
Login
- Votanti: 34 - Lettori: 18858 - Livello: Medio
Dopo aver manipolato approfonditamente le stringhe, vediamo un po' più in dettaglio i
numeri: in alcune lezioni passate avevo già spiegato la rappresentazione interna dei numeri
interi in formato binario, che è quello utilizzato intrinsecamente da ogni processore. Oltre al
formato binario, e al formato decimale usato da noi utenti, c'è almeno un altro sistema di
numerazione degno di essere menzionato: il sistema esadecimale. Come il sistema binario è
in base 2 (da 0 a 1) e il sistema decimale è in base 10 (da 0 a 9), così il sistema esadecimale
è in base 16 (da 0 a F): per superare il limite delle 10 cifre, insito nel sistema decimale da noi
utilizzato, i numeri da 10 a 15 sono rappresentati dalle prime sei lettere dell'alfabeto
occidentale, fino alla F per l'appunto. Per rappresentare numeri più grandi il sistema è
sempre lo stesso: si moltiplica ogni cifra del numero per la potenza della base indicata dalla
posizione di quella cifra all'interno del numero, sempre partendo da destra; ad esempio:
3d5c = 3*16^3 + d*16^2 + 5*16^1 + c*16^0 = 3*4096+13*256+5*16+12 = 15708
In Visual Basic i numeri esadecimali vanno sempre preceduti dall'apposito prefisso &h per
permettere al compilatore di riconoscerli come numeri esadecimali; senza il prefisso
sarebbero interpretati o come numeri decimali o come nomi di variabili o routine se
contengono lettere; ad esempio, possiamo scrivere:
Dimenticata la password?
Dim lVar as Long
Clicca qui
Non sei ancora iscritto?
lVar = &h12
Clicca qui
Debug.Print lVar
Mailing List
Visual Basic
Il risultato della stampa sarà naturalmente 18. A cosa serve utilizzare i numeri esadecimali?
A nulla, se vi limitate ad usare i numeri per fare normali operazioni matematiche; l'utilizzo del
formato esadecimale diventa conveniente quando c'è bisogno di ragionare con la
rappresentazione interna dei numeri nella memoria del computer, ad esempio quando
occorre fare operazioni logiche con due numeri, perché bisogna tenere in considerazione
l'impostazione dei singoli bit relativi ai due numeri. Poiché 16 è una potenza di 2, il formato
esadecimale risulta essere un "parente stretto" del formato binario utilizzato dal processore,
ma allo stesso tempo risulta più facilmente comprensibile (con un po' di allenamento) per noi
abituati al sistema decimale; inoltre, 16 non è una qualunque potenza di 2, ma è 2^4, cioè
rappresenta un gruppo di 4 bit: il che significa che 16^2=(2^4)^2=2^8=256, ovvero un byte è
rappresentabile con 8 cifre binarie (bit) oppure con 2 cifre esadecimali. Questo semplifica la
lettura di una sequenza di byte, perché è molto più informativa rispetto alla stessa sequenza
rappresentata in formato binario (troppo lunga) o decimale (troppo "estranea"); confrontiamo
ad esempio queste sequenze:
7F 3F 4E 4F
127 63 78 79
La relazione, a livello di singoli bit, tra i 4 byte della sequenza appare molto più evidente dalla
lettura dei valori esadecimali che non dalla lettura dei valori decimali; solo leggendo la prima
sequenza uno riesce a capire (ripeto, con un po' di allenamento) che tutti i byte sono minori
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2938&idArea=10 (1 of 4)12/09/2003 12.18.10
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
Canon Eos 3
Power Point 2000
di 128, che tutti hanno in comune almeno 3 bit (precisamente quelli di ordine 1, 2, 3 a partire
da destra), che tutti tranne uno sono dispari: queste sono le caratteristiche immediatamente
visibili a occhio. Leggendo la seconda sequenza, si riesce a capire subito soltanto che tutti i
numeri sono minori di 128 e che tutti tranne uno sono dispari, ma non si riesce a intuire quali
bit siano in comune e quali no. Perché tutti i byte sono minori di 128? Perché la prima cifra
nel formato esadecimale è sempre minore di 8: &h80 è infatti pari a 8*16=128. "E allora?"
direte voi; e allora si dà il caso che la cifra 8 nel formato esadecimale riveste una particolare
importanza (quando sta a sinistra come in &h80 e non quando sta a destra come in &h08)
perché indica che il bit più significativo del byte è impostato; se vi ricordate le spiegazioni
sull'organizzazione dei bit e sui numeri negativi, dovreste ricordarvi che il bit più significativo
(ovvero quello più a sinistra) è solitamente utilizzato come bit di segno, perciò potete testare
se il bit più significativo è impostato verificando la condizione:
If x<0 then
Questo lo potete fare solo per i tipi di dati provvisti di segno: se avete a che fare con dati di
tipo Byte sareste costretti a testare la condizione:
If x<128 then
Con l'operatore logico And e il formato esadecimale è possibile usare una sola espressione:
If x and &h80 then
Inoltre, la seconda cifra esadecimale nella sequenza di byte vista prima è sempre una E o
una F: la F ha un altro significato particolare, perché indica che tutti i 4 bit che rappresenta
sono impostati: F (esadecimale) = 15 (decimale) = 1111 (binario). Invece la E, siccome
precede la F, ha solo tre bit impostati, tutti tranne l'ultimo: E (esadecimale) = 14 (decimale) =
1110 (binario); da ciò si deduce subito che tutti i quattro byte della sequenza hanno tre bit in
comune e solo uno è pari mentre gli altri sono dispari. Queste nozioni, ripeto, tornano utili
principalmente quando c'è necessità di verificare quali bit di una certa variabile sono
impostati e quali no, oppure se vi capita spesso di aprire un file con un editor esadecimale,
ovvero un editor che mostra il contenuto del file tramite il valore numerico esadecimale dei
singoli byte; in tal caso, però, occorre fare attenzione al fatto che l'ordine dei byte può essere
invertito. I processori Intel in particolare hanno la singolare caratteristica di invertire l'ordine
dei byte quando li scrive sul disco fisso: ad esempio, il numero integer 18224 (che
corrisponde a &h4730) sarà scritto su un file come &h30 &h47, invertendo l'ordine dei byte:
prima quello meno significativo, poi quello più significativo; altri processori invece hanno un
comportamento "normale". Il motivo di questa inversione non è affatto illogico come può
sembrare a prima vista: il punto fondamentale è la modalità con cui il processore esegue le
operazioni aritmetiche fondamentali sui byte; immaginate di scrivere su un foglio a quadretti
la somma 54+82 in verticale, proprio come si fa alle elementari:
54 +
82 =
-----A questo punto occorre fare la somma: se avete cominciato a scrivere proprio all'inizio del
foglio, ora vi troverete in difficoltà perché il risultato è di tre cifre, mentre gli addendi sono di
due cifre; non solo manca lo spazio per una cifra, ma se si vuole mantenere
l'incolonnamento, manca lo spazio della cifra più significativa del risultato! Un modo semplice
per ovviare a questo problema è invertire l'ordine delle cifre, esattamente come fanno i
processori Intel, senza preoccuparsi preventivamente della lunghezza del risultato. Visual
Basic fornisce l'apposita funzione Hex$ per convertire un numero in stringa esadecimale; per
la conversione inversa basta semplicemente una qualunque funzione di conversione, come
CLng o Val, avendo cura di usare il prefisso "&h", ad esempio:
Hex$(5423)="152F"
Val("&h152F")=5423
Visual Basic è provvisto anche della funzione Oct$, che funziona come Hex$ ma converte un
numero nel sistema ottale (in base 8); questo sistema è molto meno usato di quello
esadecimale (che è già utilizzato limitatamente ad alcuni compiti); esso può tornare utile
quando un parametro può assumere tre possibili valori o una loro combinazione: ad esempio,
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2938&idArea=10 (2 of 4)12/09/2003 12.18.10
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
nei sistemi unix/linux un utente può avere tre tipi di permessi su un file: lettura, scrittura ed
esecuzione (e ovviamente una loro combinazione); indicando con 1 la lettura, 2 la scrittura e
4 l'esecuzione, i diritti dell'utente sul file possono essere sintetizzati da un numero che va da
0 (nessun permesso) a 7 (tutti i permessi), secondo un sistema "ottale".
Finora abbiamo trattato di numeri interi, ma naturalmente spesso si ha a che fare con numeri
reali, in virgola mobile; per questo tipo di numeri si utilizzano solitamente i dati di tipo Single o
Double; essi sono memorizzati in modo molto diverso dai numeri interi, seguendo gli
standard dettati dall'IEEE (Institute of Electrical and Electronics Engineers). Un numero in
virgola mobile è rappresentato da tre gruppi di bit: un bit di segno (il più significativo, come al
solito), un "esponente" che indica l'ordine di grandezza del numero e una "mantissa", ovvero
un numero compreso tra 1 e 2; l'insieme della mantissa e dell'esponente forma il numero in
formato decimale come lo rappresentiamo noi. Per il tipo di dati Single, l'esponente è
rappresentato da 8 bit, che però NON formano un byte in quanto in realtà sono "a cavallo" di
due byte (quello con il segno e quello con l'inizio della mantissa), mentre la mantissa è di 23
bit: in totale 4 byte; invece per il tipo di dati Double l'esponente è di 11 bit e la mantissa di 52
bit: in tutto 8 byte. Le operazioni in virgola mobile sono solitamente svolte dal coprocessore
matematico, "specializzato" in quest'ambito. I numeri in virgola mobile permettono di gestire
numeri molto grandi o molto piccoli, ma perdono in precisione (anche se il tipo Double, per
definizione, è più preciso del tipo Single); questo significa che i decimali successivi a una
certa posizione sono ininfluenti e non permettono di distinguere un numero dall'altro; ad
esempio, per Visual Basic sono indistinguibili i numeri (il punto esclamativo è il carattere di
dichiarazione del tipo single):
a!=1.123456789E+20
b!=1.123456778E+20
anche se la loro differenza è di più di mille miliardi! Invece assegnando gli stessi valori a
variabili di tipo double, i due numeri non sono indistinguibili.
Quando ad una variabile si assegna, o direttamente o in seguito ad un'operazione, un valore
superiore al massimo che può memorizzare, si ottiene un errore di "overflow"; analogamente
può accadere, anche se molto più raramente, di ottenere un errore di "undeflow", quando il
valore assegnato alla variabile va al di sotto del minimo consentito: ciò si verifica in
particolare con i numeri in virgola mobile, quando la precisione richiesta è superiore a quella
massima per il tipo di dati utilizzato. In realtà, per quanto mi consta, l'errore underflow non è
previsto in Visual Basic, che in questo caso assegna alla variabile il valore 0 tout court; con
altri linguaggi e altri compilatori invece può essere generato anche l'errore di underflow. Tra
le operazioni matematiche utilizzabili in Visual Basic ci sono ovviamente quelle fondamentali
più alcune altre; in particolare esiste un operatore di divisione intera ("\"), che esegue la
divisione tra due numeri, ma anziché restituire un numero reale restituisce un numero intero:
5/3=1.666666
5\3=1
Come si può intuire, la divisione intera semplicemente tronca la parte decimale, senza
approssimazioni. Inoltre Visual Basic è provvisto di alcune funzioni matematiche e
trigonometriche, in particolare:
Abs(x): restituisce il valore assoluto di x;
•
Sin(x), Cos(x): restituisce il seno o il coseno dell'angolo x, espresso in radianti;
•
Log(x): restituisce il logaritmo naturale (in base e) di x;
•
Exp(x): restituisce e elevato alla x (funzione esponenziale: è la funzione inversa
•
di log(x));
Round(x, i): restituisce x arrotondato alla i-esima cifra decimale;
•
Sgn(x): restituisce –1, 0, 1 a seconda che x sia negativo, 0, positivo;
•
Sqr(x): restituisce la radice quadrata di x.
•
La funzione Sgn può tornare utile ad esempio quando si ha a che fare con un ciclo in cui il
limite superiore non è conosciuto a priori e può anche essere negativo:
For x=0 to nLimite Step Sgn(nLimite)
'…
Next x
Se nLimite è positivo, il ciclo viene eseguito alla solita maniera; se è negativo sarà eseguito
al contrario, perché lo step è negativo; se è nullo, il ciclo eseguirà infinite iterazioni, perché il
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2938&idArea=10 (3 of 4)12/09/2003 12.18.10
Programmazione.it: Il portale Italiano sull'Information Technology
contatore non è incrementato (vige la clausola step 0): in tal caso bisognerà prevedere
un'altra condizione di uscita dal ciclo. Le funzioni trigonometriche vogliono l'argomento
espresso in radianti, in modo cioè che l'angolo giro (360°) corrisponda a 2*pi greco; si può
quindi implementare una semplice funzione di conversione, ad esempio per il seno:
Private Function SinG(dAngolo as Double) as Double
SinG=Sin(dAngolo*3.14159/180)
End Function
La funzione Log assume che la base sia il numero e (pari a circa 2,71828): per ottenere il
logaritmo con una base diversa (ad esempio in base 10) basta semplicemente dividere per il
logaritmo naturale della base, ovvero:
log 10 (x)=log e (x)/log e (10)
Con questa semplice formula si può implementare un'altra piccola funzione che accetti in
ingresso il valore di cui calcolare il logaritmo e la base sul quale calcolarlo:
Private Function Logaritmo (dNumero as Double, nBase as Integer) as Double
Logaritmo=Log(dNumero)/Log(dBase)
End Function
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2938&idArea=10 (4 of 4)12/09/2003 12.18.10
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 15
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
Login
- Votanti: 34 - Lettori: 18858 - Livello: Medio
Oggi cominciamo un nuovo argomento: gli oggetti. Un oggetto è un insieme di codice e dati
che è possibile utilizzare indipendentemente da altri oggetti poiché rappresenta un insieme a
sé stante. Esempi tipici ed evidenti di oggetti sono i controlli che abitualmente inseriamo su
un form: pulsanti, caselle di testo, checkbox e così via. Ma anche lo stesso form è un
oggetto, così come il font di un testo, un database, un documento word o un grafico di excel.
Fondamentalmente, un oggetto in Visual Basic è rappresentato da una classe che ne
definisce le caratteristiche: in particolare, la classe di un oggetto definisce le sue proprietà, i
suoi metodi e i suoi eventi. Per rendersi conto in modo semplice ed efficace di cosa ciò
significhi basta dare un'occhiata al "visualizzatore oggetti" (o "object browser"), che potete
aprire premendo F2 dall'IDE o tramite il menu "visualizza". Se date un'occhiata alle voci con
l'icona del modulo di classe
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
Login
Partners
ASPItalia.com
Password
vi accorgerete che essa è associata non solo ai normali controlli visibili sulla casella degli
ZioBudda
strumenti, ma anche all'oggetto ErrObject, all'oggetto StdFont, all'oggetto App, eccetera. Tutti FastUrl
questi oggetti hanno un'interfaccia (un insieme di metodi, eventi, proprietà) definiti nelle
NewBie
Login
rispettive classi, che quindi ne rappresentano il "modello" valido per tutte le istanze
Dimenticata la password?
dell'oggetto. Infatti, quando un programmatore manipola un oggetto tramite la sua interfaccia, OSWay
Clicca qui
TC Sistema
in realtà non ha a che fare direttamente con la classe dell'oggetto, ma con una sua istanza,
Non sei ancora iscritto?
ovvero con una copia direttamente utilizzabile. Se mi è consentito un paragone filosofico, il
Clicca qui
rapporto che c'è tra una classe e le sue istanze è in qualche modo analogo a quello che
intercorre tra le "idee" platoniche e le sue manifestazioni concrete sul nostro mondo. Ogni
istanza di una classe è indipendente dalle altre, così che in uno stesso form è possibile
inserire ad esempio 4 textbox senza che debba esserci alcuna reciproca connessione tra di
essi: ognuna di queste istanze è gestibile in modo del tutto indipendente dalle altre, ma la
loro interfaccia è comune perché è definita per tutte dalla medesima classe TextBox.
L'indipendenza reciproca delle istanze, naturalmente, non impedisce che esse possano
interagire tra di loro e con altri tipi di oggetti. La classe, oltre a contenere il codice per la
gestione dell'interfaccia, contiene anche dati da esporre, di norma, tramite le proprietà; tali
Alla scoperta di
dati si dice che sono "incapsulati" nell'oggetto e ovviamente sono replicati per ognuna delle
sue istanze. Per creare un oggetto, quindi, è sufficiente inserire in un progetto un modulo di
Visual Basic & VB .
Mailing List
classe, definirne le proprietà, i metodi e gli eventi e scrivere il codice e i dati necessari per
NET
Visual Basic
consentirne la corretta funzionalità: vediamo come fare queste dichiarazioni con una
semplice classe di esempio. Creiamo una classe di nome "clsUomo" che rappresenta un
essere umano: tra le proprietà attribuibili ad esso ci può essere ad esempio il nome, il peso,
l'altezza, il sesso, eccetera. Per fare in modo che l'interfaccia della classe Uomo esponga la
proprietà "Nome" occorre dichiarare questa proprietà nel modo seguente:
Public Property Get Nome() as String
Nome = sName
End Property
Public Property Let Nome(sNewName as String)
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2984&idArea=10 (1 of 6)12/09/2003 12.18.41
Programmazione.it: Il portale Italiano sull'Information Technology
sName = sNewName
End Property
Videoproiettore NEC
Occorre innanzitutto notare che le dichiarazioni sono due: una per leggere il valore della
proprietà (Property Get) e una per modificare tale valore (Property Let); la Property Get
funziona sostanzialmente come una funzione che restituisce il contenuto di una variabile, in
questo caso sName; solitamente essa è dichiarata come variabile privata del modulo di
classe:
Olidata 17"
Private sName as String
Quando invece il programmatore accede alla proprietà Nome per modificarne il valore viene
richiamata la procedura Property Let, a cui viene passato come argomento il nuovo nome da
assegnare alla proprietà; tale valore è assegnato via codice alla variabile di modulo sName,
in modo che una successiva chiamata della Property Get restituisca il nome appena
modificato. Esiste anche un altro tipo di routine per modificare una proprietà: si tratta della
Property Set, che va utilizzata qualora la proprietà faccia riferimento a un oggetto; questo
accade perché Visual Basic richiede di utilizzare l'istruzione Set per assegnare un riferimento
a un oggetto, mentre per i tipi di dati fondamentali non è richiesta alcuna istruzione
particolare (l'istruzione Let era necessaria nelle vecchie versioni del basic ed è stata
mantenuta per compatibilità). Per quale motivo è opportuno utilizzare una coppia di routine
Property anziché dichiarare a livello di modulo una variabile pubblica Nome?
Anche questo secondo metodo consentirebbe di leggere e scrivere a proprio piacimento il
valore della variabile; ma il problema è proprio questo: usando una variabile pubblica non ci
sarebbe alcun controllo sulla correttezza dell'uso della "proprietà" Nome. Uno potrebbe
assegnare a questa variabile qualunque valore, anche palesemente inadatto a descrivere il
nome di un essere umano; invece implementando una coppia di routine Property è possibile
scrivere del codice atto ad evitare che a tale proprietà siano assegnati valori non adeguati,
ad esempio nomi del tipo "123&g#" oppure nomi troppo lunghi o troppo corti. Inoltre, con una
variabile pubblica non sarebbe possibile implementare una proprietà di sola lettura; cosa che
risulta invece molto semplice con le routine property: basta infatti cancellare la routine
Property Let (o Property Set)! Ad esempio la proprietà "Sesso" potrebbe essere una tipica
proprietà di sola lettura (a meno di esigenze particolari):
Public Property Get Sesso() as Boolean
Sesso = True
End Property
(In questo caso bisogna solo decidere se il valore True corrisponde a maschio o femmina:
eventualmente è possibile utilizzare un tipo di dati diverso dal booleano). Come le proprietà
descrivono le caratteristiche di un oggetto, i metodi rappresentano le azioni che l'oggetto può
compiere: un essere umano, ad esempio, può correre, mangiare, dormire, pensare, eccetera.
Ciascuna di queste azioni può essere eseguita tramite un apposito metodo; ad esempio
possiamo implementare un metodo "Scrivi" che consenta di scrivere qualcosa su un certo
form: tale metodo non sarà altro che una procedura Sub pubblica, la quale accetterà come
parametri in ingresso la frase da scrivere e il form su cui scrivere (eventualmente si
potrebbero aggiungere parametri aggiuntivi come le coordinate o il font della frase da
scrivere; la proprietà AutoRedraw del form deve essere impostata a True):
Public Sub Scrivi (frmScrivi as Form, sFrase as String)
frmScrivi.Print sFrase
End Sub
Se si desidera che il metodo restituisca un valore (ad esempio per controllare la corretta
esecuzione del metodo), si può usare una funzione al posto di una subroutine. Infine, gli
eventi sono quegli elementi dell'interfaccia che non sono richiamati da chi utilizza l'oggetto,
ma sono generati direttamente dall'oggetto; quando si verifica qualcosa che si ritiene
opportuno segnalare al client (ovvero al componente che utilizza l'oggetto), è l'oggetto stesso
che genera l'evento corrispondente. Per un essere umano i tipici eventi potrebbero essere
"Nascita", "Morte", "Matrimonio", eccetera. Per poter generare un evento in una classe deve
essere innanzitutto presente la sua dichiarazione:
Public Event Nascita(dtData as Date, bSesso as Boolean)
La generazione vera e propria dell'evento avviene però con l'istruzione RaiseEvent;
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2984&idArea=10 (2 of 6)12/09/2003 12.18.41
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
supponiamo ad esempio che l'interfaccia della classe clsUomo esponga il metodo
"GeneraFiglio": in tale evento sarà generato l'evento Nascita:
Public Sub GeneraFiglio(sNome as String)
Dim fSesso as Single
Dim bSesso as Boolean
fSesso=Rnd
If fSesso>0.5 then
bSesso=True
End If
RaiseEvent Nascita(Date, bSesso)
End Sub
Il sesso del nascituro viene determinato casualmente con la funzione Rnd, dopodiché viene
ufficializzata la nascita del figlio in data odierna: quando ciò accade nel client l'esecuzione
passerà alla routine Sub Nascita che avrà per argomenti la data di nascita e il sesso del
neonato. Ad esempio in questa routine si potrebbe creare una nuova istanza della classe
Uomo con le caratteristiche del neonato. Per inserire una proprietà, un metodo o un evento,
a una classe si può anche ricorrere alla funzione "Inserisci routine" del menu "Strumenti";
tutto quello che occorre fare è selezionare il tipo di routine da inserire: "Property" per le
proprietà, "Event" per gli eventi, "Sub" o "Function" per i metodi. Finora abbiamo visto la
costruzione dell'interfaccia tramite una classe: ora vediamo come utilizzare questa classe dal
lato client. Prima di tutto bisogna dichiarare una o più variabili di tipo clsUomo:
Dim uPrimoUomo as clsUomo
Dim uSecondoUomo as clsUomo
La "u" di prefisso sta a indicare che la variabile è di tipo "Uomo": si tratta di una convenzione
ad hoc per distinguere queste variabili dalle altre. Poi bisogna impostare un riferimento alla
classe Uomo; infatti, come dicevo prima, l'utilizzatore dell'oggetto Uomo non lavora
direttamente sulla classe che lo definisce,ma sulle sue singole istanze: istanze che sono
rappresentate dalle variabili di tipo Uomo e che diventano concretamente utilizzabili solo
quando contengono un riferimento alla classe.
Per fare ciò si utilizza l'istruzione Set:
Set uPrimoUomo = New clsUomo
Set uSecondoUomo = New clsUomo
La parola chiave New serve a specificare che abbiamo bisogno di una nuova istanza della
classe clsUomo; senza questa parola chiave l'istruzione Set non funzionerebbe, perché
clsUomo non è direttamente referenziabile da una variabile. È invece possibile specificare
New nella stessa dichiarazione delle variabili:
Dim uPrimoUomo as New clsUomo
Dim uSecondoUomo as New clsUomo
Così facendo sarà lo stesso Visual Basic a impostare il riferimento a una nuova istanza della
classe per ogni variabile in occasione del suo primo utilizzo nel codice; quando Visual Basic
per la prima volta incontrerà una istruzione che invoca una proprietà o un metodo
dell'oggetto, ad esempio:
uSecondoUomo.Nome = "Piergiovanni"
provvederà a creare una nuova istanza della classe Uomo, ad assegnare alla variabile
uSecondoUomo un riferimento a tale istanza e a modificare il valore della proprietà Nome di
questa istanza. La successiva istruzione che coinvolge la variabile uSecondoUomo farà
riferimento all'istanza già creata e assegnata alla variabile, finché non sarà implicitamente o
esplicitamente distrutta. In questo modo, però, non si ha un pieno controllo sul momento in
cui l'istanza della classe viene creata, proprio perché questo compito è lasciato a Visual
Basic; invece spesso è importante che sia il programmatore a decidere quando l'istanza deve
essere creata utilizzando l'apposita istruzione Set, onde evitare ambiguità nel codice. Allo
stesso modo, è importante che il programmatore provveda a distruggere esplicitamente il
riferimento all'istanza della classe quando questa istanza non è più necessaria; a questo
scopo si usa ancora l'istruzione Set:
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2984&idArea=10 (3 of 6)12/09/2003 12.18.41
Programmazione.it: Il portale Italiano sull'Information Technology
Set uPrimoUomo = Nothing
La parola chiave Nothing specifica a Visual Basic che l'istanza della classe a cui faceva
riferimento la variabile uPrimoUomo deve essere distrutta: in questo modo la quantità di
memoria occupata da quell'istanza (ricordo che ogni istanza rappresenta una copia della
classe) può essere liberata; la distruzione del riferimento all'istanza è quindi una buona
abitudine utile soprattutto nel caso in cui si abbia a che fare con classi complesse e scarse
quantità di risorse sul proprio pc. La parola chiave Nothing può essere utilizzata anche per
testare se una variabile contiene già un riferimento ad un'istanza della classe oppure no:
If uPrimoUomo is Nothing Then
Set uPrimoUomo = New clsUomo
End If
In questo caso si usa l'operatore "Is" e non "=" perché la condizione di uguaglianza non
avrebbe significato in questo contesto, dato che Nothing non è una costante con un valore
particolare che è possibile confrontare direttamente col contenuto di una variabile. Invece
l'operatore Is con la parola chiave Nothing verifica se la variabile uPrimoUomo fa riferimento
a un oggetto valido ed esistente oppure no. Una volta creata una nuova istanza della classe
ed assegnato un suo riferimento alla variabile considerata, è possibile utilizzare le proprietà e
i metodi esposti dall'interfaccia dell'oggetto. Per poter usufruire anche degli eventi
dell'oggetto occorre che la variabile che contiene il riferimento all'istanza sia stata dichiarata
con la parola chiave WithEvents:
Dim WithEvents uPrimoUomo as clsUomo
Se la classe Uomo espone degli eventi, questi saranno visibili nella finestra del codice
selezionando "uPrimoUomo" dalla casella degli oggetti in alto a sinistra e uno degli eventi
esposti dalla casella delle procedure in alto a destra:
In questa routine il programmatore può gestire l'evento, esattamente come si farebbe con
l'evento Load del Form o con l'evento Click di un pulsante. I metodi dell'oggetto vengono
richiamati esattamente come si farebbe con altre funzioni o routine, utilizzando la sintassi:
nomeoggetto.metodo parametri
dove "nomeoggetto" indica il nome della variabile che contiene il riferimento a un'istanza
dell'oggetto, ad esempio:
uPrimoUomo.Scrivi Me, "Ciao"
Identica sintassi vale per le proprietà, come si è visto negli esempi precedenti; l'unica
particolarità è che quando si assegna un nuovo valore alla proprietà, questo viene passato
come parametro alla routine Property Let (o Set), anche se ciò non è immediatamente
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2984&idArea=10 (4 of 6)12/09/2003 12.18.41
Programmazione.it: Il portale Italiano sull'Information Technology
visibile al programmatore che utilizza l'oggetto. Ovviamente, come per tutte le procedure, è
possibile prevedere specifici argomenti: ad esempio, per la proprietà Nome potrebbe essere
utile specificare se si vuole assegnare il primo o il secondo nome, oppure se si vuole
assegnare solo il nome di battesimo o anche il cognome, eccetera. In tal caso la routine
Property Let avrà sempre un argomento in più rispetto alla corrispondente Property Get:
questo argomento in più è proprio il nuovo valore da assegnare alla proprietà. Ad esempio:
Public Property Get Nome (bNomeCompleto as Boolean) as String
If bNomeCompleto Then
Nome = sNome & " " & sCognome
Else
Nome = sNome
End If
End Property
Public Property Let Nome (bNomeCompleto as Boolean, sNewName as String)
Dim nSpace as Integer
If bNomeCompleto Then
nSpace = InStr(1, sNewName, " ")
sNome = Left$(sNewName, nSpace – 1)
sCognome = mid$(sNewName, nSpace+1)
Else
sNome = sNewName
End If
End Property
La lettura e scrittura di questa proprietà può avvenire ad esempio nel modo seguente:
uPrimoUomo.Nome(True) = "Paperino Paolino"
sNome = uPrimoUomo.Nome(False) 'sNome="Paperino"
Nella prima istruzione la stringa "Paperino Paolino" diventerà il valore del parametro
sNewName della routine Property Let. E' importante che i tipi di dati coinvolti siano
congruenti: ovvero, se la proprietà è di tipo stringa (Property Get … as String), l'ultimo
parametro della routine Property Let deve essere anch'esso di tipo stringa (Property Let …
(sNewName as String)), altrimenti si ottiene un "Type Mismatch". L'IDE di Visual Basic
consente inoltre di specificare quale, tra le proprietà o gli eventi di una classe, deve essere la
proprietà o l'evento predefinito: per fare ciò occorre (previa attivazione della finestra del
codice del modulo di classe) selezionare la voce "Attributi routine" dal menu "Strumenti";
nella finestra di dialogo è possibile scegliere una delle proprietà, metodi o eventi disponibili
nell'interfaccia della classe e, tramite le "opzioni" della finestra di dialogo, attivare o
disattivare la casella di controllo "predefinita nell'interfaccia utente". Quando una proprietà è
predefinita è possibile evitare di scriverne il nome nel codice, ad esempio l'istruzione:
lErrNumber = Err.Number
è equivalente a:
lErrNumber = Err
perché la proprietà Number è quella predefinita per l'oggetto Err. Ciò può risultare a volte
comodo per il programmatore, ma in generale io sconsiglierei la seconda sintassi, perché
non rende immediatamente chiaro che si sta utilizzando una proprietà, né quale proprietà si
sta utilizzando; non tutti infatti possono sapere che la proprietà predefinita dell'oggetto Err è
Number. La prima sintassi invece è esplicita e più facilmente leggibile; inoltre è anche più
veloce perché evita a Visual Basic di capire da solo quale proprietà deve richiamare. Un
evento predefinito, ovviamente, non è un evento che si verifica "per default" quando ricorre
chissà quale condizione: l'evento predefinito di un oggetto è semplicemente quello che
appare nella finestra del codice quando si seleziona l'oggetto dalla casella in alto a sinistra o
quando si fa doppio click su un controllo. Ad esempio, l'evento Click è quello predefinito per il
pulsante di comando, l'evento Load è quello predefinito per il form, l'evento Change è quello
predefinito per la casella di testo. La casella di controllo "predefinita nell'interfaccia utente"
non è attiva per i metodi, per ovvi motivi. La stessa finestra di dialogo permette di dare una
descrizione ad ogni elemento dell'interfaccia: tale descrizione sarà mostrata nel
visualizzatore oggetti e, per le proprietà, anche nella parte inferiore della finestra delle
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2984&idArea=10 (5 of 6)12/09/2003 12.18.41
Programmazione.it: Il portale Italiano sull'Information Technology
proprietà; inoltre è possibile nascondere il membro al visualizzatore oggetti, scegliere se
mostrare o nascondere una proprietà nella finestra delle proprietà e in quale categoria, e
altre opzioni. La medesima finestra di dialogo è visualizzabile anche tramite il visualizzatore
oggetti, selezionando un membro dell'interfaccia e scegliendo "proprietà" dal menù che
compare premendo il tasto destro del mouse.
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=2984&idArea=10 (6 of 6)12/09/2003 12.18.41
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 16
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
Login
Login
Password
Login
Dimenticata la password?
Clicca qui
Non sei ancora iscritto?
Clicca qui
- Votanti: 34 - Lettori: 18858 - Livello: Medio
Per mettere in pratica qualcuna delle nozioni viste nella lezione precedente torniamo a un
vecchio progetto: il campo minato. Tempo fa mi è stato fatto notare un errore (di cui
probabilmente si saranno accorti in molti, ma che solo un lettore si è degnato di segnalarmi):
l'errore consiste nel fatto che quando si scopre una mina, in effetti la partita non termina, ma
può essere proseguita dal giocatore, benché il programma visualizzi la posizione delle altre
mine e interrompa il timer. In realtà di errori ce ne sono anche altri: ad es. la partita non si
interrompe neppure quando il giocatore vince e per di più il timer continua a scorrere. Per
correggere questi bug occorre intervenire sull'evento Click dei pulsanti "mina", che controlla
appunto se una mina sta per esplodere oppure no; questa è la routine originaria:
Private Sub Mina_Click(Index As Integer)
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 'nessuna mina esplosa, almeno una mina
circostante
If Len(Mina(Index).Caption) = 0 Then
ContaMine = ContaMine + 1
End If
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
Mina(Index).Caption = Mina(Index).Tag
ElseIf Mina(Index).Tag = -1 Then 'mina esplosa
Mina(Index).Caption = "M"
For x = 0 To 2
Mina(PosMine(x)).Caption = "M"
Next x
Timer1.Enabled = False
Else 'mina(index).tag=0 (nessuna mina esplosa, nessuna mina circostante)
Mailing List
Visual Basic
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
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3196&idArea=10 (1 of 6)12/09/2003 12.19.03
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
Next y1
Next x1
End If
Olidata 17"
If ContaMine = MaxContaMine Then
MsgBox "HAI VINTO!"
End If
End Sub
Per fare in modo che il giocatore non possa ulteriormente continuare a premere i pulsanti
Philips Free Speech dopo aver vinto, o perso, occorre in qualche modo "inibire" l'evento click, in modo che le
precedenti istruzioni non siano eseguite: il modo più semplice per fare ciò è adottare un flag
del tipo bEseguiClick che valga true se la partita è ancora in corso o false se la partita è
terminata. In realtà una variabile del genere l'abbiamo già utilizzata, anche se non è un
booleano: si tratta della variabile ContaMine, che quando raggiunge il massimo segnala la
vittoria del giocatore.
Nessuno ci impedisce di assegnarle il medesimo valore anche quando il giocatore perde e
condizionare l'esecuzione della routine al fatto che tale variabile sia minore o uguale al valore
massimo raggiungibile:
Private Sub Mina_Click(Index As Integer)
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 ContaMine < MaxContaMine Then
If Mina(Index).Tag > 0 Then 'nessuna mina esplosa, almeno una mina
circostante
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 esplosa
Mina(Index).Caption = "M"
For x = 0 To 2
Mina(PosMine(x)).Caption = "M"
Next x
Timer1.Enabled = False
ContaMine = MaxContaMine
Exit Sub
Else 'mina(index).tag=0 (nessuna mina esplosa, nessuna mina
circostante)
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!"
Timer1.Enabled = False
End If
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3196&idArea=10 (2 of 6)12/09/2003 12.19.03
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
End If
End Sub
Quando il giocatore scopre una mina, oltre ad arrestare il timer il programma provvede ad
assegnare:
ContaMine = MaxContaMine
dopodiché termina forzatamente la routine per evitare la comparsa del messaggio "hai
vinto!" (anche se il giocatore ha perso…); una volta che ContaMine ha il suo massimo valore
concesso, qualunque ulteriore pressione delle mine non sortisce effetti, perché la condizione
iniziale risulta falsa e la routine termina subito. In realtà il vero campo minato si comporta in
modo leggermente diverso, impedendo la pressione stessa dei pulsanti: ciò può essere
facilmente ottenuto disabilitando tutti i pulsanti "mina", ovvero impostando la proprietà
Enabled su False per tutti gli elementi della matrice di CommandButton "Mina".
Se invece il contatore ContaMine ha un valore minore di MaxContaMine, significa che la
partita è ancora in corso, pertanto la routine esegue tutti i controlli del caso ed eventualmente
mostra il messaggio di vittoria; anche in tal caso il timer viene interrotto. Ciò non è ancora
sufficiente, perché se una nuova partita viene cominciata subito dopo, l'etichetta lblTempo
riprende da dove si era interrotta anziché ricominciare daccapo; il problema si risolve
semplicemente inserendo l'istruzione:
lblTempo.Caption = ""
alla fine della routine mnuNew_Click, prima dell'abilitazione del timer, e quest'altra istruzione
all'inizio dell'evento Timer1_Timer:
If ContaMine = 0 Then i = 0
Poiché la variabile ContaMine viene inizializzata a zero ad ogni nuova partita, il timer
controllando il valore della variabile riesce a capire se deve continuare a contare da dove era
arrivato (ricordiamo che la variabile "i" usata dal timer è di tipo static) o se deve ricominciare
da zero. L'unico inconveniente è che finché il giocatore non comincia effettivamente a
giocare (cioè non prova a premere qualche "mina") la variabile ContaMine resterà sempre a
zero e quindi il timer continuerà a inizializzare la variabile i e l'etichetta lblTempo continuerà a
mostrare il valore "1" anche se la partita è cominciata da un bel pezzo. A questo punto si
potrebbero escogitare vari trucchi, ma la soluzione più elegante è usare una variabile a livello
di modulo anziché una variabile statica ma locale rispetto all'evento timer di Timer1:
Dim ContaSecondi As Long 'numero di secondi trascorsi dall'inizio della
partita
'E modificare di conseguenza la routine del timer:
Private Sub Timer1_Timer()
ContaSecondi = ContaSecondi + 1
lblTempo.Caption = CStr(ContaSecondi)
End Sub
Ovviamente la variabile ContaSecondi dovrà essere inizializzata a zero tutte le volte che
comincia una nuova partita. Questo per quanto riguarda la correzione dei bug. Già che ci
siamo vediamo di migliorare il gioco implementando la visualizzazione di una bandierina sui
pulsanti sui quali il giocatore clicca col tasto destro del mouse. Per fare ciò occorre
innanzitutto impostare a 1 la proprietà Style dei pulsanti "mina": corrisponde allo stile grafico,
che consente appunto di visualizzare icone sui pulsanti; la modalità di default è quella
standard (Style=0), senza icone. Purtroppo la proprietà è di sola lettura in fase di esecuzione,
per cui è necessario modificare manualmente la proprietà di tutte le mine in ambiente di
progettazione: si può fare in un colpo solo selezionando tutte le mine insieme (come si fa con
un gruppo di file in gestione risorse) e cambiando il valore della proprietà. A questo punto
dobbiamo intercettare l'evento "click col tasto destro" per mostrare o nascondere l'icona della
bandiera. L'evento "click", come sapete, è associato per default al tasto sinistro del mouse e
l'oggetto CommandButton non supporta un evento "RightClick" generato dalla pressione del
tasto destro; esso supporta però l'evento "MouseDown", che viene generato ogniqualvolta
l'utente preme un pulsante del mouse (non importa quale) sull'oggetto: è questo evento che
dobbiamo intercettare. Questa è la dichiarazione dell'evento:
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3196&idArea=10 (3 of 6)12/09/2003 12.19.03
Programmazione.it: Il portale Italiano sull'Information Technology
Private Sub Mina_MouseDown(Index As Integer, Button As Integer, Shift As
Integer, X As Single, Y As Single)
End Sub
Il primo parametro, come per l'evento Click, indica su quale "mina" è stato premuto un tasto
del mouse; il secondo invece specifica quale dei tasti è stato premuto: perciò è su questo
parametro che dovremo fare gli opportuni controlli; il terzo parametro specifica quali dei tasti
control, alt o shift sono premuti mentre viene generato l'evento: è molto utile per riconoscere
particolari sequenze (ad es. ctrl+shift+tasto destro); gli ultimi due parametri indicano le
coordinate del puntatore del mouse nel momento in cui viene generato l'evento e per ora non
ci interessano. Come si è detto, il nostro compito è individuare la pressione del tasto destro
del mouse per visualizzare la bandiera sulla mina corrispondente; la chiave è in una semplice
If:
With Mina(Index)
If Button = vbRightButton Then 'click col tasto destro
If .Picture.Handle = 0 Then 'nessuna bandiera sul pulsante
.Picture = LoadPicture("BandieraCampoMinato.ico")
Else 'il pulsante ha già la bandiera
.Picture = LoadPicture()
End If
End If
End With
Se il tasto del mouse premuto è il destro, il parametro Button avrà il valore 2, che è il valore
della costante vbRightButton (fa parte del gruppo MouseButtonConstants, insieme a
vbLeftButton e vbMiddleButton): in tal caso, se il pulsante non ha alcuna bandiera essa viene
visualizzata attraverso la proprietà Picture, altrimenti significa che il giocatore ha premuto il
tasto destro su una mina che aveva già la bandiera: perciò essa sarà tolta.
L'impostazione della proprietà Picture avviene tramite la funzione LoadPicture: dei vari
parametri che accetta, qui è stato usato solo il primo, ovvero il nome del file. Come potete
appurare controllando sul visualizzatore oggetti, la funzione restituisce un riferimento a
un'istanza della classe IPictureDisp, che dispone di una proprietà Handle: tale proprietà
assume valore zero se non è associata ad alcuna immagine, altrimenti assume il valore
dell'handle dell'icona; l'handle è un numero a 32 bit che fa riferimento univocamente
all'immagine caricata in memoria dalla funzione LoadPicture. Per eliminare un'immagine
associata al pulsante, basta usare ancora la funzione LoadPicture senza alcun parametro:
infatti il nome del file immagine è facoltativo e, se assente, indica di rimuovere l'immagine
correntemente associata al pulsante. Intercettato il click col tasto destro occorre poi fare in
modo che facendo click col tasto sinistro non accada nulla se sulla mina premuta è posta una
bandiera: si tratta di fare una piccola estensione alla condizione che abbiamo posto all'inizio
dell'evento Mina_Click:
If (ContaMine < MaxContaMine) And (Mina(Index).Picture.Handle = 0) Then
L'istruzione If controlla non solo che il gioco sia ancora in corso confrontando il valore di
ContaMine con MaxContaMine, ma anche che la mina che il giocatore ha premuto non sia
una di quelle "bloccate" con la bandiera, altrimenti l'evento click viene sostanzialmente
ignorato. A differenza del vero campo minato, tuttavia, i pulsanti appaiono effettivamente
"premuti" quando si fa click, anche se non succede nulla: non è come se i pulsanti "Mina"
fossero disabilitati. In effetti si potrebbe impostare Enabled=False quando il giocatore fa click
col tasto destro del mouse, approfittando anche del fatto che ai pulsanti disabilitati è possibile
associare un'icona tramite la proprietà DisabledPicture: ogniqualvolta un pulsante è
disabilitato Visual Basic provvede a mostrare l'immagine associata alla proprietà, senza
bisogno di usare la funzione LoadPicture. Il problema è che poi una volta disabilitato il
pulsante non è più possibile intercettare l'evento mousedown per eventualmente reimpostare
Enabled=True; non è un problema insormontabile, ma sembra più conveniente la strada
illustrata sopra. È opportuno invece disabilitare le mine all'avvio dell'applicazione, abilitandole
solo quando il giocatore sceglie di iniziare una nuova partita:
Private Sub Form_Load()
Dim lContatore As Long
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3196&idArea=10 (4 of 6)12/09/2003 12.19.03
Programmazione.it: Il portale Italiano sull'Information Technology
MaxContaMine = 13
For lContatore = Mina.LBound To Mina.UBound
Mina(lContatore).Enabled = False
Next lContatore
End Sub
Mina.LBound e Mina.UBound equivalgono rispettivamente a Lbound(Mina) e Ubound(Mina),
ovvero 0 e 15. Per lo stesso motivo, come si è detto più sopra, risulta conveniente disabilitare
i pulsanti quando il giocatore termina (vincendo o perdendo) la partita, anziché utilizzare la
variabile ContaMine per inibire il click sulle mine: dipende anche dai gusti del programmatore
scegliere una delle tante strade disponibili.
Resta ancora una cosa da fare: eliminare tutte le bandiere eventualmente esistenti quando si
comincia una nuova partita; l'ultima parte della routine mnuNew_Click diventerà quindi:
For i = 0 To 15
With Mina(i)
.Caption = ""
.Enabled = True
.Picture = LoadPicture()
End With
Next i
ContaMine = 0
lblTempo.Caption = ""
ContaSecondi = 0
Timer1.Enabled = True
Infine, una caratteristica prevista ma non ancora implementata riguardava l'etichetta lblMine,
che nel campo minato dovrebbe indicare quante mine restano ancora da scoprire: ora che
abbiamo capito come intercettare il click col tasto destro possiamo anche aggiornare questa
etichetta. Si tratta semplicemente di sottrarre, dal numero complessivo di mine (3 nel nostro
esempio), il numero di pulsanti su cui sventola la bandiera; il luogo più adatto in cui effettuare
questo calcolo è l'evento mousedown, dato che è lì che si decide se issare o ammainare la
bandiera.
Intanto dichiariamo un nuovo contatore per le bandiere a livello di modulo nel form:
Dim ContaBandiere As Long 'numero di bandiere issate
Poi incrementiamo o decrementiamo il contatore ogni volta che una bandiera viene aggiunta
o tolta e aggiorniamo l'etichetta:
Private Sub Mina_MouseDown(Index As Integer, Button As Integer, Shift As
Integer, X As Single, Y As Single)
With Mina(Index)
If Button = vbRightButton Then 'click col tasto destro
If .Picture.Handle = 0 Then 'nessuna bandiera sul pulsante
.Picture = LoadPicture("c:\giorgio\word\corsovb\BandieraCampoMinato.
ico")
ContaBandiere = ContaBandiere + 1
Else 'il pulsante ha già la bandiera
.Picture = LoadPicture()
ContaBandiere = ContaBandiere - 1
End If
End If
End With
lblMine.Caption = CStr(3 - ContaBandiere)
End Sub
Infine, inizializziamo il contatore ad ogni nuova partita:
Private Sub mnuNew_Click()
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3196&idArea=10 (5 of 6)12/09/2003 12.19.03
Programmazione.it: Il portale Italiano sull'Information Technology
[…]
ContaMine = 0
lblTempo.Caption = ""
lblMine.Caption = "3"
ContaSecondi = 0
ContaBandiere = 0
Timer1.Enabled = True
End Sub
In teoria dovremmo usare una costante per indicare il numero di mine, ma per ora non
formalizziamoci troppo. Ci sarebbe anche un piccolo bug: se il giocatore mette più bandiere
di quante sono le mine, l'etichetta mostra naturalmente un numero negativo; cosa che
peraltro accade anche col vero campo minato. Se il programmatore lo ritiene opportuno, può
imporre dei controlli per evitare che ciò accada, ma a me sembra tutto sommato più
conveniente lasciarlo così: l'etichetta ha solo una funzione indicatrice e se il giocatore ha
voglia di mettere bandiere in eccesso, che lo faccia pure.
Ora il campo minato sembra funzionare a dovere, ma molte cose possono essere ancora
migliorate, come vedremo. Buon divertimento.
Clicca qui per scaricare il sorgente di esempio della lezione.
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3196&idArea=10 (6 of 6)12/09/2003 12.19.03
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 17
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Hosting
Telexa.net
Data: 04-07-2003 - Voto:
- Votanti: 34 - Lettori: 18858 - Livello: Medio
DnsHosting.it
Nella
lezione
precedente
abbiamo
visto
come
correggere
alcuni
bug
presenti
nella
prima
EuroLogon.com
Cerca!
versione del nostro "Campo Minato": ora vediamo come migliorare ulteriormente
Dominiando.it
Vai alla ricerca
l'applicazione costruendo un controllo personalizzato che abbia la funzione di "mine". Visual
FuturaHost.com
Basic infatti dà la possibilità al programmatore di creare un controllo (come potrebbe essere
avanzata
HostingSolutions.it
ad esempio il textbox o la label) che abbia caratteristiche definite dallo stesso programmatore
Consultingweb.it
in modo da poterlo adattare alle sue esigenze; tali controlli sono denominati "controlli
Olimont.com
ActiveX" perché sfruttano la tecnologia ActiveX creata da Microsoft per estendere la
precedente tecnologia OLE; oltre ai controlli, esistono anche i server ActiveX e i documenti
ActiveX. Questa tecnologia permette una maggiore integrazione con Internet ed è sfruttabile
da tutti i linguaggi di programmazione che la supportano, così che un controllo ActiveX creato
Login
con Visual Basic può essere utilizzato ad esempio anche tramite Visual C++ o il Borland
Delphi. Quando si crea un controllo ActiveX in effetti si definisce la classe del controllo,
Login
classe che sarà poi istanziata una o più volte da applicazioni client che lo conterranno; la
Partners
classe è definita in un file *.ctl, che viene compilato in un file *.ocx. Una volta compilato, lo
ASPItalia.com
Password
sviluppatore che lo utilizza può collocarlo su un form come qualsiasi altro controllo, creando
ZioBudda
così un' istanza di progettazione ; essa può essere programmata tramite l'interfaccia
esposta dal controllo: ad esempio il programmatore può inserire codice negli eventi (se ce ne FastUrl
NewBie
Login
sono) generati dal controllo e può usarne metodi e proprietà. Quando invece l'applicazione
Dimenticata la password? client viene eseguita, viene generata un' istanza di esecuzione , che esegue il codice
OSWay
Clicca qui
incapsulato in esso e interagisce col suo contenitore (tipicamente un form) e con gli altri
TC Sistema
Non sei ancora iscritto? controlli eventualmente presenti.
Clicca qui
Per creare un controllo ActiveX occorre selezionare "Controllo ActiveX" dalla finestra di
apertura di un nuovo progetto: Visual Basic creerà una finestra di progettazione del tutto
analoga a quella di un form; essa rappresenta l'oggetto UserControl, che è la base su cui
viene costruito ogni controllo personalizzato. All'interno dello UserControl, come in un form, è
possibile collocare i controlli costitutivi del componente ActiveX; ogni istanza del
componente, pertanto, conterrà un'istanza dell'oggetto UserControl e dei vari controlli
disposti al suo interno, così come quando al caricamento di un form durante l'esecuzione di
un'applicazione vengono generate istanze di tutti i controlli presenti nel form. L'oggetto
UserControl fornisce l'interfaccia per far interagire il controllo ActiveX con i suoi controlli
costitutivi eventualmente in risposta ad azioni dell'utente: ad esempio fornisce gli eventi
Alla scoperta di
generati dal mouse o dalla tastiera, gli eventi di inizializzazione e terminazione del controllo,
Visual Basic & VB .
eccetera. L'interazione con il contenitore del controllo è però supportata dall'oggetto
Mailing List
NET
Extender , che fornisce alcune proprietà di estensione, cioè le caratteristiche dell'interfaccia
Visual Basic
del componente ActiveX controllate dal suo contenitore. Ad esempio un controllo ActiveX non
può sapere a priori quale sarà il contenitore in cui sarà collocato, per cui una proprietà come
Parent, che appunto identifica il contenitore del controllo, non può essere gestita dal controllo
stesso, ma deve essere gestita dal suo contenitore: questo avviene tramite l'oggetto
Extender, accessibile tramite l'apposita proprietà dell'oggetto UserControl. Lo stesso dicasi
per proprietà come Left o Top, che indicano la posizione del controllo all'interno del
contenitore: è intuibile che dev'essere il contenitore a gestirle e anche questo avviene tramite
l'oggetto Extender.
L'utilizzatore del controllo non si accorge della differenza, perché per lui tutte queste
proprietà sono fornite dal controllo ActiveX; invece lo sviluppatore del controllo deve fare un
Cerca
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3461&idArea=10 (1 of 5)12/09/2003 12.20.31
Programmazione.it: Il portale Italiano sull'Information Technology
Olidata 17"
Sveglia Orolà
po' più attenzione. Infatti, secondo le specifiche ActiveX definite da Microsoft, ogni oggetto
Extender dovrebbe fornire almeno le proprietà Cancel, Default, Name, Parent e Visible, ma
può darsi che un determinato contenitore non gestisca queste proprietà o altre non standard,
come ad esempio Index o Left o Enabled. In tal caso, se si tenta di accedere a una proprietà
di estensione non supportata dal contenitore, si verificherà un errore (più precisamente
l'errore 438: "L'oggetto non supporta la proprietà o il metodo"); pertanto è opportuno che lo
sviluppatore del controllo usi codice di intercettazione degli errori quando tenta di accedere a
proprietà di estensione. Inoltre, proprio perché l'oggetto Extender dipende dal contenitore,
esso diventa disponibile solo quando il controllo è stato effettivamente collocato nel
contenitore, ovvero quando è stata creata un'istanza del controllo; in altre parole, l'oggetto
Extender non è disponibile nell'evento Initialize dell'oggetto UserControl. Naturalmente Visual
Basic fornisce per ogni oggetto Extender non solo le proprietà standard indicate sopra, ma
anche altre (ad esempio Tag, TabStop, TooltipText) insieme ad alcuni metodi ed eventi (ad
esempio il metodo SetFocus o l'evento GetFocus).
Altre importanti proprietà per una corretta interazione del controllo con il contenitore sono
fornite dall'oggetto AmbientProperties, accessibile tramite la proprietà Ambient dell'oggetto
UserControl: queste proprietà di ambiente definiscono alcune caratteristiche dell'ambiente in
cui si trova il controllo ActiveX, ad esempio le proprietà BackColor e ForeColor suggeriscono
colori di sfondo e di primo piano per fare in modo che il controllo ActiveX si presenti in modo
uniforme con il suo contenitore. In particolare, occorre prestare attenzione alla proprietà
DisplayName, che restituisce il nome dell'istanza del controllo (differisce dalla proprietà
UserControl.Name perché quest'ultima definisce il nome del controllo e non della sua
particolare istanza), e alla proprietà UserMode, che indica se ci si trova in fase di esecuzione
(UserMode=True) o di progettazione (UserMode=False). Se ci si trova in fase di
progettazione, significa che l'istanza non è in modalità "utente" (per utente si intende qui
l'utilizzatore finale del controllo, ovvero chi usa l'applicazione) e quindi la proprietà UserMode
è False. Tale proprietà risulta molto utile quando certe porzioni di codice vanno eseguite solo
in fase di esecuzione: infatti, anche se a prima vista può sembrare strano, il codice di un
controllo ActiveX può essere eseguito anche in fase di progettazione. Alcuni esempi
chiariranno le nozioni fin qui esposte.
Dato che il controllo ActiveX che stiamo per costruire sarà da utilizzare nel campo minato,
apriamo il progetto relativo al gioco e aggiungiamo un nuovo progetto scegliendo "Controllo
ActiveX": Visual Basic creerà un "gruppo" di progetti visibile nella finestra "gestione progetti",
in cui l'originario Campo Minato apparirà in grassetto, perché rappresenta il progetto di avvio
(ovvero quello che viene avviato quando si preme F5); è possibile impostare un progetto
come progetto di avvio cliccando col tasto destro del mouse sulla voce del progetto e
selezionando l'apposita voce dal menu di contesto. Il progetto relativo al controllo ActiveX è
identificato (oltre che dall'icona) da un oggetto UserControl a cui corrisponde un file *.ctl;
possiamo dare "Mina" come nome dell'oggetto, visto che il controllo serve appunto a creare
una mina personalizzata: questo sarà il nome della classe (come "TextBox"), poi ogni istanza
dell'oggetto avrà il suo nome particolare, ad esempio Mina1, Mina2 (come TextBox1,
TextBox2), eccetera. Poiché la mina è costituita essenzialmente da un pulsante, all'interno
della finestra di progettazione dello UserControl inseriamo, come se si trattasse di un form,
un CommandButton; questo pulsante, che rappresenta un controllo (in questo caso IL
controllo) costitutivo del controllo ActiveX, dovrà avere un nome appropriato, ad esempio
"cmdMina". Aprendo l'editor del codice relativo alla classe Mina.ctl vi accorgerete che sono
elencati due oggetti: cmdMina e UserControl, ciascuno con i suoi eventi.
Nella casella degli strumenti, inoltre, è stata aggiunta automaticamente un'icona che
rappresenta il nostro controllo ActiveX; proviamo ad inserire il nostro controllo Mina all'interno
della finestra del Campo Minato come faremmo con un qualunque altro controllo (se l'icona
dell'ActiveX risulta disabilitata è perché la finestra di progettazione dello UserControl è
ancora aperta: chiudetela). Ora, quasi certamente non vi sarete preoccupati della posizione
del pulsante cmdMina all'interno dello UserControl, cosicché disegnando il controllo Mina
all'interno del form del Campo Minato avete ottenuto un risultato diciamo insoddisfacente;
poco importa, basta ridimensionare il pulsante sfruttando l'evento Resize. Quando il controllo
Mina è stato inserito nel form è stata creata un'istanza di progettazione del controllo, con
un'istanza sia dell'oggetto UserControl, sia dell'oggetto cmdMina: pertanto quando la Mina è
collocata sul form saranno generati gli eventi di inizializzazione del controllo e anche l'evento
Resize; se all'interno di questo evento scriviamo il seguente codice:
With cmdMina
.Left = 0
.Top = 0
.Height = UserControl.Height
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3461&idArea=10 (2 of 5)12/09/2003 12.20.31
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
.Width = UserControl.Width
End With
vedremo che all'inserimento della Mina sul form il pulsante cmdMina avrà esattamente le
dimensioni che vogliamo dargli noi disegnando il controllo sul form, perché è il controllo
stesso che obbliga cmdMina ad avere quelle dimensioni in occasione dell'evento Resize.
Questa è anche la dimostrazione del fatto, cui accennavo prima, che il codice di un controllo
ActiveX è eseguito anche in fase di progettazione, oltre che a run-time. Ora, poiché di norma
non è possibile ridimensionare i controlli in fase di esecuzione, dovremmo assicurarci che il
codice dell'evento Resize venga eseguito solo in fase di progettazione: a questo scopo torna
utile la proprietà UserMode descritta sopra:
If Not UserControl.Ambient.UserMode Then
With cmdMina
.Left = 0
.Top = 0
.Height = UserControl.Height
.Width = UserControl.Width
End With
End If
Il pulsante cmdMina viene ridimensionato solo se UserMode è falso, ovvero solo se siamo in
fase di progettazione; questo però sarebbe un errore, perché in realtà il ridimensionamento
avviene non solo quando viene creata un'istanza di progettazione del controllo, ma anche
quando viene creata un'istanza di esecuzione: perciò è meglio togliere la condizione If
(potete rendervi conto voi stessi della differenza eseguendo il progetto con e senza la
condizione). Ora, avrete notato che all'istanza di progettazione del controllo Mina è stato
assegnato per default il nome "Mina1" da Visual Basic: controllando gli eventi associati
all'oggetto Mina1 noterete anche che manca l'evento Resize, che invece esiste per quasi tutti
i controlli standard. Questo avviene perché l'interfaccia dello UserControl (come anche del
pulsante cmdMina) è incapsulata all'interno del controllo ActiveX e quindi non visibile di per
sé all'utente che utilizza il controllo; gli eventi, le proprietà e i metodi dell'oggetto Mina
disponibili per default sono solo quelli provvisti dall'oggetto Extender e li potete vedere
utilizzando il visualizzatore oggetti (facendo attenzione che il progetto attivo sia quello del
campo minato e non quello del controllo ActiveX) e selezionando l'oggetto "Mina". Mancano
le proprietà Cancel e Default, che pure fanno parte delle proprietà standard che ogni oggetto
Extender dovrebbe fornire, semplicemente perché il nostro controllo ActiveX non è
configurato per essere un pulsante di comando, per cui quelle proprietà non avrebbero
senso; infatti la proprietà DefaultCancel dello UserControl è per default impostata su False.
Per rendere visibile anche all'utente l'evento Resize (o altri elementi dell'interfaccia) dello
UserControl occorre dichiarare un apposito evento Resize che faccia da delegato per lo
stesso evento del controllo ActiveX: la delega è il meccanismo che consente di esporre
l'interfaccia dello UserControl o dei controlli costitutivi all'utilizzatore del controllo ActiveX. Il
fatto che queste interfacce non siano automaticamente disponibili all'utente può sembrare
una seccatura per lo sviluppatore del controllo, che deve provvedere a delegare tutte quelle
che gli sembra opportuno, ma consente la massima libertà nella creazione dell'interfaccia
appropriata che il controllo deve avere per chi lo utilizza. Tornando a noi, dobbiamo quindi
dichiarare un evento Resize nella sezione delle dichiarazioni dello UserControl:
Option Explicit
Public Event Resize()
Questo sarà un evento appartenente all'interfaccia del controllo ActiveX Mina e dovrà essere
generato dall'evento Resize dell'oggetto UserControl:
Private Sub UserControl_Resize()
With cmdMina
.Left = 0
.Top = 0
.Height = UserControl.Height
.Width = UserControl.Width
End With
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3461&idArea=10 (3 of 5)12/09/2003 12.20.31
Programmazione.it: Il portale Italiano sull'Information Technology
RaiseEvent Resize
End Sub
Ora, nel form del Campo Minato, l'oggetto Mina1, istanza del controllo Mina, disporrà anche
dell'evento Resize. Volendo evitare di seguire la procedura di delega manualmente per tutti
(o quasi) gli elementi dell'interfaccia del controllo ActiveX, è possibile avvalersi della
creazione guidata disponibile come aggiunta nell'IDE di Visual Basic.
Come visto nella scorsa lezione, gli eventi che più ci interessano sono MouseDown e Click,
unitamente alla proprietà Enabled; questa proprietà non figura inizialmente tra quelle esposte
dall'interfaccia del controllo Mina perché, nonostante sia fornita dall'oggetto Extender, è
necessario che sia esplicitamente dichiarata come delega dell'omonima proprietà dello
UserControl:
Public Property Get Enabled() As Boolean
Enabled = UserControl.Enabled
End Property
Public Property Let Enabled(ByVal vNewValue As Boolean)
UserControl.Enabled = vNewValue
PropertyChanged "Enabled"
End Property
La Property Get non fa altro che esportare il valore corrente della corrispondente proprietà
dell'oggetto UserControl, mentre la Property Let, oltre ad impostare tale valore secondo
quanto specificato dall'utilizzatore del controllo, segnala tramite l'istruzione PropertyChanged
che il valore della proprietà è cambiata, in modo che Visual Basic possa memorizzare
l'impostazione e assegnarla correttamente alla proprietà quando crea l'istanza di esecuzione.
PropertyChanged è un'istruzione che serve in particolar modo in fase di progettazione,
affinché Visual Basic aggiorni correttamente le impostazioni delle proprietà per quelle istanze
che hanno subito modifiche.
L'implementazione della proprietà Enabled descritta sopra è quella standard, ma a noi serve
qualcosa di leggermente diverso: quello che dovremmo abilitare o disabilitare non è tanto il
controllo ActiveX Mina, ma piuttosto il pulsante cmdMina, perché in ogni caso dobbiamo
essere in grado di intercettare almeno il click col tasto destro per issare o ammainare la
bandiera sul pulsante. Tuttavia, l'utilizzatore del componente ActiveX dovrebbe poter
comunque disabilitare, se lo desidera, l'intero controllo e non semplicemente i suoi controlli
costitutivi. Una soluzione a questo problema è delegare le proprietà Enabled sia dell'oggetto
UserControl sia dell'oggetto cmdMina, in modo che l'utilizzatore del controllo possa scegliere
liberamente se disattivarlo completamente o soltanto "in parte". Questa soluzione va bene
nel nostro caso specifico, ma naturalmente potrebbe non essere adatta a qualunque
situazione: lo sviluppatore deve valutare caso per caso se è opportuno esporre membri
dell'interfaccia dei controlli costitutivi. Inseriamo quindi la proprietà EnabledCmd:
Public Property Get EnabledCmd() As Boolean
EnabledCmd = cmdMina.Enabled
End Property
Public Property Let EnabledCmd(ByVal vNewValue As Boolean)
cmdMina.Enabled = vNewValue
PropertyChanged "EnabledCmd"
End Property
Inoltre, per la nostra Mina risulta molto utile generare un apposito evento per il click destro, in
modo che l'utilizzatore sappia esattamente quando occorre disabilitare solo il cmdMina e non
l'intero UserControl; per far ciò dichiariamo due eventi "click":
Option Explicit
Public Event Resize()
Public Event ClickLeft()
Public Event ClickRight()
ClickLeft corrisponde al normale evento Click, ClickRight corrisponde al click col tasto destro
che in precedenza avevamo intercettato tramite l'evento MouseDown; ora l'intercettazione
non viene più fatta a livello dell'evento generato nell'applicazione Campo Minato, ma
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3461&idArea=10 (4 of 5)12/09/2003 12.20.31
Programmazione.it: Il portale Italiano sull'Information Technology
direttamente nel controllo ActiveX: in altri termini, non è più l'utilizzatore del controllo a
preoccuparsi dell'intercettazione del click destro, ma è il suo sviluppatore, che fornisce
direttamente l'evento ClickRight nell'interfaccia del controllo. L'intercettazione deve avvenire
nell'evento MouseDown sia dello UserControl sia di cmdMina, perché quest'ultimo potrebbe
essere disattivato anche se lo UserControl è ancora attivo; se il click destro fosse intercettato
solo nell'evento MouseDown di cmdMina non avremmo risolto il problema che abbiamo
incontrato nella scorsa lezione. Infatti, se cmdMina è attivo, tutti gli input del mouse andranno
su di esso senza generare gli eventi corrispondenti di UserControl: quindi è necessario
intercettare l'evento cmdMina_MouseDown; invece se cmdMina è disattivo, gli input del
mouse andranno solo sullo UserControl e ovviamente non su cmdMina: perciò è necessario
intercettare anche l'evento UserControl_MouseDown:
Private Sub UserControl_MouseDown(Button As Integer, Shift As Integer, x As
Single, y As Single)
If Button = vbRightButton Then
RaiseEvent ClickRight
End If
End Sub
Private Sub cmdMina_Click()
RaiseEvent ClickLeft
End Sub
Private Sub cmdMina_MouseDown(Button As Integer, Shift As Integer, X As
Single, Y As Single)
If Button = vbRightButton Then
RaiseEvent ClickRight
End If
End Sub
Anche l'evento ClickLeft potrebbe essere generato dall'evento cmdMina_MouseDown, a
discrezione dello sviluppatore. Ora l'utilizzatore del controllo può scrivere del codice come
questo:
Private Sub Mina1_ClickRight()
Mina1.EnabledCmd = Not Mina1.EnabledCmd
End Sub
Cliccando col tasto destro sull'istanza Mina1, l'utilizzatore attiva o disattiva automaticamente
il solo cmdMina del controllo ActiveX, lasciando attivo il controllo in se stesso e permettendo
così l'intercettazione di un ulteriore click col tasto destro; in questo modo non occorre più
"inibire" il click sinistro utilizzando un flag come avevamo fatto nella precedente lezione,
perché il click sinistro viene generato solo quando cmdMina è attivato. Inserendo
un'istruzione del tipo: Debug.Print "pippo" nell'evento Mina1_ClickLeft, ci si può rendere
conto meglio di come funziona il procedimento implementato.
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3461&idArea=10 (5 of 5)12/09/2003 12.20.31
Programmazione.it: Il portale Italiano sull'Information Technology
Programmazione.it >> Visual Basic >>
Autore:
Giorgio Abraini
Lezione 18
Guida dell'area:
Carmine De Marco
Continua il corso dedicato a Visual Basic: poste le premesse nel Corso di tredici lezioni
che lo ha preceduto, ci rivolgiamo, con questa Parte II, ai lettori che hanno già posto le
basi nella conoscenza di questo linguaggio.
Giorgio Abraini
Cerca
Data: 04-07-2003 - Voto:
Cerca!
Vai alla ricerca
avanzata
Login
Login
Password
Login
- Votanti: 34 - Lettori: 18858 - Livello: Medio
Nella scorsa lezione abbiamo visto come creare una mina personalizzata in grado di
generare tre eventi: Resize, ClickLeft e ClickRight; gli ultimi due devono essere sfruttati dal
progetto Campo Minato per abilitare/disabilitare le mine. Prima di procedere alla sostituzione
delle vecchie mine (command button) con le nuove mine (usercontrol activex), aggiungiamo
qualche caratteristica che può tornarci utile: nella precedente versione del Campo Minato
avevamo utilizzato la proprietà Tag dei pulsanti per sapere se nascondevano una mina o no;
ora che abbiamo un controllo apposito possiamo aggiungere la proprietà adatta per questa
informazione.
Nella sezione delle dichiarazioni generali aggiungiamo una variabile privata denominata
mlNumeroMine: dato che dovrà contenere un numero intero, Long sembra essere il tipo
adatto:
Option Explicit
Private mlNumeroMine As Long
Public Event Resize()
Public Event ClickLeft()
Public Event ClickRight()
Dimenticata la password?
Clicca qui
Aggiungiamo anche una proprietà NumeroMine per rendere accessibile l'informazione al
Non sei ancora iscritto?
client che userà il controllo:
Clicca qui
Hosting
Telexa.net
DnsHosting.it
EuroLogon.com
Dominiando.it
FuturaHost.com
HostingSolutions.it
Consultingweb.it
Olimont.com
Partners
ASPItalia.com
ZioBudda
FastUrl
NewBie
OSWay
TC Sistema
Public Property Get NumeroMine() As Long
NumeroMine = mlNumeroMine
End Property
Public Property Let NumeroMine(ByVal vNewValue As Long)
mlNumeroMine = vNewValue
End Property
Mailing List
Visual Basic
mlNumeroMine conterrà il numero di mine circostanti ogni specifica istanza del controllo; il
valore –1 indicherà la presenza di una mina proprio sotto l'istanza considerata: la proprietà
NumeroMine quindi fa le veci della proprietà Tag in precedenza utilizzata. Già che ci siamo,
deleghiamo la proprietà Caption del pulsante del nostro controllo ActiveX in modo che sia
accessibile dagli utilizzatori del componente:
Public Property Get Caption() As String
Caption = cmdMina.Caption
End Property
Public Property Let Caption(ByVal vNewValue As String)
cmdMina.Caption = vNewValue
End Property
Dobbiamo inoltre delegare anche la proprietà Picture, attraverso la quale viene visualizzata
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3571&idArea=10 (1 of 6)12/09/2003 12.21.18
Visual Basic .NET Corso di
programmazione
Programmazione.it: Il portale Italiano sull'Information Technology
una bandiera sulle mine disabilitate:
Videoproiettore NEC Public Property Get Picture() As IPictureDisp
Set Picture = cmdMina.Picture
End Property
Public Property Set Picture(ByVal vNewValue As IPictureDisp)
cmdMina.Picture = vNewValue
End Property
Barometro Trekking
Come avevamo già visto in precedenza (e come si può capire consultando il visualizzatore
oggetti), la proprietà Picture dell'oggetto CommandButton è un'istanza della classe
IPictureDisp: dello stesso tipo dovrà pertanto essere la proprietà Picture aggiunta al nostro
controllo ActiveX. Trattandosi di un oggetto, la routine di impostazione della proprietà dovrà
essere una Property Set e non una Property Let; per lo stesso motivo si utilizza l'istruzione
Set nel codice della routine Property Get. Naturalmente il pulsante contenuto nello
UserControl dovrà avere la proprietà Style impostata a 1-Graphical.
Prima di sostituire le vecchie mine con le nuove, sarebbe bene fare in modo che il
caricamento dei controlli avvenga in modo automatico anziché disporli manualmente sul
form: finché sono 16 si può anche fare a mano, ma il giorno in cui si decidesse di portarli a
100… La cosa importante è che ci sia almeno un'istanza disegnata sul form, con indice zero,
come se fosse il primo elemento di una matrice di controlli; poi usando l'istruzione Load se ne
possono caricare quanti si vuole. Per semplificarci la vita definiamo nel form Form1 un paio
di costanti per memorizzare il numero di "righe" e "colonne" della matrice di pulsanti "Mina": il
concetto di righe e colonne ha a che fare con la loro disposizione spaziale nel form, non con
le dimensioni della matrice di controlli, poiché tale matrice è unidimensionale; continuando
con gli esempi finora fatti, i pulsanti saranno 16 divisi in 4 righe e 4 colonne:
Private Const mlNUMERO_RIGHE As Long = 4
Private Const mlNUMERO_COLONNE As Long = 4
Nella routine di inizializzazione del form dovremo perciò caricare 15 pulsanti (il primo esiste
già e ha indice zero impostato in fase di progettazione) disponendoli in una matrice 4 x 4:
Private Sub Form_Load()
Dim lContRighe As Long
Dim lContColonne As Long
Dim lContPulsanti As Long
MaxContaMine = 13
lContPulsanti = 0
For lContRighe = 1 To mlNUMERO_RIGHE
If lContPulsanti Then
lContPulsanti = lContPulsanti + 1
Load Mina(lContPulsanti)
With Mina(lContPulsanti)
.Enabled = False
.Visible = True
.Left = Mina(lContPulsanti - mlNUMERO_COLONNE).Left
.Top = Mina(lContPulsanti - 1).Top + Mina(lContPulsanti - 1).Height
End With
End If
For lContColonne = 2 To mlNUMERO_COLONNE
lContPulsanti = lContPulsanti + 1
Load Mina(lContPulsanti)
With Mina(lContPulsanti)
.Enabled = False
.Visible = True
.Left = Mina(lContPulsanti - 1).Left + Mina(lContPulsanti - 1).Width
.Top = Mina(lContPulsanti - 1).Top
End With
Next lContColonne
Next lContRighe
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3571&idArea=10 (2 of 6)12/09/2003 12.21.18
Alla scoperta di
Visual Basic & VB .
NET
Programmazione.it: Il portale Italiano sull'Information Technology
End Sub
Nella routine vengono utilizzati tre contatori: uno per le righe, uno per le colonne, uno per i
pulsanti; mentre i primi due sono gestiti dai rispettivi cicli, il terzo serve per la matrice dei
controlli vera e propria. Il ciclo per riga non fa altro che impostare le coordinate del primo
pulsante di ogni riga in base a quelle del primo pulsante della riga precedente (il nuovo
pulsante viene messo sotto): queste impostazioni sono naturalmente saltate a piè pari se
l'indice lContPulsanti è ancora a zero, perché in tal caso stiamo trattando il pulsante Mina(0)
che abbiamo già inserito in fase di progettazione. All'interno del ciclo per riga c'è il ciclo per
colonna, che ripete sostanzialmente le stesse istruzioni con la differenza che le coordinate
dipendono dal pulsante precedente nella medesima riga (il nuovo pulsante viene messo
affianco). Tutti i pulsanti vengono inizialmente disabilitati, poiché l'abilitazione avverrà nel
momento in cui il giocatore decide di iniziare una nuova partita. Volendo modificare la
disposizione dei pulsanti basterà cambiare il valore assegnato alle costanti
mlNUMERO_RIGHE e mlNUMERO_COLONNE.
Se provate ad avviare il progetto vi dovreste accorgere di una cosa curiosa: tutti i pulsanti
sono disabilitati tranne il primo. Ciò avviene perché l'istruzione Enabled=False viene ignorata
quando lContPulsanti è uguale a zero; per evitare l'inconveniente si può forzare il programma
ad eseguire quell'istruzione (utilizzando ad es. la clausola Else nel costrutto If…Then) oppure
si può impostare direttamente a False la proprietà Enabled del pulsante Mina(0) in fase di
progettazione. In realtà questa seconda procedura non funziona, per un motivo molto
semplice: Visual Basic non salva il valore della proprietà modificato dall'utente. Mentre le
proprietà gestite direttamente dall'Extender (ad es. Left e Top) sono automaticamente salvate
da Visual Basic quando l'utente le cambia in fase di progettazione, le altre proprietà (come
appunto Enabled, che abbiamo delegato con le routine Property) necessitano di un
salvataggio esplicito: tale salvataggio, e la corrispondente lettura del valore modificato,
avvengono rispettivamente negli eventi WriteProperties e ReadProperties dell'oggetto
UserControl. In pratica, ogni volta che l'istanza del pulsante che abbiamo inserito nel form
viene distrutta (ad esempio quando si chiude la finestra di progettazione del form), viene
generato l'evento WriteProperties che provvede a salvare i valori correnti delle proprietà del
componente. Quando invece l'istanza viene ricreata (ad es. riattivando la finestra di
progettazione in precedenza chiusa), viene generato l'evento ReadProperties, che assegna i
valori delle proprietà secondo le ultime impostazioni salvate. Tutti questi valori sono
fisicamente memorizzati nel file *.frm che definisce il form dell'applicazione con i controlli
disegnati al suo interno: è un semplice file di testo che contiene, per ogni controllo, i valori
delle proprietà (non tutte) ed eventualmente le variabili e tutto il codice che vediamo nella
finestra del codice corrispondente al form. Ai file *.frm per i form corrispondono i file *.ctl per i
controlli ActiveX. Naturalmente il file su disco viene salvato solo alla chiusura del progetto;
tutte le modifiche effettuate mentre il progetto è ancora aperto sono mantenute in memoria
ma non effettivamente scritte sul disco.
Ora, affinché le nostre proprietà personalizzate possano "ricordare" l'ultimo valore assegnato
dall'utente, dobbiamo inserire del codice apposito nei suddetti eventi ReadProperties e
WriteProperties, ad esempio:
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
On Error Resume Next
With PropBag
Enabled = .ReadProperty("Enabled", True)
EnabledCmd = .ReadProperty("EnabledCmd", True)
Caption = .ReadProperty("Caption")
End With
End Sub
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
With PropBag
Call .WriteProperty("Enabled", UserControl.Enabled, True)
Call .WriteProperty("EnabledCmd", cmdMina.Enabled, True)
Call .WriteProperty("Caption", cmdMina.Caption)
End With
End Sub
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3571&idArea=10 (3 of 6)12/09/2003 12.21.18
Programmazione.it: Il portale Italiano sull'Information Technology
L'oggetto PropertyBag , argomento di entrambi gli eventi, è quello che contiene le
informazioni sui valori delle proprietà e che consente di leggerle/scriverle tramite i metodi
"ReadProperty" e "WriteProperty": il primo vuole come argomenti il nome della proprietà ed
eventualmente il valore di default, da usare nel caso in cui non sia disponibile un valore
salvato; il secondo invece, oltre al nome della proprietà, vuole anche il valore di essa da
salvare ed eventualmente ancora il valore di default. L'ultimo parametro (valore di default) è
importante perché il valore della proprietà è effettivamente salvato solo se è diverso da
quello di default, in modo da risparmiare tempo e spazio. L'oggetto PropertyBag è gestito
direttamente da Visual Basic, il programmatore deve solo preoccuparsi di leggere/salvare le
proprietà che gli interessano: un valido aiuto in questo compito è dato dalla aggiunta
"Creazione guidata interfaccia controlli ActiveX" (menu aggiunti), che inserisce
automaticamente il codice necessario per le proprietà selezionate. Nell'evento
ReadProperties è stata aggiunta un'istruzione di gestione degli errori come suggerito dalla
guida, per evitare problemi nel caso in cui un furbastro abbia modificato manualmente il file *.
frm inserendo valori non validi per una certa proprietà. Non tutte le proprietà necessitano di
essere salvate: ad es. la proprietà NumeroMine non ha bisogno di essere salvata, perché il
suo valore è generato in fase di esecuzione dall'applicazione client, non è modificato
dall'utente in fase di progettazione. C'è poi un terzo evento, InitProperties , che viene
generato quando l'istanza del componente è creata per la prima volta (ad es. quando il
pulsante è disegnato sul form): infatti in questo caso non esistono valori delle proprietà
precedentemente salvati, perciò più che "leggere" le proprietà occorre "inizializzarle": anche
in questo caso torna molto utile il valore di default, che è opportuno specificare sempre, se
possibile. Ora, impostando a False la proprietà Enabled del pulsante Mina(0), questo sarà
disabilitato come gli altri all'avvio del progetto, perché all'avvio del progetto l'istanza di
progettazione viene distrutta, generando l'evento WriteProperties che salva il valore corrente
della proprietà Enabled; quando viene creata l'istanza di esecuzione, l'evento
ReadProperties assegna a tale proprietà il valore appena salvato.
Tornando alla sostituzione delle mine vecchie con le nuove, un'altra routine da modificare è
quella dell'inizio di una nuova partita (mnuNew_Click): praticamente si tratta solo di sostituire
la proprietà Tag con la proprietà NumeroMine e la parola chiave "True" con il valore –1
(anche se in realtà è la stessa cosa). Già che ci siamo approfittiamone anche per usare le
costanti per il numero di righe e colonne anziché i valori letterali come 4 o 16:
Private Sub mnuNew_Click()
Dim t As Integer 'variabile temporanea per eseguire i controlli
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
Randomize Timer
PosMine(0) = Int(Rnd * mlNUMERO_RIGHE * mlNUMERO_COLONNE)
Mina(PosMine(0)).NumeroMine = -1
Estrai:
t = Int(Rnd * mlNUMERO_RIGHE * mlNUMERO_COLONNE)
If t = PosMine(0) Then
GoTo Estrai
Else
PosMine(1) = t
Mina(PosMine(1)).NumeroMine = -1
End If
EstraiDiNuovo:
t = Int(Rnd * mlNUMERO_RIGHE * mlNUMERO_COLONNE)
If t = PosMine(0) Or t = PosMine(1) Then
GoTo EstraiDiNuovo
Else
PosMine(2) = t
Mina(PosMine(2)).NumeroMine = -1
End If
'aggiorna le proprietà NumeroMine dei pulsanti
For i = 0 To (mlNUMERO_RIGHE * mlNUMERO_COLONNE - 1)
Mina(i).NumeroMine = 0
Next i
For i = 0 To 2
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3571&idArea=10 (4 of 6)12/09/2003 12.21.18
Programmazione.it: Il portale Italiano sull'Information Technology
Mina(PosMine(i)).NumeroMine = -1
X = Int(PosMine(i) / mlNUMERO_COLONNE) 'riga in cui si trova la mina
Y = (PosMine(i) Mod mlNUMERO_COLONNE) 'colonna in cui si trova la mina
For x1 = IIf(X = 0, 0, X - 1) To IIf(X = mlNUMERO_RIGHE - 1,
mlNUMERO_RIGHE - 1, X + 1)
For y1 = IIf(Y = 0, 0, Y - 1) To IIf(Y = mlNUMERO_COLONNE - 1,
mlNUMERO_COLONNE - 1, Y + 1)
If Mina(mlNUMERO_COLONNE * x1 + y1).NumeroMine > -1 Then
Mina(mlNUMERO_COLONNE * x1 + y1).NumeroMine = Mina(mlNUMERO_COLONNE
* x1 + y1).NumeroMine + 1
End If
Next y1
Next x1
Next i
For i = 0 To (mlNUMERO_RIGHE * mlNUMERO_COLONNE - 1)
With Mina(i)
.Caption = ""
.Enabled = True
.EnabledCmd = True
Set .Picture = LoadPicture()
End With
Next i
ContaMine = 0
lblTempo.Caption = ""
lblMine.Caption = "3"
ContaSecondi = 0
ContaBandiere = 0
Timer1.Enabled = True
End Sub
Un analogo trattamento va fatto con le routine Mina_Click (che ora diventa Mina_ClickLeft) e
Mina_MouseDown (che ora diventa Mina_ClickRight):
Private Sub Mina_ClickLeft(Index As Integer)
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 (ContaMine < MaxContaMine) And (Mina(Index).Picture.Handle = 0) Then
If Mina(Index).NumeroMine > 0 Then 'nessuna mina esplosa, almeno una mina
circostante
If Len(Mina(Index).Caption) = 0 Then
ContaMine = ContaMine + 1
End If
Mina(Index).Caption = CStr(Mina(Index).NumeroMine)
ElseIf Mina(Index).NumeroMine = -1 Then 'mina esplosa
Mina(Index).Caption = "M"
For X = 0 To 2
Mina(PosMine(X)).Caption = "M"
Next X
Timer1.Enabled = False
ContaMine = MaxContaMine
Exit Sub
Else 'mina(index).NumeroMine=0 (nessuna mina esplosa, nessuna mina
circostante)
X = Int(Index / mlNUMERO_COLONNE) 'riga in cui si trova il pulsante
premuto
Y = Index Mod mlNUMERO_COLONNE 'colonna in cui si trova il pulsante
premuto
For x1 = IIf(X = 0, 0, X - 1) To IIf(X = mlNUMERO_RIGHE - 1,
mlNUMERO_RIGHE - 1, X + 1)
For y1 = IIf(Y = 0, 0, Y - 1) To IIf(Y = mlNUMERO_COLONNE - 1,
mlNUMERO_COLONNE - 1,Y+1)
If Len(Mina(mlNUMERO_COLONNE * x1 + y1).Caption) = 0 Then
ContaMine = ContaMine + 1
End If
Mina(mlNUMERO_COLONNE*x1+y1).Caption=CStr(Mina(mlNUMERO_COLONNE*x1+
y1).NumeroMine)
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3571&idArea=10 (5 of 6)12/09/2003 12.21.18
Programmazione.it: Il portale Italiano sull'Information Technology
Next y1
Next x1
End If
If ContaMine = MaxContaMine Then
MsgBox "HAI VINTO!"
Timer1.Enabled = False
End If
End If
End Sub
Private Sub Mina_ClickRight(Index As Integer)
With Mina(Index)
If .Picture.Handle = 0 Then 'nessuna bandiera sul pulsante
Set .Picture = LoadPicture("BandieraCampoMinato.ico")
ContaBandiere = ContaBandiere + 1
.EnabledCmd = False
Else 'il pulsante ha già la bandiera
Set .Picture = LoadPicture()
ContaBandiere = ContaBandiere - 1
.EnabledCmd = True
End If
End With
lblMine.Caption = CStr(3 - ContaBandiere)
End Sub
Ora che sembra finalmente tutto a posto, sorge però un problema: lo vedremo la prossima
lezione.
Vai a pagina:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
Vota - Segnala ad un amico - Inserisci un commento - Aggiungi ai preferiti
Autori / Pubblicità / Chi siamo / Copyright / Dicono di noi / Entra nello Staff / Mailing List / Contattaci
Programmazione.it - Testata giornalistica registrata col N. 569 presso il Tribunale di Milano il 14/10/2002 - OSWay S.r.l.
http://www.programmazione.it/index.php?entity=earticle&idArticle=566&idPage=3571&idArea=10 (6 of 6)12/09/2003 12.21.18