INTRODUZIONE A MICROSOFT® VISUAL BASIC® Microsoft® Visual Basic® 6.0 Microsoft® Visual Basic® è uno strumento di sviluppo della famiglia Microsoft® Visual Studio® che permette di realizzare in modo rapido ed immediato (RAD: Rapid Application Development) applicazioni altamente integrate con l'insieme di sistemi operativi Windows® di Microsoft. Il prodotto è distribuito in tre versioni principali: Learning Edition Professional Edition Enterprise Edition La differenza fonamentale tra le tre versioni non riguarda la sintassi del linguaggio di programmazione ma gli strumenti sviluppo disponibili. La versione Learning infatti può essere vista come una versione di base orientata agli studenti o agli hobbisti che vogliono iniziare a conoscere il VB. E' ottima per imparare ma inevitabilmente finirete per sentire la necessità degli strumenti presenti nelle versioni maggiori se inizierete ad usare con continuità questo strumento. La versione Professional infatti include strumenti indispensabili per la creazione di applicazione client/server e per Internet. La versione Enterprise è infine la scelta corretta per gruppi di sviluppatori che necessitano di tutti gli strumenti per monitorare e migliorare le prestazioni e l'efficienza del loro codice. Ovviamente i prezzi dei tre prodotti cambiano in funzione delle caratteristiche, quindi si passa dalle circa 200 mila Lire (109$) della versione Learning, al milione abbondante (549$) della edizione Professional per finire con circa 3 milioni (1299$) della versione Enterprise. Se la vostra intenzione è solo quella di valutare il prodotto vi consiglio la versione Learning in quanto contiene anche un manuale di "avviamento" alla programmazione su cd-rom. Se avete intenzione di sviluppare delle applicazioni professionali però vi ocnviene comprare direttamente la versione Professional. Un consiglio che mi sento di darvi: usate la versione Inglese. Il VB è un linguaggio molto simile all'inglese, non sono 3 voci di menù in italiano che ci semplificano la vita, l'inglese dobbiamo conoscerlo comunque per poter utilizzare il VB. Spesso le versioni nazionalizzate hanno problemi/bug specifici che nelle versioni inglesi e/o internazionali non sono presenti. I fix spesso escono prima nella versione inglese e poi nelle versioni nazionalizzate, quindi se avete il VB italiano vi tocca aspettare. Se volete prima o poi certificarvi come Microsoft Certified Professional non tutti gli esami esistono in italiano. Quelli sul VB sono tutti in inglese quindi se voi imparate ad usare il VB in italiano rischiate poi di non sapere le risposte alle domande relative all'interfaccia utente. Cosa possiamo fare ? In questa prima "puntata" intendo dare solo una panoramica veloce delle possibilità offerte dal VB. Con le versioni Professional/Enterprise possiamo realizzare le seguenti tipologie fondamentali di applicazione: Standard EXE: sono i classici programmi eseguibili, pensati per le postazioni desktop ActiveX EXE: sono applicazioni eseguibili che espongono ulteriori informazioni per essere raggiungibili anche da altri programmi/computer ActiveX DLL: sono librerie COM (Component Object Model) ... vedremo bene di cosa si tratta nella seconda metà del tutorial ActiveX Control: sono dei controlli grafici che possiamo inserire nelle nostre applicazioni o in pagine Web ActiveX Documents: sono dei particolari documenti visualizzabili in container (Contenitore di Office, Internet Explorer, ecc.) AddIn: sono programmi che servono a noi programmatori per automatizzare determinati compiti durante lo sviluppo delle nostre applicazioni DHTML Application: sono applicazioni Web basate sul Dynamic HTML IIS Application: sono applicazioni Internet particolari che permettono di inviare ai client Web (Explorer, Netscape, ecc.) pagine HTML pure anche se generate dinamicamente sul server Kit di sopravvivenza Prima di buttarci a capofitto nello sviluppo VB mi permetto di consigliarvi alcuni strumenti che potete pensare come una sorta di kit di sopravvivenza. Questi sono: Programmare Microsoft Visual Basic 6.0 - Francesco Balena (Microsoft Press - Mondadori Informatica) Microsoft Access® 97/2000 - non un libro ma il software in quanto tale. Microsoft Windows NT® Option Pack o il Personal Web Server di Windows® 9x Il libro di Francesco Balena è un comodo supporto allo sviluppo di tutti i giorni. I software sono praticamente indispensabili per poter lavorare e di conseguenza saranno fondamentali anche per alcune delle realizzazione che svolgeremo nella seconda parte di questo tutorial. INTERFACCIA UTENTE E PROGRAMMAZIONE GUIDATA DAGLI EVENTI Avviamo il VB Se mandiamo in esecuzione il nostro Microsoft® Visual Basic® 6.0 ci troviamo all'interno dell'ambiente di sviluppo. Tramite questa interfaccia possiamo creare tutte le nostre applicazioni e i nostri componenti. La prima schermata che ci si presenta è quella che ci permette di scegliere il tipo di applicazione che stiamo iniziando a creare (Standard EXE, ActiveX EXE, ActiveX DLL, ecc.). In questa primo caso ci occuperemo di un applicativo desktop tradizionale e quindi chiederemo al VB di creare un modello di progetto Standard EXE. Questa scelta comporta il fatto che il VB crei un nuovo progetto e vi inserisca automaticamete le informazioni necessarie a gestire la visualizzazione di una finestra. Tradotto in pratica questo determina la creazione di un progetto di applicazione inizialmente costituita da una sola form. La prima Form Selezionato il tipo di progetto quindi ci troviamo nell'ambiente di sviluppo VB e sullo schermo del nostro PC avremo la possibilità di disegnare la nostra interfaccia utente tramite il mouse e la casella degli strumenti, di default alla sinistra della finestra. Un approccio classico allo sviluppo di applicazioni desktop VB è quindi il disegno dell'interfaccia utente. Pensiamo di voler inserire un pulsante nella nostra form. Non dovremo fare altro che selezionare nella casella degli strumenti lo strumento CommandButton e poi disegnarne la forma sulla form affinché il pulsante venga aggiunto all'interfaccia grafica della stessa. Se a questo punto volessimo eseguire (tasto F5 = Esegui) il nostro programma - che non farà assolutamente nulla - vedremmo che comunque il programma è già in grado di funzionare ed infatti la sua esecuzione comporta la visualizzazione di una finestra con lo stesso aspetto della form da noi definita. Questo ci permette di concludere che le applicazioni desktop per essere realizzate in termini di interfaccia grafica possono non richiedere alcun elemento di codice. In reltà vedremo nel prosieguo di questo tutorial che molto spesso l'interfaccia grafica viene caricata o quantomento gestita dal codice e quindi nelle applicazioni concrete non basterà mai definire soltanto l'aspetto grafico degli oggetti. Se premiamo il tasto F4 (= Visuallizza Proprietà) possiamo accedere ad una finestrella come quella riportata in figura, che ci permette di definire la maggior parte delle informazioni relative ai nostri oggetti disegnati sullo schermo. Ad esempio generalmente ci sono le proprietà per selezionare il colore di sfondo dell'oggetto, il colore in primo piano, ecc. Una buona regola di comportamento, attuabile tramite l'utilizzo di questa finestra di proprietà, è assegnare al progetto, alle form ed ai controlli dei nomi (proprietà (Name) nel pannellino) sensati, meglio se nel rispetto della notazione ungarica. La programmazione ad eventi Definita l'interfaccia utente del mio programma devo pensare a scrivere il codice che la gestisca. L'idea alla base di questo modo di ragionare è che, definito un elemento nell'interfaccia utente di un applicazione, posso codificare la procedura da fargli eseguire quando questo elemento viene premuto con il mouse, piuttosto che quando il mouse gli passa sopra senza premerlo. Il bello di questi programmi è che l'utente finale delle nostre applicazioni di solito è libero di premere un tasto piuttosto che un altro, di muovere il mouse sopra un controllo piuttosto che un altro. Quindi si potrebbe verificare potenzialmente qualsiasi delle casistiche accennate. Questa idea di applicazione che risponde alle richieste ed alle azioni dell'utente, richiamando determinate procedure senza un ordine di esecuzione prestabilito, ci porta ad avere programmi che saranno costituiti da piccoli blocchi di codice, che gestiranno le singole situazioni che si possono verificare (click del mouse, movimenti del mouse, altro...). Quindi si parla di programmazione guidata dagli eventi (Event-Driven) perchè tutto ciò che si verifica è dovuto ad eventi associati ad elementi della nostra interfaccia grafica. Gestiamo il click di un pulsante Chiarito il concetto di programmazione guidata dagli eventi vediamo come gestire un semplice evento. Inseriamo un pulsante di comando all'interno della nostra interfaccia utente e chiamiamolo cmdButton tramite la finestra delle proprietà (F4). Facendo doppio click sull'oggetto appena definito comparirà una finestra che ci permette di scrivere del codice associato all'evento predefinito dell'oggetto. Si parla di evento predefinito perchè un oggetto può essere in grado di gestire diversi eventi (click, doppio click, mousemove, mouseover, ecc.). Nel caso del pulsante di comando ci troveremo all'interno della procedura di gestione dell'evento Click e quindi ci si presenterà una schermata simile a quella nella seguente. Osserviamo che ci troviamo in un modulo di codice associato ad una form ed all'interno di questo troviamo della sintassi VB (che nelle prossime lezioni approfondiremo) che ci definisce una procedura cmdButton_Click, la quale scatterà ogni volta che il nostro utente farà click con il mouse sul pulsante cmdButton. Per verificare il funzionamento di questa logica ad eventi abbiamo inserito un'istruzione MsgBox "Hai fatto click sul pulsante cmdButton !" che altro non fa che aprire sullo schermo una finestrella di messaggio che riporterà come messaggio appunto la dicitura che le forniamo come argomento. Eseguendo (F5) il nostro progetto possiamo renderci conto del fatto che ora, contrariamente al progetto privo di codice in esecuzione, la nostra applicazione ogni volta che viene premuto il pulsante di comando eseguirà questa riga di codice e visualizzerà il messaggio sullo schermo. COSTRUTTI FONDAMENTALI DEL LINGUAGGIO VISUAL BASIC Aree di codice In Microsoft® Visual Basic® possiamo avere diversi file e moduli di codice all'interno di un unico progetto di applicazione. Nei moduli di codice di Microsoft® Visual Basic® abbiamo diverse aree che possiamo localizzare selezionandole nelle caselle a discesa che troviamo nella parte alta dei moduli di codice stessi. Queste aree sono: (Generale) - (Dichiarazioni) => in questa dovremo inserire informazioni di interesse per tutto il modulo di codice. Nome_Oggetto - Procedura Evento => in queste aree dovremo caricare il codice relativo a singole procedure evento (per es. Command1_Click). (Generale) - Procedura Generica => in queste aree avremo il codice relativo alle procedure e funzioni da noi definite (ne parleremo tra poco). All'interno di queste aree dovremo inserire del codice di programmazione nel rispetto della sintassi del linguaggio ed appoggiandoci ai costrutti che vedremo in questa lezione. Le Variabili Anche in Microsoft® Visual Basic® 6.0 come in qualsiasi linguaggio di programmazione è possibile dichiarare delle variabili. Le variabili sono un'area di memoria dove è possibile salvare delle informazioni per riutilizzarle in seguito. Le parole chiave per dichiarare delle variabili in Microsoft® Visual Basic® sono: Dim NomeVariabile As Tipo Public NomeVariabile As Tipo Private NomeVariabile As Tipo Static NomeVariabile As Tipo Vi sono diverse parole chiave a seconda dello spazio di azione (visibilità) che vogliamo assegnare alle variabili che dichiariamo. In realtà in Microsoft® Visual Basic® è possibile lavorare senza dichiarare le variabili, ma il fatto che un linguaggio offra questa possibilità non significa che noi dobbiamo per forza sfruttarla. Lavorare senza dichiarare le variabili che si utilizzano è una pessima abitudine. Dal momento che lo scopo di questo tutorial è imparare a programmare bene in Microsoft® Visual Basic®, per noi le variabili saranno sempre da dichiarare. Per essere sicuri di non sbagliare possiamo attivare un'opzione che ci costringe a dichiarare le variabili. Dal menù Strumenti->Opzioni selezionare il primo pannello ed attivare "Richiedi la dichiarazione delle variabili". A questo punto vediamo cosa significano le parole chiave elencate precedentemente: Dim significa che stiamo dimensionando una variabile che avrà uno spazio di azione pari a quello della sezione in cui dichiariamo la variabile. Questo significa che se useremo Dim all'interno di una procedura (un blocco di codice che può essere richiamato più volte) allora la variabile che dichiareremo sarà valida soltanto all'interno di quella procedura, soltanto nel momento in cui la procedura è in esecuzione. Di solito la parola chiave Dim si usa proprio in questo caso. Public e Private invece si usano di solito nel dichiarare variabili nella parte che prima abbiamo definito come (Generale) - (Dichiarazioni). Qualora si voglia avere le variabili visibili dagli altri moduli di codice del progetto si userà Public altrimenti Private per avere le variabili visibili solo all'interno del modulo in cui le abbiamo definite. Torneremo più avanti nel Tutorial a parlare di queste cose (sviluppo di componenti). La parola chiave Static infine dimensiona una variabile all'interno di una procedura in modo tale che la stessa mantenga il suo valore tra diverse esecuzioni della procedura. Tutte e quattro le parole chiave richiedono un nome (conviene dare nomi sensati alle variabili) ed un tipo subito dopo la parola chiave As. Per tipo si intende la tipologia di valori che la variabile può assumere. I tipi di dati più comuni definiti da Microsoft® Visual Basic® sono: Tipo di Dato Prefisso Valore Ammesso Boolean bln True/False Byte byt 0-255 Currency cur da -922,337,203,685,477.5808 a 922,337,203,685,477.5807. Date (Time) dtm Data (64 bit) Double dbl da 1.79769313486231E308 a -4.94065645841247E-324 per valori negativi; da 4.94065645841247E-324 a 1.79769313486232E308 per valori positivi. Integer int da -32,768 a 32,767 Long lng da -2,147,483,648 a 2,147,483,647 Object obj Un variabile Oggetto Single sng da -3.402823E38 a -1.401298E-45 per valori negativi; da 1.401298E-45 a 3.402823E38 per valori positivi String str Sequenze di caratteri a lunghezza fissa da 0 a 63K caratteri o a lunghezza variabile da 0 a 2 billioni di caratteri. User-defined type udt Tipi definiti dall'utente Variant vnt Valore Variant (16/22 Byte) Possiamo anche definire dei nostri tipi di dati personalizzati, così come possiamo definire delle matrici di dati. Anche di questo parleremo più avanti nel corso del Tutorial. Quando le variabili vengono inizializzate, le variabili numeriche sono inizializzate a 0, le stringhe a lunghezza variabile sono inizializzate ad una stringa vuota (""), e le stringhe a lunghezza fissa sono riempite con caratteri ASCII zero. Le variabili Variant sono inizializzate al valore Empty. Ciascun elemento di un tipo definito dall'utente viene inizializzato come se fosse un elemento isolato dal resto del tipo definito dall'utente. Operatori Logici Anche in Microsoft® Visual Basic® 6.0 ci sono una serie di operatori logici per valutare e relazionare espressioni. Tra questi i più importanti sono: NOT OR AND XOR Negazione Disgiunzione Congiunzione Esclusione Costrutti Condizionali Tra i costrutti condizionali presenti in Microsoft® Visual Basic® 6.0 i più importanti sono: If ... Then ... Else ... End If Select Case ... End Select Il primo costrutto permette di valutare diverse condizioni e di eseguire del codice al verificarsi della prima condizione che risulti vera. Al verificarsi di una condizione le condizioni seguenti saranno ignorate, se all'interno dello stesso blocco condizionale. I costrutti If possono essere annidati. If condizione Then [istruzioni] [ElseIf condizione-ennesima Then [istruzioni alternative]] ... [Else [istruzioni da eseguire in tutti i casi non previsti]] End If Il secondo costrutto invece permette di valutare diverse situazioni in funzione di un'unica espressione. Risulta comodo quando vi sono espressioni uniche che possono assumere diversi valori a seconda del momento. In questi casi il costrutto permette di svolgere blocchi di istruzioni piuttosto che altri in funzione del valore assunto dall'espressione valutata. Select Case espressione da valutare [Case lista di valori possibili separati da virgole [istruzioni]] ... [Case Else [istruzioni da eseguire in qualsiasi caso non previsto]] End Select Il costrutto Select Case ... End Select è da preferirsi, in termini di prestazioni, al costrutto If ... Then ... End If laddove sia possibile applicarli indistintamente. Costrutti Iterativi I costrutti iterativi sono quelli che permettono di eseguire più volte uno stesso blocco di istruzioni. In Microsoft® Visual Basic® 6.0 abbiamo diverse modalità di costruzione di costrutti iterativi. I più utilizzati sono i costrutti For ... Next e Do ... Loop . Vediamo come funziona il primo: For Contatore = Inizio To Fine [Step Passo] [istruzioni] Next [Contatore] Il che equivale a dire che si vuole ripetere N volte un blocco di istruzioni, per N che varia da Inizio a Fine spostandosi ogni volta di un valore pari al Passo. C'è anche la possibilità di abbandonare l'iterazione prima della fine tramite l'istruzione Exit For. Il costrutto Do ... Loop invece ripete un blocco di istruzioni finchè non diventa vera una determinata condizione. Do {While | Until [condizione]} [istruzioni] Loop {While | Until [condizione]} Se si indica la condizione all'inizio del ciclo (Do), qualora la condizione non sia verificata nemmeno all'inizio del ciclo stesso, non vi sarà alcuna esecuzione di istruzione. Se invece la verifica della condizione viene posta alla fine del ciclo (Loop) allora comunque le istruzioni all'interno del ciclo saranno eseguite almeno una volta. Qualora si utilizzi la parola chiave While il ciclo sarà ripetuto finché la condizione rimane vera, altrimenti nel caso della parola chiave Until l'iterazione si svolgerà finché la condizione rimane falsa e si fermerà non appena la condizione diventa vera. Come nel caso precedente è possibile uscire immediatamente da un ciclo di questo tipo tramite l'istruzione Exit Do. Dichiarazione di Procedure e Funzioni Entreremo meglio nel dettaglio della dichiarazione di procedure e funzioni nelle prossime lezioni del Tutorial su Microsoft® Visual Basic® 6.0 . In questo momento ciò che conta è capire come sia possibile costruire isole di codice richiamabili più volte dalle nostre procedure evento per poter svolgere compiti ripetitivi. Per fare questo ci appoggeremo alla dichiarazione di subroutine (blocchi di istruzioni che svolgono una determinata attività e non restituiscono alcun valore a chi le ha invocate) e funzioni (procedure che restituiscono un particolare valore a chi le invoca). La sintassi da usare è: [{Public | Private}] Sub NomeProcedura ([argomenti]) [istruzioni] End Sub [{Public | Private}] Function NomeFunzione ([argomenti]) As TipoRestituito [istruzioni] End Function Come si intuisce dalle dichiarazioni, la parola chiave Sub identifica la dichiarazione di una subroutine (non restituisce valori) e la parola chiave Function indica la dichiarazione di una funzione (restituisce una variabile di tipo TipoRestituito). Come argomenti vanno passate delle variabili (capiremo meglio più avanti con che modalità) che permettano alle nostre subroutine e funzioni di lavorare su dei dati in ingresso. Queste variabili saranno caratterizzate da un tipo di dato. La nostra prima Funzione Concludiamo quindi questa lunga puntata con un esempio di funzione che restituisce un valore dopo avere eseguito un ciclo nel quale si valutano delle espressioni. Public Function ContaNumeroInteroLungo() As Long 'Questa funzione conta casualmente per 5 secondi 'a partire da quando viene chiamata, 'quindi restituisce il valore contato. Dim lngValoreContato As Long Dim vntInizioConteggio As Variant vntInizioConteggio = Timer Do While (Timer - vntInizioConteggio) <5 lngValoreContato = lngValoreContato + 1 'Se sto andando oltre i limiti di dimensione della 'variabile che ho scelto esco comunque dal ciclo If lngValoreContato > 2147483645 Then Exit Do End If Loop 'Restituisco al chiamante il valore contato ContaNumeroInteroLungo = lngValoreContato End Function LA NOSTRA PRIMA FORM: LA FINESTRA DI BENVENUTO La Form di benvenuto All'interno di un progetto Microsoft® Visual Basic® possiamo inserire diversi elementi, tra questi vi sono le Form che sono il principale strumento per la realizzazione di una interfaccia grafica. La Form è l'equivalente di una qualsiasi finestra di Windows. Il tipico approccio allo sviluppo di applicazioni desktop (destinate all'utente finale) orientato agli eventi in Microsoft® Visual Basic® prevede la realizzazione della parte di interfaccia grafica e l'associazione agli elementi disegnati di procedure evento (vedi Lezione 2). In questa puntata ci concentreremo sugli elementi grafici che generalmente si inseriscono in una finestra di benvenuto. Controlli predefiniti All'interno di Microsoft® Visual Basic® abbiamo una serie di controlli sempre disponibili di seguito riepilogati. Puntatore: serve per selezionare i controlli nella form. Picture: permette di inserire elementi grafici nelle form, può contenere altri controlli al suo interno (si dice controllo contenitore). Label: serve per inserire etichette di testo nella form. TextBox: consente di ricevere informazioni testuali dall'utente della nostra applicazione. Frame: è un altro controllo contenitore che permette di delimitare l'area che contiene con un titolo ed un riquadro tridimensionale. CommandButton: si utilizzano per inserire pulsanti clickabili nelle finestre. CheckBox: forniscono caselle di selezione dove l'utente può abilitare o meno valori, permettono di rappresentare informazioni binarie (Vero/Falso). Spesso si inseriscono in controlli contenitore per raggrupparli. OptionButton: si utilizzano per porre all'utente domande a risposta multipla, di cui una sola per volta può essere vera. Spesso si inseriscono in controlli contenitore per raggrupparli. ComboBox: è la classica casella a discesa tipica delle interfacce grafiche di oggi. Permette di fornire all'utente un elenco di valori che si espande solo quando l'utente preme la freccia di espansione. Fornisce diversi stili di interazione con l'utente. ListBox: è la versione senza pulsante a discesa ma con barre di scorrimento del controllo precedente. HScrollBar, VScrollBar: consentono di disegnare barre di scorrimento orizzontali (HScrollBar) e verticali (VScrollBar). Generalmente si utilizzano per scorrere il contenuto di controlli contenitore. Timer: ci permette di svolgere determinate attività ripetutamente (ogni N millisecondi) oppure dopo un certo momento da quando decidiamo di eseguirle (eventi asincroni). DriveListBox, DirListBox, FileListBox: consentono di creare elementi grafici di selezione del disco, della cartella e dei file dal file system dell'utente finale della nostra applicazione. Si possono utilizzare per costruire finestre di apertura/salvataggio files su disco. Conviene però appoggiarsi agli strumenti forniti dal sistema operativo (Common Dialog) al fine di avere un'interfaccia consistente con il sistema in cui la nostra applicazione viene eseguita. Shape: permette di disegnare forme varie (cerchi, quadrati, ecc.) sulla Form. Line: permette di disegnare linee sulla Form. Image: si utilizza per caricare nel documento file grafici. Contrariamente al controllo Picture non si tratta di un controllo contenitore. Data Control: consente di collegare alcuni dei controlli precedenti ad una fonte dati, noi useremo la versione OLEDB del Data Control e non questa che si appoggia ad una tecnologia di accesso ai dati (DAO = Data Access Objects) ormai in disuso. OLE Control: permette di collegare (OLE = Object Linking and Embeding) documenti da OLE server esterni (Word, Excel, ecc.), ultimamente è poco utilizzato. Disegnamo la Form di benvenuto All'interno di Microsoft® Visual Basic® creiamo quindi un progetto di tipo Standard EXE nuovo. Selezioniamo la Form che viene creata automaticamente (Form1) e gestiamone prima di tutto l'aspetto grafico. Una delle prime attività che conviene svolgere è rinominare il progetto (tasto destro del mouse su Project1 -> Project1 Properties e quindi assegnare un valore sensato alla proprietà Project Name nel nostro caso useremo Biblio_VBIUG). Quindi è una buona regola dare un nome ragionevole anche alle form che si utilizzano. Assegnamo nome frmSplash alla nostra finestra iniziale di benvenuto (tasto destro del mouse su Form1 -> Properties, quindi assegnare il valore alla proprietà (Name) ). La rappresentazione del nostro progetto diventerà quindi la seguente. A questo punto siamo pronti per disegnare il contenuto della nostra finestra di benvenuto. Per prima cosa caratterizzimo l'aspetto grafico della finestra stessa. Dalle proprietà della form configuriamo la finestra come di seguito indicato: Caption = Vuoto (è il titolo della finestra e di solito le finestre di benvenuto non hanno titolo). BorderStyle = 1 - Fixed Single (corrisponde ad un bordo sottile e non ridimensionabile). ControlBox = False (nasconde il menù di sistema e l'icona della finestra in alto a sinistra). StartUpPosition = 2 - CenterScreen (fa in modo che la finestra si apra esattamente al centro dello schermo corrente). Selezioniamo dalla casella degli strumenti il controllo Frame e tracciamo un riquadro nella finestra della dimensione che più ci aggrada. Anche di questo controllo svuotiamo la proprietà Caption in modo tale che non abbia alcun titolo e si presenti solo come un riquadro. Selezioniamo il Frame appena creato e disegnamo al suo interno un controllo Image, diamo a questo ultimo nome imgSplashLogo (proprietà (Name) ). Per caricare un'immagine nel controllo appena disegnato selezioniamo la proprietà Picture e facciamo doppio click sulla stessa per scegliere un file grafico dal disco fisso (nel percorso "C:\Program Files\Microsoft Visual Studio\Common\Graphics" o simile, a seconda di dove è stato installato Visual Basic, troverete diverse immagini già pronte. Nella sotto cartella Icons\Writing selezioniamo il file BOOKS05.ICO). Il controllo immagine appena disegnato si ridimensionerà per contenere secondo le giuste proporzioni l'immagine appena assegnatagli. Per fare in modo che l'immagine sia "stirabile" e si possa quindi ingrandire impostate a True la proprietà Stretch e quindi stirate il controllo fino alla dimensione desiderata. Avremo una frmSplash simile alla seguente: Inseriamo a questo punto un paio di etichette di testo (Label) per rappresentare le informazioni sul prodotto (nome, versione, produttore) e una linea che separi queste informazioni dalle informazioni sul Copyright. Sono sufficienti quattro controlli Label così configurati: 1. (Name) = lblCompany Autosize = True Caption = "Visual Basic - Italian User Group" Font = Verdana, 12, Regular 2. (Name) = lblProductName Autosize = True Caption = "Archivio Biblioteca" Font = Verdana, 14, Bold 3. (Name) = lblProductVersion Autosize = True Caption = "1.0" Font = Verdana, 14, Regular 4. (Name) = lblCopyRight Autosize = True Caption = "(C) VBIUG Tutorials - 2001" Font = Verdana, 10, Italic Inseriamo anche una riga (controllo Line) che divida le informazioni. Il risultato sarà simile al seguente: Se provate ad eseguire questo progetto (F5) vi renderete subito conto che manca qualcosa di fondamentale ... un modo per fermare la finestra di benvenuto e continuare con la normale esecuzione del programma. Per fare questo utilizzeremo due diversi strumenti: Un controllo Timer Una procedura evento Il controllo Timer possiamo inserirlo dove vogliamo nella form, si tratta infatti di un controllo invisibile in fase di esecuzione e quindi non rovinerà l'aspetto grafico della nostra finestra. Assegnamogli le seguenti proprietà: (Name) = tmrUnload Interval = 3000 (cioè 3 secondi, si tratta di millesimi di secondo) Enabled = True (vogliamo che scatti!) A questo punto facciamo doppio click sul controllo Timer appena configurato. Ci troveremo nella finestra del codice associato alla procedura evento tmrUnload_Timer(). Scriviamo il seguente codice: Public Sub tmrUnload_Timer() 'Questa procedura scarica la finestra di benvenuto e passa il controllo alla normale esecuzione del programma Unload frmSplash End Sub L'istruzione Unload frmSplash equivale a comunicare al VB che vogliamo scaricare la finestra frmSplash dalla memoria e quindi vogliamo anche nasconderla. In questo modo, dopo 3 secondi dal caricamento della nostra finestra di benvenuto il Timer scatterà e la scaricherà dalla memoria. Salviamo prima di chiudere Ricordiamoci di salvare il progetto prima di chiuderlo e confermiamo i nomi che abbiamo associato agli oggetti creati sino a qui (Biblio_VBIUG per il progetto, frmSpalsh per la finestra di benvenuto). LA MASCHERA PRINCIPALE DI UN PROGETTO: MENU' E BARRE DEI PULSANTI Applicazioni SDI e MDI Le interfacce utente dei software possono essere raggruppate in due grandi categorie: Single Document Interface (SDI): applicazioni basate su un'unica finestra, che per ogni documento richiedono una nuova istanza di programma (ad es. Notepad). Multiple Document Interface (MDI): applicazioni che gestiscono più documenti all'interno di un'unica istanza (ad es. Microsoft® Word 9x). Per creare applicazioni SDI è sufficiente costruire dall'IDE di Microsoft® Visual Basic® l'interfaccia grafica ed associare ai controlli inseriti nella form delle procedure evento (Lezione 2, Lezione 3 e Lezione 4). Lo sviluppo di applicazioni MDI richiede invece generalmente uno sforzo maggiore per gestire barre dei pulsanti, barre dei menù contestuali al documento visualizzato e gestione degli eventi di caricamento e scaricamento dei documenti. Per questo motivo ci occuperemo in questa lezione dello sviluppo di interfacce MDI, in quanto i concetti appresi saranno applicabili nello stesso modo anche alle applicazioni SDI seppur in un contesto semplificato. Creare una finestra MDI principale I progetti basati su interfaccia MDI richiedono che sia inserita all'interno del progetto una form "speciale" di tipo MDIForm che si comporterà da "contenitore" per tutte le altre form di visualizzazione dei documenti. Per fare questo apriamo il progetto Biblio_VBIUG e dalla finestra di progetto (CTRL+R) premiamo il tasto destro del mouse sul progetto e scegliamo la voce Inserisci -> MDI Form come illustrato in figura. Ogni progetto può contenere una sola finestra di tipo MDI che sarà il contenitore di tutte le altre finestre di documento, che si diranno MDIChild. Tutte le form tradizionali (non MDI) proprio per questo hanno tra le loro proprietà la voce MDIChild che se impostata a True permette di inserirle all'interno della MDIForm contenitore. Per impostare l'aspetto grafico della MDI Form in modo tale che presenti una barra dei pulsanti ed una barra di stato occorre aggiungere al progetto dei riferimenti a controlli esterni. Si prema il tasto destro del mouse sulla casella degli strumenti e si selezioni la voce Componenti o si scelga la voce di menù Progetto -> Componenti. Si cerchi e si selezioni "Microsoft Windows Common Controls 6.0" (MSCOMCTL.OCX). Questo componente OCX fornisce alcuni controlli utili per disegnare interfacce utente con il tipico aspetto delle applicazioni Windows. Premendo il pulsante Chiudi appariranno nella casella degli strumenti dei nuovi controlli (come rappresentato in figura). Quelli evidenziati in rosso sono gli strumenti che servono a noi per realizzare la nostra interfaccia utente. Selezionare e trascinare la Barra Pulsanti sulla MDI Form appena inserita. Fare altrettanto con la Barra di Stato e con la Lista di Immagini. Assegnare i seguenti nomi agli elementi: ToolBar: barra dei pulsanti StatusBar: barra di stato imgList: lista di immagini frmMain: MDIForm Premere il tasto destro del mouse sul controllo imgList e scegliere la voce di menù "Proprietà". Selezionare una dimensione 16x16 per le immagini e visualizzare il pannello "Immagini", dal quale è possibile caricare una lista di file grafici che dovranno corrispondere alle immagini dei nostri pulsanti. Caricare una ad una le immagini dei pulsanti da inserire ed assegnare a ciascuna immagine una Chiave. Usare le seguenti immagini (dal percorso su disco "\Program Files\Microsoft Visual Studio\Common\Graphics\Bitmaps\ OffCtlBr\Large\Color\): NEW.BMP => Chiave: New OPEN.BMP => Chiave: Open SAVE.BMP => Chiave: Save Premere OK per salvare. Premere ora il tasto destro del mouse sulla barra dei pulsanti e selezionare la voce di menù "Proprietà". Assegnare il valore imgList alla proprietà "ImageList". Sarà ora possibile creare i pulsanti della barra bottoni associandovi le immagini precedentemente inserite. Dal pannello "Bottoni" inserire tramite il tasto "Inserisci Bottone" i pulsanti valorizzando le seguenti proprietà: Chiave: è la chiave che useremo per riconoscere i bottoni da codice (usare New, Open e Save rispettivamente). ToolTipText: è il testo di aiuto che verrà visualizzato al passaggio del mouse sui bottoni (opzionale ma utile). Immagine: indica la chiave dell'immagine associata a ciascun bottone (usare le tre chiavi inserite per le immagini nella lista New, Open e Save). A questo punto la nostra barra pulsanti è pronta dal punto di vista grafico. Occorre quindi creare la barra dei menu. Premere il tasto destro del mouse su un'area libera della MDI Form e scegliere la voce "Menù Editor". Dalla finestra che compare caricare le voci di menù necessarie considerando che: Caption: è il testo che verrà visualizzato nella voce di menù. Per avere la prima lettera di un menù sottolineata usare la & (e commerciale) prima della lettera da sottolineare. Per inserire uno spazio (riga) tra diverse voci di menù usare come caption il segno meno (-). Nome: è il nome che verrà utilizzato da codice per riferirsi alla voce Le freccette (vedi figura) servono per gestire la posizione delle voci di menù le une rispetto alle altre. Rimane ora da configurare la barra di stato. Per farlo è sufficiente premere un'ultima volta il tasto destro del mouse sulla barra di stato disegnata nella MDI Form e scegliere "Proprietà". Dal pannello "Pannelli" caricare i pannelli necessari tenendo presente che: Chiave: ci servirà, ancora una volta, per valorizzare da codice i pannelli in termini di contenuto Stile: ci permette di scegliere se il pannello deve visualizzare del testo, lo stato dei pulsanti Ins, CapsLock, BlocNum oppure informazioni su data e ora (Kana è per le tastiere giapponesi, a noi non serve) Allineamento: permette di impostare l'allineamento a destra, sinistra o centrato del testo mostrato AutoSize: permette di indicare alla statusbar quali pannelli si devono dimensionare automaticamente. Di solito si usa per il pannello all'estrema sinistra che contiene le informazioni di stato testuali scegliendo il valore sbrSpring che corrisponde ad occupare tutto lo spazio a disposizione, lasciato quindi libero dagli altri pannelli. Inseriamo 4 pannelli con le seguenti caratteristiche: Chiave: Chiave: Chiave: Chiave: Status - Stile: sbrText - AutoSize: sbrSpring - Allineamento: sinistra Ins - Stile: sbrIns - AutoSize: sbrNoAutoSize - Allineamento: centrato Date - Stile: sbrDate - AutoSize: sbrNoAutoSize - Allineamento: centrato Time - Stile: sbrTime - AutoSize: sbrNoAutoSize - Allineamento: centrato Diamo infine un titolo alla finestra MDI Form tramite la proprietà Caption, usiamo il valore "Archivio Biblioteca - VBIUG". A questo punto abbiamo costruito una finestra principale che si presenterà in modo simile alla figura seguente. Rimane da gestire il codice associato agli eventi che il nostro utente può scatenare usando tale interfaccia. Associazione del codice all'interfaccia Il codice da associare alla nostra interfaccia sarà comune tanto ai menù quanto ai pulsanti. E' allora consigliabile creare delle procedure (Sub o Function) a livello di modulo Form o all'interno di un modulo di codice BAS (Progetto -> Inserisci Modulo) al fine di richiamare le stesse da diversi punti del programma (menù e bottoni per esempio). Inseriamo quindi un modulo BAS e definiamo 4 procedure al suo interno, come segue: Public Sub Main() 'Questa procedura si occupa dell'avviamento del programma frmSplash.Show vbModal frmMain.Show End Sub Public Sub NewFile() 'Questa è la procedura che gestirà la creazione di un nuovo file End Sub Public Sub OpenFile() 'Questa è la procedura che gestirà l'apertura di un file End Sub Public Sub SaveFile() 'Questa è la procedura che gestirà il salvataggio di un file End Sub Public Sub Quit() 'Questa è la procedura che gestisce la chiusura del programma End End Sub Per associare queste procedure ai pulsanti ed alle voci di menù basta selezionare dalla modalità di design le voci di menu. Comparirà il codice di ciascuna voce nel quale si dovrà scrivere il nome della procedura da richiamare. Per i pulsanti si dovrà invece fare doppio click sulla barra dei pulsanti e all'interno del codice della procedura evento che si presenterà(Private Sub ToolBar_ButtonClick) riportare un costrutto Select Case basato sulla chiave del parametro Button che viene passato ByVal alla procedura evento. Il codice della procedura evento della barra pulsanti della nostra MDI Form sarà simile a quanto riportato nella figura seguente. Ultime operazioni prima di chiudere Dalla finestra delle proprietà del nostro progetto configuriamo come oggetto di avvio la procedura Sub Main anziché la form frmSplash. Chiamiamo il modulo di codice BAS creato modMain e salviamolo. Salviamo la MDI Form con nome frmMain. MASCHERE DI SECONDO LIVELLO Form secondarie Nella puntata precedente abbiamo visto come sia possibile realizzare una finestra principale in grado di contenere una serie di finestre secondarie, una barra dei menu ed una barra dei pulsanti. Ora ci occuperemo della creazione delle finestre secondarie da inserire all'interno della form MDIForm principale. Innanzitutto occorre dire che una form secondaria si dice MDIChild, cioè figlia, proprio per indicare che potrà vivere solo all'interno di una form MDIForm principale. Contrariamente alle MDIForm principali che sono diverse dalle form normali, le form MDIChild sono finestre canoniche (form) che hanno la proprietà MDIChild impostata a True. Inseriamo quindi nel nostro progetto Biblio_VBIUG una nuova Form e nella finestra delle proprietà assegnamo alla proprietà MDIChild il valore True. Diamole quindi nome (Name) frmChild e impostiamo il suo titolo (Caption) con il valore "Documento". Al suo interno per ora non carichiamo alcun oggetto. Vedremo nelle prossime lezioni come gestire tramite controlli grafici l'accesso ai dati. Per ora occupiamoci dell'interazione tra la nostra MDIForm e la frmChild. Visualizzare le form MDIChild Per mostrare una form MDIChild è sufficiente richiamare il metodo Show così come accade per le form tradizionali. Proviamo quindi a scrivere nella Sub NewFile() il codice che mostra la nostra finestra MDIChild. Public Sub NewFile() 'Questa è la procedura che gestirà la creazione di un nuovo file frmChild.Show End Sub Proviamo ad eseguire il nostro programma. Qual è il problema che si evidenzia subito ? Se premiamo più volte il tasto "File->Nuovo" la finestra si apre solo la prima volta e non viene creata una nuova finestra per ogni pressione del tasto o scelta della voce di menù corrispondente. Questo è corretto se ci pensiamo perchè noi stiamo chiedendo, ad ogni esecuzione della procedura, di mostrare la frmChild. Se frmChild è già visibile non succederà nulla. Potremmo a questo punto pensare di dover creare N copie della nostra frmChild (ne facciamo 10 ? 20 ? quante ?) per averle pronte da visualizzare, ma questo non sarebbe un buon modo di lavorare, non possiamo vincolare il nostro utente finale ad aprire un numero massimo di finestre Child in funzione di quante secondo noi dovevano essere aperte. Allora quale può essere la soluzione al problema ? Istanze multiple di una form Quando noi creiamo una form all'interno di Visual Basic dall'interfaccia grafica del VB quello che facciamo in realtà è creare un "modello" di form e non una form unica. Se provate a scrivere dentro ad una qualsiasi finestra di codice del nostro programma Biblio_VBIUG un'istruzione come la seguente: Dim objForm As New frmChild vedrete che da VB non avrete alcun errore, anzi potrete fare riferimento a tutte le caratteristiche di frmChild, compresi i controlli eventualmente inseriti al suo interno, tramite la variabile objForm. Attenzione però che quella che utilizzerete non sarà la frmChild che avete disegnato voi, ma una copia identica della stessa. Cosa significa questo ? Significa che le form che noi costruiamo in VB sono "modelli" di form, si chiamano in realtà classi e di queste possiamo crearne diverse copie fra loro uguali come caratteristiche e comportamento (label, textbox, pulsanti, codice delle procedure evento) ma diverse come valore delle singole proprietà (la Caption, la posizione, ecc.). Allora proviamo a scrivere il codice seguente nella procedura NewFile: Public Sub NewFile() 'Questa è la procedura che gestirà la creazione di un nuovo file Dim frmNewChild As frmChild Set frmNewChild = New frmChild nlDocumenti = nlDocumenti + 1 frmNewChild.Caption = "Documento " & nlDocumenti frmNewChild.Show End Sub Ogni volta che la procedura verrà richiamata otterremo la creazione di una nuova copia della frmChild che avrà titolo diverso dalle copie precedenti in virtù della variabile globale (definita come Private nlDocumenti As Long in modMain.BAS). Il risultato sarà simile a quello rappresentato in figura. Ora rimangono ancora alcune questioni da risolvere: 1) come facciamo a distinguere tra loro le finestre MDIChild ? 2) come facciamo a gestire le MDIChild con codice univoco all'interno della MDIForm principale ? Scaricare una Form MDI Child Ciascuna form MDIChild sarà individuabile tramite la proprietà ActiveForm delle MDIForm principale. Nel nostro caso quindi scrivere: MsgBox frmMain.ActiveForm.Caption corrisponderà a visualizzare in una MsgBox il titolo della MDIChild attiva in questo momento. In alternativa ciascuna MDIChild al suo interno potrà fare riferimento a se stessa tramite la parola chiave Me. Infatti se provate ad inserire nella frmChild un CommandButton con il codice seguente: MsgBox frmChild.Caption fornirà un risultato forse inaspettato, ma corretto: avrete sempre il titolo della frmChild così come l'avete definita dall'IDE del VB. Se invece scrivete nel codice della procedura evento click del CommandButton: MsgBox Me.Caption otterete ogni volta il titolo della frmChild corrente. Poter individuare una MDIChild fra tante è fondamentale sia per poter gestire correttamente il diverso contenuto delle MDIChild sia per poterne gestire la corretta chiusura/scaricamento. Pensate di voler aggiungere una voce di menù "Chiudi" che permetta di scaricare la MDIChild corrente. Un primo modo di lavorare è aggiungere una voce di menù alla frmMain e associare ad essa il seguente codice: Unload frmMain.ActiveForm In alternativa potete associare dei menù particolari a ciascuna MDIChild tenendo presente che quando una MDIChild ha un menù associato, questo sovrascriverà il menù della MDIForm principale ogni qualvolta la Child sarà la finestra attiva. Questo significa che in un progetto con una MDIForm e due MDIChild, di cui una senza menù e l'altra con un menù personalizzato, ogni volta che la MDIChild senza menù sarà la MDIChild attiva avremo in alto il menù della MDIForm, mentre quando sarà attiva la MDIChild dotata di menù avremo in alto il menù di questa ultima al posto di quello della MIDForm. Allora per renderci conto di questo inseriamo nella frmChild un menù uguale a quello della frmMain ma con in più la voce "Chiudi" prima di "Esci" e associamole il seguente codice: Private Sub mnuClose_Click() Unload Me End Sub Il risultato sarà proprio quello voluto: ogni volta che sceglieremo il menù "Chiudi" la sola MDIChild corrente si chiuderà. Durante la chiusura di una MDIChild o della MDIForm principale potremmo avere l'esigenza di gestire il salvataggio dei dati contenuti. Pensate a cosa accade quando chiudete Microsoft Word senza aver salvato il documento corrente. Il programma vi chiede se volete salvare o meno i dati, oppure se volete annullare la chiusura del programma. La stessa situazione possiamo gestirla anche noi con il codice delle procedure evento Form_Unload e Form_QueryUnload delle MDIChild. Ogni volta che la MDIForm viene chiusa prima di chiudersi e scaricare con se le MDIChild che contiene succede quanto segue: 1) Tutte le MDIChild vengono "pre-allertate" della chiusura tramite l'evento Form_QueryUnload e se anche una sola di queste imposta l'argomento Cancel = True nel codice della procedura evento, l'intero processo di Unload viene sospeso e tutte le form (MDIForm e MDIChild) rimangono caricate 2) Se nessuna MDIChild ha detto Cancel = True nell'evento Form_QueryUnload allora la MDIForm richiama per ciascuna MDIChild l'evento Form_Unload e scarica la MDIChild. Se anche una sola delle MDIChild imposta Cancel = True a questo punto il processo di Unload si ferma comunque, ma le MDIChild che avevano già ricevuto l'evento Form_Unload senza dire Cancel = True, ormai sono già state scaricate. Per provare questo comportamento nel codice del nostro progetto, che potete scaricare in versione aggiornata, trovate un esempio concreto. Gestire le form MDIChild dalla MDIForm Cosa manca ancora al nostro progetto, dal punto di vista dell'interfaccia utente, che gli altri programmi MDI hanno ? La possibilità di gestire le finestre con il classico menù "Finestre" che permette di posizionare e richiamare le MDIChild. Questa ultima funzionalità è assolutamente banale e si ottiene semplicemente aggiungendo al menù della MDIForm o delle MDIChild (non dimenticate che i menù delle MDIChild vincono su quello della MDIForm !) una voce di menù che abbia l'attributo WindowList selezionato. Questo ci permetterà di avere l'elenco delle MDIChild caricate, separato con una linea dal resto delle voci di quel menù (vedi le figure). Noi dovremo aggiungere a mano le voci di menù per allineare le MDIChild. Il codice da associare alle singole voci di menù si basa sul metodo Arrange proprio di ciascuna MDIForm che permette di gestire l'allineamento delle MDIChild. Per i più curiosi è stata inserita anche una procedura che "scorre" tutte le MDIChild per ridurle ad icona basandosi sulla parola chiave Forms che corrisponde alla collezione di tutte le form caricate (Loaded) nel progetto corrente. Tra queste a noi interessano solo quelle di tipo frmChild (TypeOf objForm Is frmChild). Ecco il codice caricato nella nostra frmChild: Private Sub mnuTileHorizontally_Click() frmMain.Arrange vbTileHorizontal End Sub Private Sub mnuTileVertically_Click() frmMain.Arrange vbTileVertical End Sub Private Sub mnuCascade_Click() frmMain.Arrange vbCascade End Sub Private Sub mnuIconize_Click() Dim objForm As Form For Each objForm In Forms If TypeOf objForm Is frmChild Then objForm.WindowState = vbMinimized End If Next End Sub ACCESSO AI DATI CON OLEDB E I CONTROLLI NATIVI DEL VISUAL BASIC I Controlli Nativi Microsoft Visual Basic è da sempre un linguaggio orientato alla produzione rapida di applicazioni dotate di interfaccia utente. In particolare le applicazioni di accesso ai dati sono tra le più tipiche realizzazioni Visual Basic grazie alla presenza di controlli di accesso ai dati e di visualizzazione degli stessi dall'utilizzo immediato. Tra questi controlli possiamo per esempio considerare: Microsoft Microsoft Microsoft Microsoft ADO Data Control 6.0 DataGrid Control 6.0 DataList Control 6.0 DataRepeater Control 6.0 E' sufficiente quindi da un qualsiasi progetto VBP selezionare tramite la voce di menù Progetto>Componenti i precedenti controlli per poter scrivere (o il più delle volte disegnare con il mouse) le nostre applicazioni di accesso ai dati. Una form MDIChild di accesso ai Dati Iniziamo quindi con il disegnare il nostro database dei libri per la nostra biblioteca. Il database prevederà le seguenti tabelle: tab_Libri tab_Iscritti tab_Prestiti tab_Prenotazioni tab_StoricoPrestiti e pensiamo alla realizzazione di una form MDIChild per consultare la lista dei libri tramite una griglia. Inseriamo nel nostro progetto della biblioteca una form e impostiamo a True la sua proprietà MDIChild. Assegnamo alla form il nome frmLibri e il titolo (caption) "Anagrafica Libri" e salviamola. Aggiungiamo al progetto i controlli: - Microsoft ADO Data Control 6.0 - Microsoft DataGrid Control 6.0 ed inseriamoli nella form MDIChild come nell'immagine seguente. Assegnamo alla DataGrid il nome grdLibri e all'ADO Data Control il nome datLibri. Iniziamo quindi a configurare l'ADO Data Control indicandogli quale sia il database Access 2000 da cui deve leggere i dati e quale tabella vogliamo leggere. Configurazione di ADODC Per fare questo premiamo il tasto destro del mouse sul controllo e scegliamo la voce di menù "Propietà ADODC". Nella finestra di proprietà che viene visualizzata possiamo indicare all'ADODC la stringa di connessione al database in diversi modi, scegliamo "Usa Stringa di Connessione" (la terza opzione) e configuriamo graficamente la connessione alla base dati. Scegliamo come Provider "Microsoft Jet 4.0 OLE DB Provider" per indicare all'ADODC che vogliamo utilizzare il Jet Engine di Access 2000 (4.0), selezioniamo quindi il file di database MDB e abilitiamo il salvataggio delle informazioni di sicurezza, come nell'immagine seguente e - dopo aver eventualmente testato positivamente la connessione - premiamo il tasto "OK". A questo punto spostiamoci nella sezione relativa alla sorgente dati (terzo pannello di configurazione dell'ADODC) e indichiamo che vogliamo accedere ad una tabella di dati (adCmdTable) di nome tab_Libri, come nella seguente immagine. Premiamo OK e salviamo nuovamente la form. Configurazione della Data Grid A questo punto dobbiamo indicare alla Data Grid che il suo contenuto deve essere letto dall'ADODC appena configurato. Ottenere questo è assolutamente banale, è sufficiente selezionare la Data Grid e dalla finestra delle sue proprietà selezioniamo nella proprità DataSource l'ADODC appena configurato, come da immagine. Se a questo punto modifichiamo i menù della nostra MDIForm affinché vi sia una voce per visualizzare la frmLibri potremo osservare che la nostra form è già pronta per l'uso. Volendo potremmo personalizzare l'aspetto delle colonne di dati rappresentate nella griglia entrando nelle proprità della Data Grid o, ancor meglio, selezionando una ad una le colonne di nostre interesse nella tabella di dati. E' infatti completamente inutile recuperare un'intera tabella da un database per poi sfruttarne solo alcune colonne, conviene richiedere direttamente al motore di database le sole colonne di nostro interesse. Data Binding a singolo campo Se volessimo poi rappresentare all'interno di una singola form dati non in forma tabellare (griglia) ma tramite diversi controlli, per esempio TextBox, ComboBox, Option Button, ecc. dovremo considerare le seguenti proprietà di configurazione: DataSource: è il controllo ADODC da cui leggere i dati DataField: è il campo della tabella o selezione dati correntemente ottenuta tramite l'ADODC che si vuole visualizzare nel controllo DataFormat: si tratta di informazioni sulla formattazione a schermo del dato da visualizzare (data, valuta, numero, ecc.) Impostando queste proprietà per ciascuno dei controlli da visualizzare nella form (vedi immagine 8) è possibile costruire delle maschere che visualizzino un singolo record e che si possono poi scorrere tramite i tasti cursore dell'ADODC come nel caso della nostra form di visualizzazione degli utenti iscritti nell'anagrafica della BiblioVBIUG. GESTIRE GLI EVENTI NEI COMPONENTI Cosa sono gli eventi ? I componenti che realizziamo con Microsoft® Visual Basic® ci permettono di esporre dei metodi che le applicazioni client COM utilizzano per demandare ai nostri componenti lo svolgimento di alcuni compiti. Le chiamate a questi metodi sono però chiamate sincrone (vedremo la prossima volta che possiamo fare anche altrimenti) e quindi dal momento in cui chiamiamo il metodo a quando l'elaborazione dello stesso si conclude il nostro client non può fare altro che aspettare. Questo oltre a non essere molto elegante non è nemmeno molto comodo. Se pensiamo alla normale programmazione guidata dagli eventi (Click su un CommandButton), gli oggetti nelle nostre Form possono informarci di cio' che sta accadendo. Queste informazioni, che possono essere pensate come delle notifiche, non sono altro che eventi che gli oggetti da noi utilizzati (in questo esempio il CommandButton) ci inviano quando necessario. Anche i componenti che realizziamo autonomamente possono avere questo comportamento. Per farlo dobbiamo inserire nell'implementazione del nostro componente le informazioni necessarie a far sì che questo possa esporre degli eventi e possa farli scattare. Come si definiscono Per definire un evento in una classe dobbiamo utilizzare la parola chiave Event e precisamente dobbiamo esporre un evento pubblico che sia caratterizzato da un nome e da eventuali parametri, come segue: Public Event Notifica(ByVal sMessaggio As String) Quello appena definito è un evento di nome Notifica che passerà al chiamante (il client COM) un parametro di tipo stringa per valore. Se vogliamo notificare ad un client COM delle informazioni senza la necessità di ricevere dallo stesso delle "risposte" possiamo appoggiarci a costrutti di questo tipo, dove i parametri passati insieme all'evento sono forniti al client ByVal. Se invece vogliamo permettere al client COM di fornirci delle informazioni in risposta all'evento - per esempio vogliamo permettere al chiamante di interrompere un'elaborazione eccessivamente lunga nel tempo - allora possiamo utilizzare parametri ByRef tramite una definizione come quella in figura. A questo punto quando sul client COM scatterà il nostro evento (da noi opportunamente generato) potremo valutare se il client vuole fermare o meno l'elaborazione controllando il valore del parametro passatogli per riferimento. In ogni caso per definire un evento, avendolo quindi a disposizione nell'interfaccia della sua classe, è sufficiente definirlo con la dichiarazione Public Event. Ok così è definito! Ma come faccio a far scattare un evento ? All'interno del codice della nostra classe sarà sufficiente ricorrere all'istruzione RaiseEvent per far scattare un evento. Per esempio nel nostro caso possiamo pensare di inserire nella procedura di elaborazione la seguente riga di codice: RaiseEvent Notifica("Sto lavorando") Questo comporterà dal lato del client lo scattare dell'evento Notifica qualora la variabile oggetto che conterrà l'istanza delle nostra classe sia stata definita ad eventi, come segue: Private WithEvents objMiaClasse As clsMiaClasse Nella figura seguente è illustrato il caso in esame. Nel caso degli eventi che passano parametri per riferimento, dal codice del client basterà cambiare tali valori per far sì che il componente COM possa rendersene conto e possa agire di conseguenza. Per esempio per bFerma = True potremmmo decidere di eseguire l'istruzione Exit Do per terminare un ciclo di elaborazione. SVILUPPO DI COMPONENTI COM ASINCRONI Il concetto di asincronicità Per componente asincrono si intende un componente che sia in grado di lavorare mentre anche il chiamante (client COM) può svolgere delle altre attività. Pensiamo a Microsoft Word ed al correttore ortografico automatico. Mentre noi digitiamo i nostri testi il correttore ortografico controlla la correttezza delle informazioni da noi digitate, pero' questo non comporta per ogni parola l'interruzione della nostra attività di battitura testo. Oppure pensiamo al controllo WebBrowser che è in grado di scaricare una pagina web - mentre noi da codice facciamo altro - e ci avvisa con un evento della conclusione dello scaricamento delle pagina web richiesta. L'idea alla base dei componenti asincroni In Microsoft® Visual Basic® non esiste alcun attributo da impostare per far sì che un componente sia asincrono. Per ottenere questo risultato dobbiamo lavorare via codice ed appoggiarci a qualche strumento fornito internamente al VB. L'idea alla base dei metodi asincroni nei componenti è quella di far tornare il più velocemente possibile il controllo al client COM e poi eseguire il codice della procedura autonomamente, notificando la conclusione dei lavori al client con un normale evento VB. Per ottenere questo risultato dobbiamo appoggiarci ai timer in modo tale che possiamo dire al sistema: appena viene chiamato il metodo attiva un timer e restituisci il controllo al chiamante; poi allo scattare del timer verrà eseguito il vero lavoro del metodo, il quale alla sua conclusione scatenerà un evento di notifica. In VB, vedremo meglio poi, conviene appoggiarsi a finestre di codice che contengono l'oggetto timer. Un metodo asincrono in pratica Se vogliamo concretizzare queste idee con del codice possiamo pensare ad una classe simile alla seguente: Private frmTimer As FormTimer Public Event NotificaEsecuzione(ByVal sStatus As String) Public Sub EseguiAsync([Parametri]) Set frmTimer = New FormTimer frmTimer.tmrAsync.Enabled = True frmTimer.tmrAsync.Interval = 100 End Sub In questa procedura si fa riferimento ad una classe FormTimer che dovrà corrispondere ad una form definita nel nostro progetto e che conterrà un controllo timer di nome tmrAsync. Il codice della form sarà simile al seguente: Private Sub tmrAsync_Timer() tmrAsync.Enabled = False EseguiLavoroVero([Parametri]) End Sub La procedura EseguiLavoroVero potrà essere collocata in un modulo di codice .BAS oppure se vogliamo che scateni degli eventi dovrà essere nella classe che ha attivato il timer. In questo secondo caso dovremo passare alla form un riferimento alla classe che l'ha chiamata e lavorare con una procedura di CallBack. Nel secondo caso la procedura - definita nella classe - avrà un aspetto simile al seguente: Public Sub EseguiLavoroVero([Parametri]) Set frmTimer = Nothing [... codice da eseguire ...] RaiseEvent NotificaEsecuzione("Ho finito !") End Sub La form dovrà presentare una proprietà assegnabile per impostare un riferimento alla classe chiamante e quindi dovrà poi richiamare la procedura che svolge il lavoro non per nome ma come metodo della classe chiamante, nella forma RiferimentoAllaClasse.EseguiLavoroVero([Parametri]). Considerazioni sui Timer Ritengo importante notare che nel caso precedentemente esposto dobbiamo appoggiarci al controllo Timer del VB. Utilizzando le API di Windows si posso sfruttare i metodi SetTimer e KillTimer. La soluzione delle API di sistema ci permetterebbe di evitare di avere delle form all'interno del nostro componente. Tuttavia la funzione SetTimer richiede l'indirizzo di una funzione di callback che deve risiedere in un modulo .BAS . Questo comporta qualche problema nel caso in cui i nostri oggetti siano all'interno di un componente COM che utilizza un Poll di Thread perché potremmo trovarci a condividere variabili a livello di modulo senza volerlo. Quindi, benché sembri più elegante l'utilizzo dei timer tramite API di sistema, conviene appoggiarsi ai controlli timer delle form. ACCESSO AI DATI DA CODICE TRAMITE ACTIVEX DATA OBJECTS Universal Data Access ActiveX Data Objects (da qui in poi ADO) sarà lo strumento che useremo in Microsoft® Visual Basic® 6.0 per accedere le basi di dati. Alla base di questo strumento c'è il concetto che qualsiasi insieme di informazioni (relazionali o meno) può e deve essere acceduto nello stesso identico modo. Questa idea ha dato vita a quello che si chiama Universal Data Access (http://www.microsoft.com/data/default.htm) e che si basa appunto sull'idea che sia possibile avere un unico strumento per accedere qualsiasi tipo di informazione. Per ottenere questo risultato sono stati create dei livelli software che ci separano dall'accesso ai dati diretto e che sono rappresentati nella Figura seguente. In sostanza vi sono tre livelli software che sono: Data Providers: a questo livello si trovano gli insiemi di informazioni, relazionali o meno (SQL Server, Oracle, Exchange, Active Directory, ecc.). Service Components: a questo livello invece troviamo gli strumenti software che virtualizzano l'accesso ai dati preoccupandosi di creare dei cursori per conto nostro sulle basi di dati che vogliamo interrogare. Si basano su OLEDB e forniscono funzionalità di ricerca e raggruppamento di dati. Data Consumers: a questo ultimo livello ci siamo noi con le nostre applicazioni che utilizzano ADO per comunicare con i Service Components, per arrivare quindi ai Data Providers parlando un'unica lingua. In questa lezione ci occuperemo delle regole di utilizzo di ADO per colloquiare correttamente con i Service Components e quindi avere accesso ai dati, tra loro anche diversi, parlando un'unica lingua. Le regole che vedremo saranno applicabili a data base Access, SQL Server, Oracle, ecc. con la sola differenza che dovremo, in ciascuno dei casi, comunicare ad ADO quale sia il Data Provider con il quale vogliamo parlare. Modello ad Oggetti ADO ADO nella sua implementazione più semplice, escluse le classi più recenti (dalla versione 2.5 in poi), si basa su tre classi fondamentali: ADODB.Connection: gli oggetti di tipo Connection sono quelli che ci permettono di accedere ad un Data Provider piuttosto che ad un altro. Sono gli oggetti Connection che devono sapere il nome (OLEDB Provider) del Data Provider che vogliamo utilizzare. ADODB.Recordset: gli oggetti di tipo Recordset sono invece insiemi di righe, tabelle quindi, che ci forniscono la strada di accesso ai singoli record negli insiemi di dati che stiamo accedendo. ADODB.Command: gli oggetti di tipo Command infine sono degli strumenti molto comodi per svolgere delle interrogazioni, eseguire delle stringhe di comando SQL e delle chiamate a stored procedure e query definite all'interno dei nostri insiemi di dati. L'aspetto interessante del modello ad oggetti di ADO è che ciascuna classe può vivere indipendentemente dalle altre. Per chi tra voi ha già utilizzati altri modelli ad oggetti per l'accesso ai dati questo risulterà evidente nel momento in cui andremo ad utilizzare questo strumento concretamente. In Data Access Object (DAO) o in Remote Data Objects (RDO) eravamo costretti ad istanziare ed utilizzare in un ben preciso ordine gli oggetti a nostra disposizione. Con ADO potremo creare oggetti di tipo Recordset senza aver creato precedentemente degli oggetti di tipo Connection verso la basi di dati che vorremo utilizzare. Vediamo quindi come utilizzare questo strumento. ADODB.Connection Si tratta della classe che ci permette di aprire delle connessioni verso le basi di dati che vogliamo utilizzare. Tra le sue proprietà principali troviamo: ConnectionString: identifica la stringa di connessione verso l'insieme di dati che si vuole accedere. Può essere costruita utilizzando dei files UDL (Microsoft Data Link) oppure fornendone esplicitamente il valore come stringa. Provider: corrisponde al nome dell'OLEDB Provider che si vuole utilizzare per accedere all'insieme di dati. Alcuni esempi sono: Microsoft.Jet.OLEDB.4.0 - SQLOLEDB.1 MSDAORA.1 rispettivamente per Access 2000, SQL Server, Oracle. Ve ne sono molti altri. I metodi significativi invece sono: Open: apre una connessione precedentemente fornita nella proprietà ConnectionString oppure apre una ConnectionString fornita come argomento al metodo stesso (si veda l'esempio più avanti nella lezione). Durante la chiamata al metodo Open è possibile fornire oltre alla stringa di connessione le credenziali per l'autenticazione dell'utente (userid e password). Close: questo metodo conclude una connessione precedentemente aperta verso una base dati. Execute: esegue un blocco di istruzioni SQL. Tramite questo metodo è possibile avere in risposta un insieme di record (un Recordset quindi) da utilizzare per scorrere il risultato della interrogazione/istruzione SQL. BeginTrans: avvia una transazione sulla connessione correntemente attiva. CommitTrans: chiude e conferma le attività svolte durante una transazione. RollbackTrans: chiude e annulla le attività svolte durante una transazione. Ecco un esempio di codice Microsoft® Visual Basic® 6.0 che utilizza l'oggetto Connection per aprire una connessione verso un data base Access ed esegue un aggiornamento sulla tabella degli Utenti: 'Dimensiono ed istanzio le variabili di accesso ai dati Dim cnAccess As ADODB.Connection Set cnAccess = New ADODB.Connection 'Apro la connessione verso Access (attenzione alle 4 virgolette sulla 'password che corrispondono a 2 virgolette nella stringa) cnAccess.Open "Provider=Microsoft.Jet.OLEDB.4.0;Password="""";" & _ "Data Source=Database.mdb;Persist Security Info=True" 'Aggiorno con un'istruzione SQL dei record nella tabella Utenti cnAccess.Execute "UPDATE Utenti SET Attivo = -1 WHERE Attivo = 0" 'Chiudo la connessione cnAccess.Close 'Scarico dalla memoria gli oggetti Set cnAccess = Nothing Oppure per accedere un data base SQL Server possiamo utilizzare il seguente codice: Dim cnSQLServer As ADODB.Connection Set cnSQLServer = New ADODB.Connection 'Qui vi è l'unica DIFFERENZA con il codice precedente: 'la stringa di connessione ! cnSQLServer.Open "Provider=SQLOLEDB.1;Password="""";" & _ "Persist Security Info=True;User ID=sa;Initial Catalog=Utenti;" & _ "Data Source=(local)" cnSQLServer.Execute "UPDATE Utenti SET Attivo = -1 WHERE Attivo = 0" cnSQLServer.Close Set cnAccess = Nothing Già tramite questi due esempi è possibile notare che, a parte la stringa di connessione, il nostro comportamento nei confronti di Access o di SQL Server è rimasto analogo. Questa considerazione, con basi di dati dotate di strutture normali, è così vera che potremo spostare i nostri insiemi di informazioni tra motori diversi preoccupandoci solo di aggiornare le stringhe di connessione OLEDB da utilizzare, senza dover rivedere ogni volta il nostro codice sorgente quindi, ma agendo solo su un parametro (o su di un file UDL) che contiene la stringa di connessione da noi scelta. ADODB.Recordset Gli oggetti di tipo Recordset sono insiemi di record che ci forniscono il vero e proprio accesso alle informazioni nel data base. Tramite gli oggetti Recordset possiamo accedere i singoli record di una tabella di dati, sfogliandoli e modificandoli, piuttosto che aggiungendone di nuovi. Vedremo nel corso della lezione e del tutorial in generale che non si deve assumere per questo motivo che l'oggetto Recordset sia sempre lo strumento più corretto da utilizzare per accedere i dati in lettura/scrittura. In particolare grazie alle ultime versioni dei motori di data base e di ADO conviene infatti utilizzare altri strumenti per l'accesso in scrittura sui data base, limitando l'uso degli oggetti di tipo Recordset alla visualizzazione ed allo scambio di dati. Le proprietà fondamentali degli oggetti di tipo Recordset sono: CursorLocation: indica al Recordset dove deve risiedere il cursore, cioè dove devono stare i record mentre vengono sfogliati. I valori possibili sono adUseClient (stanno nella memoria del client) o adUseServer (stanno sul server). Un recordset lato client impiega più tempo ad arrivare sul client (perchè deve essere trasferito per intero dal server al client) ma quando è stato trasferito completamente allegerisce il server in quanto non richiede più ulteriori attività sul server stesso. Eventualmente sarà necessario fare degli aggiornamenti asincroni (UpdateBatch) sul server una volta che si sia finito di utilizzare i dati sul client. LockType: indica il tipo di lock cioè di bloccaggio sui record quando si accedono i dati in lettura/scrittura. Le configurazioni possibili sono adLockReadOnly (sola lettura), adLockPessimistic (il record o la pagina di record viene bloccata non appena si accede un qualsiasi campo in modifica), adLockOptimistic (il record o la pagina di record vengono bloccati solo al momento del salvataggio delle modifiche, potrebbero esservi dei conflitti se più utenti hanno iniziato a modificare i dati), adLockBatchOptimistic (si usa nel caso di cursori disconnessi lato client ... ne parleremo più avanti nel tutorial). CursorType: permette di decidere che tipo di notifiche vogliamo avere dall'esterno sulle modifiche apportate ai nostri dati così come ci permette di stabilire in quali direzioni vogliamo sfogliare i dati (si veda la tabella seguente per maggiori dettagli). I valori possibili sono: -adOpenForwardOnly: permette di scorrere i dati solo in avanti un record alla volta, dal primo all'ultimo e non vede le modifiche apportate dall'esterno ai record che scorre. -adOpenKeyset: permette di scorrere i dati in avanti ed indietro e vede le modifiche apportate dall'esterno ai soli record che scorre (non si accorge se ne vengono aggiunti o modificati altri). -adOpenDynamic: permette di scorrere avanti ed indietro i record e si accorge di qualsiasi tipo di modifica sui record. -adOpenStatic: si tratta di una copia dei dati presenti sul server che si può scorrere in qualsiasi direzione ma che non si accorgerà mai di modifiche apportate sul server ai dati che contiene. Direzione Modifiche Esterne adOpenForwardOnly Solo Avanti Non le vede adOpenKeyset Avanti/Indietro Solo sui propri record adOpenDynamic Avanti/Indietro Su tutti i record adOpenStatic Avanti/Indietro Non le vede AbsolutePosition: indica la posizione corrente, cioè il numero del record corrente. RecordCount: indica il numero complessivo di record (vale solo in alcuni casi). ActiveConnection: identifica un riferimento all'oggetto Connection utilizzato dal Recordset per ottenere i dati. Sort: permette di ordinare i dati contenuti nel Recordset. Filter: permette di filtrare i dati contenuti nel Recordset. BOF: ci informa del fatto che siamo arrivati all'inizio del Recordset e non ci sono più record da scorrere qualora dovessimo ancora andare indietro nel Recordset (vale True/False). EOF: ci informa del fatto che siamo arrivati alla fine del Recordset e non ci sono più record da scorrere qualora dovessimo ancora andare avanti nel Recordset (vale True/False). Fields: si tratta di una proprietà di tipo collezione che fornisce accesso ai singoli campi del record corrente. E' la proprietà di default di un Recordset. Per accedere il campo [Nome] del record corrente di un Recordset di nome rsData si puo' quindi scrivere: rsData.Fields("Nome").Value oppure in modo equivalente si può scrivere: rsData("Nome") I metodi significativi invece sono: Open: vuole un massimo di 5 argomenti che sono la sorgente da aprire (una stringa SQL, un oggetto Command, una tabella, ecc.), la connessione da utilizzare (può essere un oggetto Connection o una ConnectionString scritta esplicitamente, oppure nulla), il tipo di cursore (CursorType) ed il tipo di lock (LockType) ed infine delle opzioni. Sono tutti argomenti opzionali che dobbiamo fornire o meno a seconda di come stiamo utilizzando l'oggetto Recordset. Close: chiude un Recordset precedentemente aperto. AddNew: aggiunge un nuovo record al Recordset, qualora la modalità di accesso lo permetta. Delete: cancella un record dal Recordset, qualora la modalità di accesso lo permetta. Update: aggiorna un record modificato, qualora la modalità di accesso lo permetta. Si verifica in automatico quando si modifica un record e poi ci si sposta su di un altro. MoveFirst: si sposta in testa al Recordset (appena dopo BOF). MoveLast: si sposta in fondo al Recordset (appena prima di EOF). MoveNext: si sposta avanti di un record nel Recordset. MovePrevious: si sposta indietro di un record nel Recordset. Ecco un esempio di codice che apre un Recordset ed aggiorna tutti i record al suo interno, quindi aggiunge un nuovo record e cancella il primo record. 'Dimensiono ed istanzio le variabili oggetto di accesso ai dati Dim cnSQLServer As ADODB.Connection Dim rsData As ADODB.Recordset Set cnSQLServer = New ADODB.Connection Set rsData = New ADODB.Recordset 'Apro la connessione verso il motore di data base cnSQLServer.Open "Provider=SQLOLEDB.1;Password="""";" & _ "Persist Security Info=True;User ID=sa;Initial Catalog=Utenti;" & _ "Data Source=(local)" 'Richiedo al server la lista di tutti gli utenti Set rsData = cnSQLServer.Execute("SELECT * FROM Utenti") 'Scorro con un ciclo tutti gli utenti 'finchè non arrivo ad EOF = True, cioè alla fine Do While Not rsData.EOF 'ed imposto il campo Attivo a -1 rsData("Attivo") = -1 rsData.MoveNext Loop 'Aggiungo un nuovo record e lo valorizzo rsData.AddNew rsData("Nome") = "Paolo" rsData("Attivo") = -1 rsData.Update 'Mi sposto in testa alla tabella e cancello il primo record rsData.MoveFirst rsData.Delete adAffectCurrent 'Chiudo il Recordset e la Connection rsData.Close cnSQLServer.Close 'Scarico completamente dalla memoria gli 'oggetti Recordset e Connection Set rsData = Nothing Set cnAccess = Nothing ADODB.Command Gli oggetti di tipo Command permettono di richiamare delle stored procedure, piuttosto che di eseguire delle istruzioni o dei comandi SQL, eventualmente che restituiscono un insieme di righe (Recordset) che saranno eseguiti dal server di dati. Le proprietà fondamentali sono: ActiveConnection: indica un riferimento all'oggetto Connection tramite il quale si vuole eseguire il comando. Può essere un oggetto di tipo Connection oppure una Connection String scritta esplicitamente. CommandText: stringa SQL contenente le istruzioni da eseguire o il nome della stored procedure che si vuole mandare in esecuzione. CommandType: comunica al server il tipo di istruzione SQL che si sta richiedendo di eseguite (adCmdStoredProc=stored procedure; adCmdText=stringa SQL; ecc.). CommandTimeout: indica un tempo massimo di attesa per l'esecuzione della query. Prepared: informa il motore di data base, qualora sia in grado di recepire l'informazione, che il piano di esecuzione della stringa SQL deve essere mantenuto in memoria per successive esecuzioni. Parameters: identifica la collezione dei parametri di una stored procedure o di una stringa SQL parametrica. I metodi interessanti sono: Execute: esegue il codice SQL richiesto ed eventualmente restituisce un Recordset come valore di ritorno. CreateParameter: crea un oggetto di tipo Parametro che è possibile aggiungere alla collezione dei parametri Parameters.Append [...] . Ecco un esempio di codice SQL che apre una connessione verso SQL server ed esegue una stringa SQL tramite un Command. 'Dimensiono ed istanzio le variabili oggetto di accesso ai dati Dim cnSQLServer As ADODB.Connection Dim cmdData As ADODB.Command Dim rsData As ADODB.Recordset Set cnSQLServer = New ADODB.Connection Set cmdData = New ADODB.Command Set rsData = New ADODB.Recordset 'Apro la connessione verso il motore di data base cnSQLServer.Open "Provider=SQLOLEDB.1;Password="""";" & _ "Persist Security Info=True;User ID=sa;Initial Catalog=Utenti;" & _ "Data Source=(local)" 'Configuro il Command e richiedo al 'server la lista di tutti gli utenti With cmdData Set .ActiveConnection = cnSQLServer .CommandText = "SELECT * FROM Utenti" .CommandType = adCmdText .Prepared = True Set rsData = .Execute End With '***************************************** 'Qui potrei scorrere i dati ... come prima '***************************************** 'Chiudo il Recordset e la Connection rsData.Close cnSQLServer.Close 'Scarico completamente dalla memoria gli 'oggetti Recordset, Command e Connection Set rsData = Nothing Set cmdData = Nothing Set cnAccess = Nothing Regole di sopravvivenza Quando si lavora con ADO e si accedono basi di dati conviene tenere presenti alcune regole: E' possibili utilizzare il pool di connessioni fornito da OLEDB ed è quindi inutile mantenere connessioni aperte durante tutta l'esecuzione del programma. Conviene aprire e chiudere le connessioni (mordi e fuggi) man mano servono. I parametri di una stored procedure possono essere forniti alla stessa tramite il metodo Refresh della collezione Parameters di un oggetto Command, ma è molto più efficiente creare a mano (CreateParameter) i parametri e poi aggiungerli (Parameters.Append) alla collezione dei parametri. I Recordset con cursori adOpenDynamic sono comodi ma sono molto dispendiosi. I Recordset non dovrebbero essere utilizzati per fare aggiornamenti massicci sui dati. Conviene invece creare delle stored procedure che saranno decisamente migliori in termini di rapidità ed efficienza di esecuzione. I Recordset dovrebbero essere utilizzati per scorrere i dati e passarli tra diversi livelli software (magari come recordset disconnessi XML ... ne parleremo ...) e non per aggiornamenti massivi sui dati. Per maggiori informazioni vedere: http://www.visualbasic.it