Connessione al database con Visual Basic NET? Lo trovi su Opentraining.it Visual Basic Italia
PRINCIPALE > CORSO DI VISUAL BASIC

Eseguire una ricerca veloce nell' archivio delle risorse di Visual Basic Italia®: 

Preso dall'archivio...

Premere il pulsante sotto per accedere direttamente ad un articolo o ad un esempio preso in modo casuale dall'archivio.



Ultimo e-book pubblicato:

"INTRODUZIONE AI CSS"

Lo scopo del CSS language è quello di controllare lo stile dei vari elementi che concorrono a formare un
documento HTML.
Si può operare in tre modi collegamento ad un foglio di stile esterno;definizione degli stili all'inizio
del documento utilizzo della proprietà style all'interno di un Tag HTML (embedded style). Questo e-book introduttivo
servirà per apprendere tali nozioni fondametali dei fogli di stile.

Prezzo: € 0.0.
Presentazione:
REAL SOFTWARE RILASCIA LA VERSIONE 5.0 di REALbasic per Windows






Gorizia, 5 maggio 2003 - Active, distributore in esclusiva di REALSoftware, Austin, Tx, annuncia la disponibilità di REALbasic 5.0 per Windows, uno strumento per lo sviluppo semplice da usare che permette agli utenti Windows di tutti i livelli di creare applicazioni personalizzate e di compilarle sia per la piattaforma Windows che per quella Macintosh.
[>>]

http://www.active-software.com

 

Contatti. Utilizzare l'email generica per domande relative al sito:
Porre domande relative al sito
oppure scrivere ad un responsabile di area.
Responsabile del sito: >Andrea Martelli
Responsabile area "Corso di VB":
> Giorgio Abraini

Corso di Visual Basic: lezione 31

Questa lezione, consultata da 52537 utenti, è stata giudicata di ottimi contenuti , con un'esposizione perfettamente comprensibile e con un livello di approfondimento ottimo da 90 votanti.


Lezione 32

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()

Aggiungiamo anche una proprietà NumeroMine per rendere accessibile l'informazione al client che userà il controllo:

Public Property Get NumeroMine() As Long
NumeroMine = mlNumeroMine
End Property

Public Property Let NumeroMine(ByVal vNewValue As Long)
mlNumeroMine = vNewValue
End Property

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 una bandiera sulle mine disabilitate:

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

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

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 es. 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


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" (menù aggiunte), 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
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)
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.