Sei sulla pagina 1di 143

DELPHI: indice delle lezioni

A cura di Carlo Marona

Ambiente di sviluppo della Borland per la scrittura di applicazioni per Microsoft


Windows. Semplice da utilizzare, intuitivo nella progettazione, efficace anche per il
Web. Con Delphi è possibile costruire un programma per Windows senza conoscere le
logiche di base della programmazione classica.

Introduzione al linguaggio Delphi

1. Storia
Le principali tappe del Delphi. Dalle prime formulazioni del Pascal al rilascio del Borland Pascal fino
al Delphi 1.0

2. Le versioni di Delphi
Lo sviluppo di Delphi ha portato ad una diversificazione delle versioni a seconda degli ambienti di
utilizzo. Delphi è oggi disponibile in 3 differenti versioni.

3. Pascal, Delphi, Linux


Il Pascal, padre di Delphi, è un linguaggio progettato per sviluppare programmi multipiattaforma.
Ecco come Delphi può essere utilizzato sotto Linux.

4. Panoramica sul linguaggio


Le basi della programmazione in Delphi: i file, i package, i progetti, il compilatore. Hello Wolrd!

L'ambiente di sviluppo integrato (IDE)

5. Panoramica sull'IDE
L'Integrated Development Environment è il cuore della progettazione in Delphi. Iniziamo a
conoscere l'ambiente e i suoi strumenti.

6. L'Object Inspector
Lo strumento principale per programmare in Delphi. Conoscere a fondo l'Object Inspector è il primo
passo per iniziare a imparare Delphi.

7. Il Form designer
Con Delphi è possibile creare applicazioni grafiche per Windows. Il Form Designer si occupa di
disegnarle e renderle visualizzabili dall'utente.

8. Il Code Editor (parte prima)


L'editor del codice Delphi. Conoscere l'ambiente in cui viene scritto il codice di programmazione
permette di avere un maggiore controllo sull'applicazione da sviluppare.

9. Il Code Editor (parte seconda)


Approfondiamo la conoscenza dell'editor del codice Delphi. Impariamo le principali componenti e le
funzionalità più utili.

10. Il Project Manager


Nel Project Manager avremo a disposizione tutti i file del nostro primo progetto in Delphi e
attraverso di esso avremo il pieno controllo dei nostri file.

Introduzione all'Object Pascal

11. Principi di struttura del codice


Iniziamo ad esaminare da vicino il codice Delphi: le Unit.

12. La sintassi (parte prima)


I principi di sintassi del codice Delphi: identificatori, numerali, commenti e primi operatori.

13. La sintassi (parte seconda)

http://programmazione.html.it/delphi/index.html (1 di 3) [21/05/2002 11.16.21]


DELPHI: indice delle lezioni

I principi di sintassi del codice Delphi: operatori booleani, operatori relazionali, regole di precedenza
degli operatori.

14. I tipi (parte prima)


Introduzione ai tipi di dati: analisi dei tipi Simple.

15. I tipi (parte seconda)


Si continua ad esaminare i tipi di dati: i tipi Real, i tipi String e i tipi strutturati.

16. I tipi (parte terza)


Conclusione della rassegna dei tipi di dati: Puntatori e tipi pointer, tipi procedurali, tipi variant.

17. Le variabili
L'utilizzo delle variabili in Delphi e la loro natura.

18. Le costanti
Le costanti, come in ogni linguaggio di programmazione, permettono di associare un valore a
determinate stringhe.

19. Procedure e Funzioni


Le procedure e le funzioni permettono di riutilizzare il medesimo codice in zone diverse del
programma.

20. Gestire il flusso: le istruzioni condizionali


Gestiamo il flusso di esecuzione del codice attraverso alcune istruzioni condizionali (If...then)

21. Gestire il flusso: le istruzioni cicliche


Gestiamo il flusso di esecuzione del codice attraverso le istruzioni cicliche (For, While, Repeat)

22. Classi ed Oggetti: introduzione


Introduzione alle classi ed alle loro istanze: gli oggetti

23. Classi ed Oggetti: i metodi


Come funzionano e come vengono interpretati i metodi delle classi

24. Classi ed Oggetti: le proprietà


Come funzionano e come vengono interpretate le proprietà delle classi

25. Classi ed Oggetti: gli specificatori


Gli specificatori sono parametri che permettono di condividere lo stesso metodo con più proprietà

26. Gestione degli errori: Eccezioni


La gestione degli errori in Delphi: le istruzioni Try..Except..End, Try..Finally..End, Raise)

Sviluppo di Applicazioni con Delphi

27. La VCL (Visual Component Library)


La libreria di componenti messaci a disposizione da Delphi e con la quale iniziare a progettare le
nostre applicazioni

28. Le interfacce grafiche!


Iniziamo a costruire una nostra interfaccia grafica con gli strumenti della palette di Delphi

29. Approfondimento sulle Forms


Approfondiamo le forms: le proprietà, i metodi e gli eventi più interessanti.

30. Componenti standard


I componenti di Delphi: una visione generale.

31. Applicazioni MDI (parte prima)


Le Multiple Document Interface: organizzare più finestre in un programma

32. Applicazioni MDI (parte seconda)


Le Multiple Document Interface: aprire e gestire i file

33. Sviluppo di applicazioni Multithread


La gestione dei Thread in Delphi: la classe TThread

http://programmazione.html.it/delphi/index.html (2 di 3) [21/05/2002 11.16.21]


DELPHI: indice delle lezioni

34. Liste ed oggetti contenitore


La gestione degli insieme dei valori: le classi Tlist, TStack e TQueue.

Delphi ed i Databases

35. BDE (Borland Database Engine)


Introduzione al Borland Database Engine: Delphi e i Database

36. Componenti per l'accesso ai databases


Tutti i componenti messi a disposizione da Delphi per l'accesso ai database

37. Componenti per la manipolazione dei dati (Data-Aware)


Come utilizzare i componenti data-aware messici a disposizione da Delphi per visualizzare,
aggiungere, modificare, cancellare dati dai databases a cui ci siamo connessi tramite gli oggetti visti
nella sezione precedente

38. I Reports
Come utilizzare i componenti messici a disposizione da Delphi per creare dei reports, ovvero
ogranizzare e stampare il contenuto dei dataset

Creazione di Componenti

39. Modifica di componenti esistenti e creazione di nuovi


I componenti messici a disposizione da Delphi non sono sufficienti e vogliamo modificare il
comportamento di un componente già esistente? Con Delphi possiamo creare nuovi componenti o
modificare quelli già esistenti

DLL e Packages

40. DLL: cosa sono, a cosa servono, come crearle e utilizzarle

Come in molti altri linguaggi di programmazione, anche in Delphi è possibile creare ed utilizzare
DLL. Vediamo come si creano e si usano in Delphi librerie create con Delphi o con altri linguaggi

41. I Packages: cosa sono, a cosa servono, come crearli e


utilizzarli
Oltre alle librerie a caricamento dinamico standard, DLL, Delphi mette a disposizione altre librerie a
caricamento dinamico: i Packages

Delphi ed il Web

42. Delphi e il World Wide Web


Non tutti sanno con Delphi è possibile realizzare anche applicazioni per il web, interfacce per ASP, il
tutto in maniera semplice

Risorse

43. Risorse utili e bibliografia


Un elenco di Link utili per chi volesse approfondire gli argomenti trattati in questa guida. Bibliografia
essenziale.

http://programmazione.html.it/delphi/index.html (3 di 3) [21/05/2002 11.16.21]


DELPHI: La storia

LEZIONE 1: La storia

Il Delphi nasce come evoluzione del "Borland Turbo Pascal". Il Pascal è un


linguaggio ad alto livello che è stato sviluppato alla fine degli anni sessanta dal
professor Niklaus Wirth a Zurigo. Il suo scopo era quello di realizzare un linguaggio
comprendente un piccolo numero di concetti fondamentali di programmazione
sufficienti ad insegnarla in forma di disciplina logica e sistematica; nello stesso tempo
voleva un linguaggio che fosse facile ed efficiente da implementare sulla maggior
parte dei calcolatori. Il suo successo nel conseguire questo obiettivo può essere
misurato dalla rapida diffusione dell'uso del PASCAL sia come linguaggio usato nella
didattica dei principi di programmazione (si usa tutt'oggi nelle università), sia come
linguaggio effettivo per scrivere software di base ed applicativo.

Cosa si intende per linguaggio ad alto livello? Un linguaggio ad alto livello è un


linguaggio che tende con i suoi costrutti, la sua sintassi e grammatica di avvicinarsi il
più possibile al linguaggio ed al ragionamento umano. In contrapposizione ai
linguaggi ad alto livello, ci sono i linguaggi a basso livello come l'Assembler ed affini
che rimangono molto più vicini al linguaggio macchina utilizzato dal calcolatore.
Queste macchine infatti non comprendono il linguaggio dell'uomo direttamente,
sanno solamente lavorare con operazioni matematiche di base (addizione,
sottrazione, operazioni logiche implementate da circuiti elettronici) su sequenze più o
meno lunghe di 0 o di 1. Rimanendo molto scomodo lavorare con sequenze di
questo genere, i linguaggi di programmazione tendono ad rendere il lavoro del
programmatore più semplice astraendo da quello con cui effettivamente il calcolatore
andrà a lavorare. L'Assembler è già un linguaggio ad alto livello rispetto al linguaggio
macchina puro, ma è un linguaggio a basso livello rispetto ad altri linguaggi come il
Pascal che lavora su un livello di astrazione superiore.

L'evoluzione del Pascal negli anni successivi al suo sviluppo, lo ha portato alla
definizione della versione 7.0 (includente la v. 2.0 del Turbo Vision) completamente
orientata agli oggetti che a tutt'oggi viene ancora utilizzato per la realizzazione di
software per DOS. Con l'avvento di Windows la Borland rilasciò il Borland Pascal
pacchetto basato sul linguaggio Object Pascal, naturale evoluzione del Turbo Pascal.
La stesura di applicazioni in ambiente Windows rimaneva comunque alquanto lenta e
complessa; era necessario conoscere molto bene la programmazione in Object
Pascal e le immancabili API di Windows. Fu così che la Borland rilasciò la prima
versione del Delphi, la 1.0 appunto. Questa versione di Delphi produceva
applicazioni per Windows a 16-bit. Successivamente venne rilasciata la versione 2.0
completamente rivista ed ottimizzata per lo sviluppo di applicativi in ambiente 32-bit.
Siamo all'incirca nel 1996 e da qui il Delphi si è evoluto ad una velocità
impressionante, quasi una versione all'anno. Ad oggi (2001) la versione più recente
del Delphi è la 5.0 e si vocifera che per la seconda metà dell'anno sarà disponibile la
versione 6.0.

Con il Delphi la Borland ha introdotto il concetto di sviluppo Visuale RAD (Rapid


Application Development) orientato alla gestione degli eventi come nel Visual Basic,
senza perdere però i vantaggi della compilazione Pascal. È da notare che il codice
scritto in Delphi è conforme all'Object Pascal di cui ha anche ereditato il numero di
versione; la versione 8.0 per Delphi 1.0, la 9.0 per Delphi 2.0 etc. Ciò è confermato
dal fatto che è possibile compilare vecchie applicazioni in Object Pascal tramite il
compilatore Delphi.

Da notare che, come l'Object Pascal il Delphi è un linguaggio GP (General Purpose)


ovvero un tool di sviluppo che permette di realizzare applicazioni di qualsiasi tipo, più
o meno complesse e tutto questo con una semplicità estrema. Ho voluto fare questa
precisazione perché mi è capitato molte volte di parlare con persone "ignoranti" (nel
senso che ignorano ;-) !!) che bistrattano questo linguaggio solamente perché magari
non ne hanno sentito parlare come il Visual Basic di Microsoft e quindi lo ritengono
un linguaggio "giocattolo".

http://programmazione.html.it/delphi/delphi_01.htm (1 di 2) [21/05/2002 11.16.27]


DELPHI: Versioni di Delphi

LEZIONE 2: Versioni di Delphi

Abbiamo già visto nell'Introduzione storica le prime due versioni di Delphi: la 1.0 e la
2.0. Abbiamo già detto le principali differenze tra queste due versioni ovvero la prima
lavora a 16-bit e la seconda a 32-bit. Dalla 2.0 in poi tutte le versioni di Delphi
lavorano a 32-bit.

Fin dalla prima versione di Delphi per ognuna di queste sono disponibili più edizioni;
in Delphi 1.0 queste erano due e si differenziavano per il supporto allo sviluppo
Client/Server. Dalla versione 2.0 è possibile scegliere tra tre edizioni differenti "Delphi
2.0 Standard", "Delphi 2.0 Developer" e la "Delphi 2.0 Client/Server". Dalla
versione 4.0 queste tre edizioni hanno preso i nomi rispettivamente di "Standard",
"Professional", "Enterprise".

Le principali differenze tra queste edizioni stanno nella possibilità o no di gestire


database, di disporre del supporto per internet, di lavorare con database server con
drivers nativi (SQL Server, Oracle, Sybase, Interbase), di incorporare la gestione del
multi-language o internazionalizzazione del software e nella disponibilità di tool di
sviluppo aggiuntivi quali ad esempio l'SQL Monitor e il supporto per la creazione
visuale di query SQL, il supporto per tecnologie CORBA, MIDAS, e COM.

La versione 3.0 introduce una novità interessante non presente nelle versioni
precedenti: il supporto per i packages. Al pari delle librerie DLL permettono di
raggruppare del codice e permetterne il caricamento solo quando se ne ha la
necessità; e di condividere, magari con altre applicazioni, il codice in esse
contenuto. Ho detto «al pari delle DLL»: ciò non è del tutto corretto, ci sono delle
differenze importanti tra i due sistemi come vedremo poi nel seguito del corso.

Le versioni "Standard" sono, come dice il nome stesso, il livello base di Delphi per la
realizzazione di applicazioni semplici che non richiedono l'utilizzo di database e
l'accesso ad Internet, perlopiù indirizzate a studenti, hobbisti, principianti.

Le versioni "Developer" o "Professional" sono rivolte a coloro che utilizzano il


Delphi per lavoro e che necessitano di supporto per database e internet.

Le versioni "Enterprise", come dice il nome, sono rivolte alle aziende che
necessitano del supporto per il controllo di versione, accesso a database sever,
internet, internazionalizzazione del software.

In questo corso si presuppone, almeno per alcune funzionalità, di avere a


disposizione l'edizione "Professional" di Delphi 5.0, la stessa che normalmente
utilizzo per lavoro.

Immagini delle confezioni delle varie releases della versione 5.0 (sito Borland)

http://programmazione.html.it/delphi/delphi_02.htm (1 di 2) [21/05/2002 11.16.34]


DELPHI: Pascal, Delphi, Linux

LEZIONE 3: Pascal, Delphi, Linux

Come già menzionato nell'Introduzione storica, l'intento del professor Niklaus era
quello di sviluppare un linguaggio facilmente implementabile su piattaforme
differenti. Esistono diverse implementazioni del Pascal alcune di queste sono
disponibili anche in ambiente Linux. Si tratta di implementazioni di tipo GNU e Open-
Source, quale per esempio il "Free Pascal". Questa implementazione è
completamente compatibile con il Pascal standard e nelle sue ultime versioni
implementa anche l'Object Pascal.

Esistono anche ambienti di sviluppo GNU come per esempio Lazarus che
riproducono quasi in maniera speculare l'IDE di Delphi e che permettono di
realizzare con la stessa facilità (anche se ancora con molti problemi) interfacce
grafiche per applicazioni Linux. Le distribuzioni menzionate supportano nativamente
codice Delphi. Ciò permetterà di effettuare il porting di applicazioni per Windows
in ambiente Linux apportando il minor numero possibile di modifiche al codice.

Sulla stessa strada sta lavorando Borland che ha già rilasciato la versione 1.0 del
suo nuovo prodotto, il Kylix. Questo prodotto è a tutti gli effetti il porting di Delphi per
Windows in ambiente Linux. La stessa Borland afferma che dalla versione 6.0 di
Delphi sarà possibile effettuare il porting di applicazioni da Windows a Linux
semplicemente ricompilando il codice con il Kylix. Il Kylix, per chi già conosce
Delphi, ne è la riproduzione esatta: stessa interfaccia grafica, stessi tools, stesse
impostazioni e disposizione dei comandi. La cosa più importante è che il codice
compilato con il Kylix è nativo per Linux ovvero in formato ELF. Per permettere il
porting nella maniera più semplice possibile la Borland ha dovuto riscrivere l'intera
VCL (Visual Component Library, ovvero la libreria dei componenti visuali di Delphi)
in maniera tale che fosse indipendente dalla piattaforma. Ciò è stato realizzato
creando una nuova libreria la CLX (si legge clix) che si basa per la parte grafica sulle
librerie Qt (oggetti multipiattaforma della Trolltech) le stesse utilizzate anche per la
realizzazione della parte grafica di KDE.

Purtroppo per le prime due edizioni del Kylix non esiste una licenza Open Source o
GPL per cui andranno acquistate e non a basso prezzo. Si vocifera però la prossima
uscita di una terza edizione che andrà a coprire questa fascia. L'intento di Borland
infine è anche quello di integrare sotto lo stesso tool di sviluppo sia Delphi che C++
BUILDER. Staremo a vedere.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_03.htm [21/05/2002 11.16.59]


DELPHI: Panoramica sul linguaggio

LEZIONE 4: Panoramica sul linguaggio

Sempre facendo riferimento alla parte introduttiva storica di questo corso, abbiamo
visto che Delphi si basa su Object Pascal. Si tratta, come d'altronde il Pascal, di un
linguaggio compilato, fortemente tipizzato, che supporta la progettazione strutturata e
orientata agli oggetti. Offre un codice di facile lettura, una compilazione veloce e l'uso
di più file unit per la programmazione modulare. Object Pascal ha delle speciali
funzionalità che supportano la struttura a componenti e l'ambiente RAD di Delphi.

I programmi Delphi sono generalmente divisi in moduli di codice sorgente denominati


unit. Ogni programma inizia con una intestazione, che ne specifica il nome, la quale
è seguita da una clausola opzionale uses e da un blocco di dichiarazioni e di
istruzioni. La clausola uses elenca le unit che sono collegate nel programma; queste
possono essere condivise da programmi diversi ed hanno spesso proprie clausole
uses. La clausola uses fornisce al compilatore informazioni sulle dipendenze
esistenti tra i moduli. Dal momento che queste informazioni vengono memorizzate
nei moduli stessi, i programmi in Object Pascal non richiedono né makefile, né file
header, né direttive "include" per il preprocessore come per esempio nel C++.

Diamo un'occhiata alle estensioni utilizzate dal compilatore Delphi. Questo si aspetta
di trovare il codice sorgente in Pascal in di tre tipi:

● File sergente di unit, che terminano con l'estensione .PAS


● File progetto, che hanno come estensione .DPR
● File sorgente di package, che terminano con l'estensione .DPK

I file sorgente delle unit contengono la maggior parte del codice di un'applicazione.
Ogni applicazione Delphi ha un singolo file progetto e diversi file unit; il file progetto,
corrispondente al file "principale" nel Pascal tradizionale, organizza i file unit in
un'applicazione. Delphi mantiene automaticamente un file progetto per ciascuna
applicazione. I file sorgente dei packages sono simili ai file progetto, ma vengono
usati per costruire speciali librerie a collegamento dinamico, denominate package.
Esistono altri file utilizzati nella realizzazione di una applicazione Delphi che non
contengono codice Pascal. Questi file vengono di norma mantenuti automaticamente
da Delphi. Essi sono:

● I file from, terminano con l'estensione .DFM


● I file di risorse, che hanno come estensione .RES
● I file di opzioni di progetto, che terminano in .DOF

Esistono anche altri tipi di file utilizzati esclusivamente per le impostazioni


dell'ambiente di sviluppo come per esempio i file .DSK che contengono le
impostazioni del desktop dell'IDE.

I file .DFM sono dei file di risorse di Windows che contengono bitmap, stringhe e così
via. Ogni file form è la rappresentazione binaria della scheda Delphi corrispondente
generalmente ad una finestra o ad una finestra di dialogo di una applicazione
Windows. L'IDE di Delphi permette anche di modificare in maniera testuale il
contenuto di questi file; tranne in casi eccezionali, come il recupero di schede
danneggiate a causa di componenti malfunzionanti, non sia ha la necessità di
intervenire direttamente in maniera testuale su questo tipo di file. La gestione viene
generalmente tenuta dai tool visuali di Delphi. Ad ogni scheda è altresì associato un
file .PAS contenente il codice Pascal che si riferisce alla scheda in oggetto. Questo
file contiene il codice degli eventi degli oggetti nella scheda, le routines definite
dall'utente per quella scheda, ecc. Per impostazione predefinita questi file hanno lo
stesso nome del file form.

I file di risorse sono file di risorse standard di Windows (.RES) per contenere la
bitmap per l'icona dell'applicazione.

http://programmazione.html.it/delphi/delphi_04.htm (1 di 3) [21/05/2002 11.17.05]


DELPHI: Panoramica sul linguaggio

Questo è quello che si ha prima di effettuare la compilazione del codice. Una volta
che il codice è stato compilato, troveremo dei nuovi file nella directory del progetto,
alcuni con lo stesso nome di quelli appena visti ma con estensioni differenti. Queste
sono:

● .DCU (Delphi Compiled Unit): sono le estensioni che il compilatore da alle


unit compilate che in seguito verranno linkate insieme nel file .EXE
● .DLL (Dinamic Link Library): solo le estensioni che contraddistinguono una
libreria a collegamento dinamico; ovviamente questo tipo di estensione sarà
disponibile qualora sia stato compilato un progetto di libreria a collegamento
dinamico.
● .BPL (Borland Package Library): sono le estensioni delle librerie a
collegamento dinamico della borland ovvero i package; anche queste
estensioni non saranno sempre disponibili tranne se si effettua la
compilazione di un progetto di package.

Diamo uno sguardo ad un semplice esempio, il tipico programmino "Hello World!"


che si è soliti realizzare in fase di apprendimento di un nuovo linguaggio.

Si tratta di una semplice applicazione per console che può essere compilato ed
eseguito dalla riga di comando.

Program Hello_World;

{$APPTYPE CONSOLE}

Var MessageStr : String;

Begin

MessageStr := 'Hello world!';

Writeln(MessageStr);

End.

La prima riga di questo codice, dichiara il nome del programma. La direttiva


{$APPTYPE CONSOLE} indica al compilatore che si tratta di una applicazione
console, da eseguire dalla riga comandi.

http://programmazione.html.it/delphi/delphi_04.htm (2 di 3) [21/05/2002 11.17.05]


DELPHI: Panoramica sul linguaggio

La riga successiva dichiara una variabile di tipo stringa.

Successivamente, all'interno del blocco Begin..End., che costituisce il blocco


principale del programma, viene dapprima assegnata alla variabile MessageStr il
valore contenuto nella stringa 'Hello world!'. In ultimo con l'istruzione
Writeln(MessageStr) il contenuto della variabile MessageStr viene visualizzato sullo
schermo dopo di che il programma termina.

Per compilare il programma dell'esempio, è possibile, se Delphi è stato


correttamente installato e nella path di sistema è riportato il percorso ..\BIN nella
directory di installazione di Delphi, utilizzare il seguente comando: supponendo di
aver chiamato il file del programma Hworld.Pas o Hworld.dpr possiamo scrivere

DCC32 Hworld.pas

Otterremo un file eseguibile di nome Hworld.exe che se eseguito stamperà a video il


teso 'Hello world!'.

Questo non è solitamente l'uso che si fa di Delphi. Delphi è solitamente utilizzato per
la realizzazione di applicazioni Windows con interfaccia grafica non eseguibili dalla
riga di comando. Anche la struttura del file di progetto è leggermente differente e non
contiene generalmente codice effettivo dell'applicazione se non poche chiamate ai
metodi definiti nelle unit.

Vediamo un attimo alcune particolarità che avrete sicuramente già notato. Prima di
tutto il segno ";" (punto e virgola) che delimita la fine di una istruzione. Il Pascal,
come il C++ ed altri linguaggi simili, necessita di questo segno di punteggiatura per
riconoscere il termine di una istruzione. Un'altra particolarità è il "." (punto) che
delimita la fine del blocco principale del codice. Come vedremo più avanti, solamente
il blocco principale delle unit avrà il punto dopo l'End del blocco principale, i blocchi
delle Funzioni e delle Procedure utilizzeranno normalmente il punto e virgola.

Ma passiamo ora ad esaminare in maniera più approfondita la sintassi e la struttura


del linguaggio.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_04.htm (3 di 3) [21/05/2002 11.17.05]


DELPHI: Panoramica sull'IDE

LEZIONE 5: Panoramica sull'IDE

L'IDE (Integrated Development Environment) di Delphi ci mette a disposizione


molti strumenti utili per la stesura del codice e per la realizzazione visuale delle
interfacce grafiche dei nostri programmi.

Fig. 1- La finestra principale dell'IDE di Delphi

Quello che colpisce non appena si avvia l'IDE di Delphi per la prima volta, è che
questo non ricopre interamente il desktop. Ciò permette di vedere ciò che è dietro
l'IDE senza ridurre l'IDE stesso ad icona.

Fig. 2 - Come appare l'IDE di Delphi nel desktop di Windows

L'IDE si compone principalmente di quattro sezioni fondamentali: la finestra


principale (fig. 1), l'Object Inspector, il Code Editor, il Form Designer.

La finestra principale, contiene i menu, le toolbar con i tasti per l'accesso rapido alle
funzioni più usate (personalizzabile), la Component Palette contenente tutti i
componenti visuali e non installati in Delphi. Quest'ultima può essere organizzata a
piacimento aggiungendo o togliendo pagine e spostando i componenti da una pagina
all'altra. Per esempio, si potrebbe aggiungere una pagina "Help" dove andremo a
riporre tutti quei componenti che serviranno ad implementare il sistema di aiuto in
linea delle nostre applicazioni. Per fare ciò, si può cliccare con il tasto destro sulla
Component Palette e scegliere la voce Properties che visualizzerà una finestra di
dialogo tramite la quale sarà possibile procedere alla personalizzazione.

L'IDE permette di personalizzare la disposizione e le caratteristiche degli strumenti


attivi dando la possibilità di salvare queste impostazioni come configurazioni del
desktop per essere richiamate alla necessità. Per esempio, come è visibile nella
figura 2, nel mio ambiente di sviluppo ho definito una configurazione "Edit" che
utilizzo durante la stesura del codice. Si potrebbe definire anche una configurazione
"Debug", da attivare quando si effettua il debug di una applicazione, che visualizzi gli

http://programmazione.html.it/delphi/delphi_05.htm (1 di 4) [21/05/2002 11.19.04]


DELPHI: Panoramica sull'IDE

strumenti per il debug e nasconda quelli non necessari a questo scopo. Una volta
salvate, queste configurazioni sono richiamabili tramite il menu a tendina posto nella
toolbar a fianco a quella dei menu.

L'Object Inspector è quello strumento che ci permette di modificare a design time la


proprietà dei componenti che inseriamo nelle Form dell'applicazione. Ci permette
altresì di associare agli eventi dei componenti il codice opportuno.

Fig. 3 - L'Object Inspector di Delphi

Il Code Editor è un editor ASCII completo, ricco di utili funzionalità, tramite il quale si
agisce sul codice sorgente dell'applicazione.

Fig 4 - Il Code Editor

Il Form Designer è lo strumento che, durante la fase di sviluppo, ci permette di


disporre e configurare i componenti nelle nostre Form. A livello visuale, insieme
all'Object Inspector, costituisce lo strumento fondamentale per lo sviluppo delle
nostre applicazioni.

http://programmazione.html.it/delphi/delphi_05.htm (2 di 4) [21/05/2002 11.19.04]


DELPHI: Panoramica sull'IDE

Fig. 5 - Il Form Designer

Vedremo questi strumenti più approfonditamente nei paragrafi seguenti.

Oltre a questi strumenti sono da menzionare: il Project Manager per la gestione dei
progetti e di tutti i files che lo compongono, la To-Do List che permette di definire
all'interno del codice cosa c'è da fare (appunto To-Do!), il Class Explorer che
permette di visualizzare l'albero "genealogico" di una classe, ovvero tutti gli oggetti di
cui quella classe eredita proprietà e metodi.

Una novità importante da segnalare, rispetto alle versioni precedenti, è la possibilità


che offre il Project Manager di aprire più progetti contemporaneamente. Ciò risulta
molto utile quando abbiamo a che fare con applicazioni composte anche da DLL.
Un'altra funzionalità messaci a disposizione dall'IDE di Delphi è l'Object Repository,
ovvero uno strumento che ci permette di creare una specie di database contenente
schede, finestre di dialogo, moduli dati, wizard, DLL atte a semplificare lo sviluppo di
applicazioni. Questo è disponibile dal menu File scegliendo la voce New. Così
facendo apparirà una finestra contenente le strutture base di per vari oggetti. In
pratica questo strumento rappresenta una interfaccia per gestire templates di oggetti.
Ciò permette ad esempio di creare applicazioni con la stessa struttura di finestre,
magari centralizzando il codice in comune. È possibile espandere la dotazione di
templates esistenti aggiungendone di personalizzati; ciò è possibile cliccando con il
tasto destro del mouse sulla finestra dell'Object Repository e scegliendo la voce
Properties, oppure dal menu Tool scegliendo la voce Repository.

Questa è la dotazione standard dell'IDE di Delphi, ma non finisce qui… La struttura


dell'IDE è aperta, ovvero è possibile scrivere applicazioni o add-on per l'interfaccia di
sviluppo stessa. Per esempio, come potete notare dalle figure precedenti, nel mio
ambiente di sviluppo ho installato una serie di add-on chiamati G-Experts che
aggiungono molte funzionalità utili all'interfaccia di sviluppo. In poche parole, se l'IDE
manca di qualche funzionalità, potrete sempre inserirla voi scrivendo del codice ad
hoc. Questo argomento non verrà trattato nel corso, ma esistono libri, come quelli di
Marco Cantù, che spiegano come creare estensioni per l'IDE di Delphi.

Tra gli strumenti aggiuntivi, forniti con Delphi, sono presenti anche un Image Editor
molto semplice per realizzare le icone dei componenti e delle applicazioni, il
Database Desktop (per le versioni Professional) per l'esplorazione e la
manipolazione di database ed il Package Editor.

In ultimo è da aggiungere che la maggior parte degli strumenti nominati


precedentemente possono essere disposti a piacere all'interno dell'IDE. Per
esempio è possibile inserire nella finestra che contiene l'Object Inspector anche il
Project Manager come è possibile vedere dalle figure. Questi strumenti sono di tipo
dockable, ovvero si comportano come le barre degli strumenti di Word e quindi è
possibile, trascinandoli, posizionarli in una finestra o nell'altra oppure lasciarli liberi
come finestra a sé nel desktop.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_05.htm (3 di 4) [21/05/2002 11.19.04]


DELPHI: L'Object Inspector

LEZIONE 6: L'Object Inspector

L'Object Inspector è, insieme al Form Designer, lo strumento più utilizzato, a livello


visuale, per la creazione delle applicazioni. Tramite questo strumento si possono
gestire tutte le proprietà (disponibili a design time) ed eventi dei componenti visuali e
non. Anche questo strumento è ampiamente personalizzabile. Una delle
personalizzazioni possibili è quella di visualizzare l'elenco delle proprietà ordinate per
nome o raggruppate per categorie.

Fig. 1 - Le proprietà del componente Button1 visualizzate nell'Object Inspector

Come è possibile vedere dalla figura 1 tutte le proprietà impostabili a design time,
sono disponibili nell'Object Inspector. Le pagine di questo si dividono, sia per quanto
riguarda le proprietà che per i gestori degli eventi, in due parti: sulla sinistra sono
riportati i nomi delle proprietà e sulla destra i rispettivi valori. L'Object Inspector è
capace di visualizzare ed impostare numerosi tipi di dati. Per quanto riguarda i dati
numerici e le stringhe di caratteri, questi vengono rappresentati così come sono (per
esempio la proprietà Height del componente e la proprietà Name). Per tipi di dati più
complessi, come per esempio il font, che è rappresentato da un classe TFont,
l'Object Inspector aggiunge davanti al nome della proprietà un segno "+". Cliccando
su di esso, si espandono le proprietà relative alla classe TFont istanziata per
l'Oggetto Button1.

Fig. 2 - La proprietà Font del componente Button1.

http://programmazione.html.it/delphi/delphi_06.htm (1 di 4) [21/05/2002 11.19.16]


DELPHI: L'Object Inspector

Quando a fianco di un valore è presente un tasto con tre puntini ( ) significa che
l'Object Inspector ha un Editor di Proprietà ad hoc per quel tipo di valore. Cliccando
questo tasto si apre una finestra che permette di modificare il valore della proprietà e
di riportarlo automaticamente nell'Object Inspector. La stessa operazione può essere
effettuata premendo la combinazione di tasti Ctrl + Invio dalla tastiera. Sempre come
esempio, quello riportato di seguito è l'editor di proprietà per il tipo TFont.

Fig. 3 - L'editor di proprietà per il tipo TFont.

Quando invece al posto del tasto è presente il tasto ciò indica che è possibile
scegliere il valore da una lista predefinita di valori. Infatti cliccando questo tasto,
otteniamo ciò che è visibile nella figura 4

Fig. 4 - Scelta del valore dalla lista di valori predefiniti.

Questo è possibile in quanto la proprietà ModalResult visibile in figura è di tipo


TModalResult che altro non è che un tipo enumerato. Tutti i valori dei tipi enumerati
sono selezionabili con la modalità precedentemente vista. È anche possibile
utilizzare i tasti freccia della tastiera per selezionare un valore oppure facendo doppio
click con il tasto sinistro del mouse i valori si avvicenderanno secondo l'ordine
riportato nell'elenco. Per esempio, prendiamo in esame la figura 4. Il valore
selezionato per la proprietà ModalResult è mrNone; senza visualizzare la tendina
con l'elenco potremmo fare doppio click con il mouse sul valore per ottenere mrOk;
se continuiamo a fare doppio click avremo mrCancel e così via.

C'è ancora un altro tipo di dato che l'Object Inspector è in grado di gestire; è il tipo
Set, insieme. Quando una proprietà è di tipo Set, il suo valore è racchiuso tra
parentesi quadre. In pratica vengono rappresentati tra parentesi gli elementi che
sono inclusi nell'insieme.

http://programmazione.html.it/delphi/delphi_06.htm (2 di 4) [21/05/2002 11.19.16]


DELPHI: L'Object Inspector

Fig. 5 - Come appare una proprietà di tipo Set nell'Object Inspector

Le proprietà Set, presentano, come le proprietà di tipo Class, un segno "+" davanti
al nome della proprietà; cliccando su di esso viene mostrato l'elenco degli elementi
che possono appartenere all'insieme. Per indicare che un particolare elemento è
incluso nell'insieme, impostare il suo valore a True e viceversa per escluderlo
impostarlo a False.

Passiamo ora a visionare la pagina dell'Object Inspector dedicata alla


manipolazione dei gestori di eventi.

Fig. 6 - La pagina relativa ai gestori degli eventi.

Nella figura 6 è riportata la pagina degli eventi relativi ad un componente Button


(TButton). Per definire un gestore di eventi per un particolare evento, ci sono vari
modo; fare doppio click con il tasto sinistro del mouse sull'evento corrispondente,
scrivere il nome del gestore di evento nella casella a destra del nome dell'evento e
premere invio, scegliere un gestore di evento tra quelli già definiti agendo sul tasto
con la freccia.

Seguendo il primo modo l'IDE creerà per noi un gestore di evento il cui nome sarà
composto dal nome del componente più il nome dell'evento a cui si riferisce.
Ovviamente verrà aggiunto al codice tutto ciò che è necessario per creare il gestore
di evento. Vediamo un esempio. Sempre con il nostro componente Button di nome
Button1, facendo doppio click sul evento OnClick nell'Object Inspector, verrà creato
automaticamente un gestore per l'evento OnClick di nome "Button1Click" e verrà
aggiunto il seguente codice a quello già esistente

http://programmazione.html.it/delphi/delphi_06.htm (3 di 4) [21/05/2002 11.19.16]


DELPHI: L'Object Inspector

procedure TForm1.Button1Click(Sender: TObject);

begin

end;

All'interno del blocco Begin..End di questa procedura andrà solamente inserito il


codice che dovrà essere eseguito nel momento in cui si premerà il componente
Button. Insieme al codice sopra riportato, viene, ovviamente, aggiunta la
dichiarazione della procedura "Button1Click" come procedura di tipo pubblico
all'interno della classe TForm1.

type

TForm1 = class(TForm)

Button1: TButton;

procedure Button1Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

Nella parte superiore dell'Object Inspector è presente è presente una casella


combinata dalla quale è possibile scegliere il componente di cui modificare le
proprietà.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_06.htm (4 di 4) [21/05/2002 11.19.16]


DELPHI: Il Form Designer

LEZIONE 7: Il Form Designer

Il Form Designer di Delphi è quello strumento che permette, a livello visuale, di


creare le interfacce grafiche delle applicazioni semplicemente disponendo i
componenti su di esso ed impostando le loro proprietà. Questo gestisce sia
componenti visuali che non visuali. Gli oggetti non visuali, (ovviamente in fase di
sviluppo sono visibili!) sono rappresentati da dei riquadri assomiglianti agli speed
buttons delle toolbars recanti però una icona che è stata definita durante lo sviluppo
del componente. Un esempio di oggetto non visuale è l'oggetto Timer (TTimer) che in

fase di sviluppo appare così .

Lavorare con il Form Designer è semplicissimo, è sufficiente selezionare un


componente dalla Component Palette, situata nella finestra principale dell'IDE e
posizionarlo all'interno di quella che sarà la nostra form. Il posizionamento può
avvenire in vari modi: si può cliccare sul componente nella Component Palette e
successivamente cliccare nella form alla posizione in cui lo vogliamo inserire; si può
cliccare il componente nella Component Palette quindi cliccare nella form alla
posizione voluta e trascinare per definirne le dimensioni. Se non si trascina durante il
posizionamento del componente, questo assumerà una la dimensione standard
definita la momento dello sviluppo.

Una volta che il componente si trova nella form, possiamo spostarlo,


ridimensionarlo, cambiare le sue proprietà. Per posizionare più componenti dello
stesso tipo è possibile selezionare selezionarlo dalla Component Palette tenendo
premuto il tasto Shift sulla tastiera. Così facendo intorno ad esso nella Component
Palette apparirà un rettangolo di colore blu che indicante che stiamo posizionando
più componenti di quel tipo. Per disattivare questa modalità di inserimento basta
cliccare sul tasto con la freccia nella Component Palette. Un'altro modo per inserire
componenti in un form è quello di fare doppio clic sul tasto relativo nella Component
Palette; così facendo il componente verrà posizionato al centro della form.

Fig. 1 - Il Form Designer con alcuni componenti

Per spostare un componente è sufficiente cliccare su di esso e mantenendo cliccato


trascinarlo nella posizione voluta. Per ridimensionare un componente, invece,
occorre prima selezionarlo e quindi trascinare una delle otto maniglie che compaiono
attorno ad esso. La logica è la stessa utilizzata da programmi di grafica tipo
CorelDraw e simili.

Nella figura 1 è riportata l'immagine del Form Designer con alcuni componenti. È
possibile notare le maniglie di ridimensionamento attorno al componente Edit1.

I puntini visibili sullo sfondo della form sono relativi alla griglia di posizionamento,
ciò permette di allineare comodamente i componenti in quanto, grazie alla proprietà
snap, questi si agganciano automaticamente, con l'angolo superiore, a questi punti.

Quando si sposta un componente, appare un riquadro che ci informa sulla posizione

http://programmazione.html.it/delphi/delphi_07.htm (1 di 3) [21/05/2002 11.19.26]


DELPHI: Il Form Designer

dell'angolo superiore sinistro del componente rispetto all'angolo superiore sinistro


della form, il tutto espresso in pixel.

Fig. 2 - Spostamento di un componente

La stessa cosa accade quando si ridimensiona un componente; questa volta però


vengono visualizzate le dimensioni che assume lo stesso espresse sempre in pixel e
rappresentano la lunghezza e l'altezza del componente.

Fig. 3 - Ridimensionamento di un componente

Facendo click con il tasto destro del mouse su un componente o sulla form appare
un menu le cui voci permettono di allineare i componenti, di definirne l'ordine di
tabulazione (la sequenza con cui viene assegnato il fuoco ai componenti spostandosi
con il tasto Tab della tastiera), cambiare lo Z-Order dei componenti (portarli in primo
piano o sullo sfondo), modificare l'ordine di creazione dei componenti.

Fig. 4 - Il menu contestuale.

In questo menu possono essere presenti anche altre voci (quelle visualizzate sono
quelle standard) in base all'oggetto specificato poiché questo potrebbe avere un
Component Editor personalizzato che semplifichi la configurazione delle sue
proprietà. Potrebbero essere presenti anche voci che permettono di impostare, senza
ricorrere all'Object Inspector, alcune proprietà come per esempio per un componente
TreeView (TTreeView), dove in testa al menu contestuale appare la voce "Items
Editor..." che permette di aprire un editor ad hoc per l'inserimento, la cancellazione e
ridisposizione degli elementi dell'albero.

All'interno del Form Designer è possibile selezionare più oggetti per impostarne le
proprietà comuni agli stessi valori. Ho parlato di proprietà comuni in quanto al
momento della selezione di più componenti, anche di tipo differente, l'Object

http://programmazione.html.it/delphi/delphi_07.htm (2 di 3) [21/05/2002 11.19.26]


DELPHI: Il Form Designer

Inspector visualizzerà ovviamente solo le proprietà impostabili su tutti i componenti


selezionati. Per selezionare più componenti si può selezionare il primo di essi e
tenendo premuto il tasto Shift sulla tastiera selezionare gli altri. È anche possibile
utilizzare solamente il mouse per selezionare più componenti.

Se i componenti sono posizionati in modo da essere all'interno di una area


rettangolare, si può, cliccare sulla form e trascinare. Così facendo si vedrà un
rettangolo che avrà come origine il punto in cui si è iniziato a trascinare e come
dimensione quella da voi definita trascinando il cursore del mouse. A questo punto
rilasciando il tasto del mouse avremo selezionato tutti i componenti presenti
all'interno del rettangolo di selezione. Una volta selezionati i componenti possono
essere deselezionati cliccando semplicemente sulla form o sottraendoli alla selezione
tenendo sempre premuto il tasto Shift sulla tastiera e cliccando su di essi.

Gli spostamenti e i ridimensionamenti sono possibili anche tramite la tastiera,


tramite i tasti freccia e al combinazione del tasto Ctrl o del tasto Shift. Per spostare
un componente tramite i tasi freccia, è necessario prima selezionarlo quindi tenendo
premuto il tasto Ctrl ed agire sui tasti freccia per spostarlo nelle direzioni
rappresentate dai tasti. Per ridimensionare un componente procedere in maniera
analoga allo spostamento agendo però sul tasto Shift. Questa modo di operare
risulta comodo per un posizionamento ed un ridimensionamento preciso dei
componenti in quanto le variazioni avvengono a scatti di un pixel per volta.

Una volta posizionati i componenti all'interno della form, onde evitare di spostarli
inavvertitamente, è possibile bloccarli con la funzione "Lock Controls" dal menu Edit.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_07.htm (3 di 3) [21/05/2002 11.19.26]


DELPHI: Il Code editor (parte prima)

LEZIONE 8: Il Code editor (parte prima)

Il Code editor di Delphi è un editor ASCII molto completo, ricco di molte funzioni utili
nella stesura del codice. Quando si impostano le proprietà degli oggetti a livello
visuale, l'ambiente integrato di Delphi apporta, a livello di codice Pascal, le modifiche
necessarie. Per esempio quando aggiungiamo ad una Form un componente
prendendolo dalla "Component Palette", viene aggiunta automaticamente la
dichiarazione di quell'oggetto nel codice.

Le caratteristiche fondamentali del "Code Editor", sono il "Syntax Highligth", il


"Class Completion", il "Code Insight", il "Code Explorer", la possibilità di
aggiungere dei segna posto (bookmarks) per ritornare velocemente ad una certa
posizione all'interno del codice, i "Tool Tips" che forniscono informazioni sia durante
la fase di debug (valori delle variabili, …) che in fase di stesura del codice, fornendo
per esempio aiuto sulla sequenza e sul tipo di parametri da passare ad una funzione
o procedura. Altra funzionalità che ritengo importante, se non fondamentale, è quella
dell'aiuto in linea. Posizionando il cursore su una porzione del codice e premendo
F1, si accede direttamente all'help relativo al testo su cui è posizionato il cursore.

Fig. 1 - Ecco come appare la schermata del Code Editor

Come è possibile vedere dall'immagine, nel code editor compare già del codice che
è stato inserito al momento della creazione di una nuova applicazione. Infatti quando
si crea una nuova applicazione, l'ambiente di sviluppo crea come sia il file di progetto
che una Form (o finestra) principale.

N.B. Nelle immagini che vedrete in questo corso, potreste accorgervi di


configurazioni e impostazioni che potrebbero differire dal vostro ambiente di
sviluppo. Per esempio, la combinazione di colori che vedete nel Code Editor è
"Classic" ovvero segue i colori del vecchio ambiente di sviluppo Turbo Pascal.
Ritengo sia la migliore sia per il contrasto a livello visivo, sia perché essendo lo
sfondo di colore scuro e lavorandoci parecchie ore non stanca. La configurazione
standard presente di default dopo l'installazione prevede uno sfondo bianco. Torno a
ripetere che in questo corso si presuppone di avere a disposizione almeno la
versione 5 Professional di Delphi.

Il codice visibile nella figura fa riferimento alla form riportata di seguito

http://programmazione.html.it/delphi/delphi_08.htm (1 di 4) [21/05/2002 11.21.05]


DELPHI: Il Code editor (parte prima)

Fig. 2 - Main form del programma non contenente alcun componente

Come potete vedere, nel codice è definita una classe TForm1 e una variabile Form1
di tipo TForm1.

In pratica l'ambiente di Delphi ha già predisposto per noi le basi per cominciare a
realizzare la nostra applicazione.

Ora, se aggiungiamo un componente alla nostra Form, vedremo che il codice viene
automaticamente aggiornato. Per esempio aggiungiamo dalla Component Palette un
componente Button e vediamo cosa succede

Fig. 3 - La nostra Form con aggiunto il componente Button

Ecco cosa viene automaticamente aggiunto al codice

http://programmazione.html.it/delphi/delphi_08.htm (2 di 4) [21/05/2002 11.21.05]


DELPHI: Il Code editor (parte prima)

Fig. 4 - La definizione dell'oggetto Button aggiunto alla nostra Form.

Quando inseriamo un componente in una form, viene automaticamente aggiornato


il codice nel Code Editor e possiamo anche notare che, come in precedenza per la
Form, gli viene anche assegnato un nome in base al numero di componenti dello
stesso tipo, con lo stesso nome, già presenti nella Form stessa. Infatti il nostro
Bottone all'interno del codice Delphi viene identificato tramite il nome di Button1.

Questa è solo il minimo che può fare per noi l'ambiente di sviluppo di Delphi, infatti
come avrete notato in Fig. 1, accanto all'area predisposta per l'editing del codice, vi è
un riquadro contenente una struttura ad albero, che ci fa vedere le varie sezione del
codice in forma semplificata. Questo riquadro è il Code Explorer che ci permette di
navigare all'interno del codice senza scorrere il testo del codice, ma semplicemente
selezionando la voce che ci interessa tra quelle riportate. Quando si inserisce del
codice nell'editor, viene automaticamente aggiornata la struttura del Code Explorer.

Questo strumento è, come tutti gli strumenti messi a disposizione dall'IDE,


personalizzabile permettendo di scegliere cosa visualizzare nell'albero e cosa no.

Fig. 5 - Ramo TForm1 del Code Explorer espanso mostra il componente Button1 da
noi aggiunto.

Facendo doppio clic con il mouse nel Code Explorer alla voce Button1, vedremo
che il cursore nel Code Editor si posizionerà dove nel codice è definito il nostro
componente Botton1.

Ora espandiamo tutti i rami visualizzati nel code Explorer è vediamo le


corrispondenze con il codice.

http://programmazione.html.it/delphi/delphi_08.htm (3 di 4) [21/05/2002 11.21.05]


DELPHI: Il Code editor (parte prima)

Fig. 6 - Tutti i rami del Code Explorer espansi; possiamo vedere le corrispondenze
con il codice nella destra

Tutti gli elementi che abbiamo visto all'interno del codice sono riportati anche nella
struttura ad albero del Code Explorer. Anche le unit incluse nella clausola Uses
vengono riportate il tutto ordinato alfabeticamente. Ciò risulta molto utile per ricercare
funzioni, procedure, variabili ecc. e per posizionarsi rapidamente nel codice
soprattutto in presenza di unit contenenti migliaia di righe di codice.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_08.htm (4 di 4) [21/05/2002 11.21.05]


DELPHI: Il Code editor (parte seconda)

LEZIONE 9: Il Code editor (parte seconda)

Tramite il Code Explorer è anche possibile inserire definizioni di variabili o funzioni o


procedure. Cliccando con il tasto destro del mouse e scegliendo la voce New viene
aggiunto un nuovo item editabile nel ramo selezionato.

Fig. 7 - Aggiunta di un nuovo item al Code Explorer.

Per inserire una funzione o una procedura, utilizzare la stessa sintassi che si
utilizzerebbe per dichiararla all'interno del Code Editor.

Riguardo al Code Editor bisogna ancora dire alcune cosette. Come visibile in tutte le
figure rappresentanti il Code Editor, nella parte superiore della finestra che lo
contiene sono presenti delle "linguette" (sulla sinistra) e delle freccie tipo Internet
Explorer. La funzionalità di quest'ultime è la stessa di quelle di Internet Explorer;
infatti ci permettono di navigare nel codice seguendo gli spostamenti che abbiamo
già effettuato.

N.B. Non corrisponde all'Undo e Redo per le modifiche apportate al codice!

Per quanto riguarda le linguette, esse rappresentano i files aperti nell'Editor e ci


permettono di passare ad editare un file o l'altro semplicemente cliccando su di esse.
Per esempio, se nel nostro editor avessimo aperto una unit di nome Unit2, vedremo
a fianco della linguetta Unit1 un'altra linguetta recante il nome della nuova unita
aperta. Cliccando su di essa visualizzeremmo il codice in essa contenuto.

Rimane ancora una parte relativa all'editor che non è stata ancora trattata. È quel
piccolo riquadro presente in fondo alla finestra. Quest'area contiene, ovviamente al
momento opportuno (dopo la compilazione per esempio), messaggi di errore,
informazioni riguardanti la compilazione del codice o errori segnalati dal Class
Completion. Sotto quest'area è presente una barra di stato che contiene informazioni
riguardo la posizione del cursore (riga: colonna), se sono state apportate modifiche al
codice (modified) e lo stato della modalità di inserimento dei caratteri (insert,
overwrite).

Il Class Completion è un'altra delle funzionalità, a mio parere molto comoda, che

http://programmazione.html.it/delphi/delphi_09.htm (1 di 3) [21/05/2002 11.21.14]


DELPHI: Il Code editor (parte seconda)

permette di appunto completare, in maniera quasi automatica, il codice che si sta


scrivendo. Infatti, digitando il nome di una classe, o di una sua istanza, per esempio il
nostro Button1 nelle figure precedenti, seguito dal punto (.), il nostro editor ci propone
un elenco di proprietà e metodi appartenenti a quella classe. È possibile scegliere
la voce che ci interessa tramite le frecce della tastiera o cominciare a digitare parte
della proprietà o metodo che ci interessa. Così facendo, tramite un ricerca
incrementale, l'editor ci porta alla voce di nostro interesse; battendo invio, l'editor
terminerà per noi la scrittura del codice.

Fig 8 - Il Class Completion in azione

Un'altra funzionalità che merita di essere citata, è quella che ci permette di saltare
alla definizione di un identificatore come si farebbe cliccando su un collegamento
ipertestuale in un Web Browser. Infatti, tenendo premuto il tasto Ctrl e puntando un
identificatore nel codice, quest'ultimo si trasforma in un collegamento ipertestuale a
tutti gli effetti. Cliccandolo, verremo trasferiti in quella parte di codice contenete la
dichiarazione dell'identificatore puntato, anche se questo è dichiarato in un'altra unit.

Fig. 9 - Un collegamento ipertestuale sul tipo di Button1

Un ultima caratteristica molto interessante del Code Editor è la possibilità di definire


dei templates di codice per richiamarli all'occorrenza. Tramite la voce Editor
Properties->Code Insight accessibile dal menu Tool è possibile definire il testo dei
templates.

http://programmazione.html.it/delphi/delphi_09.htm (2 di 3) [21/05/2002 11.21.14]


DELPHI: Il Code editor (parte seconda)

Fig. 10 - Definizione dei Code Template.

Una volta fatto ciò, è possibile richiamare i code templates durante la scrittura del
codice tramite la combinazione di tasti Ctrl + J; verrà visualizzata una lista dei
templates definiti. Cominciando a scrivere il nome di un template ed utilizzando la
combinazione di tasti Ctrl + J verrà visualizzata la lista dei templates corrispondenti
alla parte di nome già digitata e qualora si fosse digitato per intero il nome del
template o alla parte di nome digitata corrispondesse solamente un template, questo
verrebbe subito riportato nel codice.

Per esempio, scrivendo "cases" e premendo la combinazione di tasti Ctrl + J apparirà


il seguente blocco di codice

Case of

: ;

: ;

End;

con il cursore posizionato tra Case ed of, cioè è possibile definire nel template la
posizione da cui si incomincerà a digitare il codice mancante.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_09.htm (3 di 3) [21/05/2002 11.21.14]


DELPHI: Il Project Manager

LEZIONE 10: Il Project Manager

Il Project Manager strumento raggruppa in sé tutti i file che compongono un


progetto. Quest'ultimo può essere composto anche da più progetti. Questo potrebbe
essere il caso di un'applicazione che includa delle DLL o più eseguibili. I vari progetti
vengono racchiusi in gruppi. Uno solo dei progetti appartenenti ad un gruppo può
essere attivo nello stesso momento e questo sarà il progetto su cui si andrà ad
operare quando per esempio di procederà alla compilazione del codice.

Fig. 1 - Il Project Manager

Nella figura precedente questo strumento è inserito nella stessa finestra dell'Object
Inspector ma è anche possibile averlo come finestra a sé o inserito nel Code Editor.
Ecco come apparirebbe solo soletto.

Fig. 2 - Il Project Manager in una finestra a sé

Fondamentalmente si compone di un albero organizzato in gruppi e progetti. Nella


figura 2 è visibile il gruppo di progetto ProjectGroup1 contenente il progetto Project1.
A fianco sono riportati i percorsi in cui sono stati salvati i file. Nella parte superiore è
presente una casella combinata che permette la selezione del progetto attivo e

http://programmazione.html.it/delphi/delphi_10.htm (1 di 2) [21/05/2002 11.21.24]


DELPHI: Il Project Manager

accanto alcuni bottoni che permettono di aggiungere nuovi progetti, di rimuoverli o di


rendere attivo il progetto selezionato nell'albero. È possibile anche operare tramite il
menu contestuale che si attiva facendo click con il tasto destro del mouse sulla voce
che interessa.

Fig. 3 - I vari menu contetsuali che appaiono cliccando sulle diverse voci dell'albero
(Da sinistra: Project Group, Project, File del progetto, file sorgente ed altri)

Le ultime tre voci sono presenti in tutti i menu e permettono di visualizzare o


nascondere parti dell'interfaccia, come la Toolbar e la Status Bar, o di
attivare/disattivare la funzionalità di docking che permette di inserire il Project
Manager in altre finestre dell'IDE. Per aggiungere files al progetto è anche possibile
trascinarli da Esplora Risorse di Windows.

Tramite questo strumento è possibile gestire le opzioni relative al progetto


selezionando la voce Options dal menu contestuale del progetto oppure scegliendo
la stessa voce dal menu Project della finestra principale dell'IDE. Tra le opzioni è
possibile specificare informazioni riguardanti la versione dell'applicazione, direttive
del compilatore, i percorsi in cui il compilatore andrà a cercare le unit da compilare,
scegliere l'icona dell'applicazione, il suo titolo, quale sarà la finestra principale del
programma, quali finestre saranno create automaticamente all'apertura del
programmai, le opzioni del linker, quali packages sono utilizzati dall'applicazione e se
l'applciazione deve essere compilata utilizzando i packages di run-time.

Fig. 4 - La finestra di dialogo opzioni del progetto.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_10.htm (2 di 2) [21/05/2002 11.21.24]


DELPHI: Sintassi e Struttura del codice

LEZIONE 11: Sintassi e Struttura del codice

Abbiamo visto in precedenza un esempio di applicazione console. Ora vedremo un


altro esempio riguardante però un'applicazione per Windows ovvero quello che ci
interessa di più e per cui Delphi è nato.

Questo è il codice:

Program Editor;

Uses

Forms,

REAbout in 'REABOUT.PAS' {AboutBox},

REMain in 'REMAIN.PAS' {MainForm};

{$R *.RES}

Begin

Application.Title := 'Text Editor';

Application.CreateForm(TMainForm, MainForm);

Application.Run;

End.

Come potete vedere il codice è leggermente diverso, come contenuto, da quello che
abbiamo visto in precedenza ma la struttura è la stessa. Abbiamo una intestazione
del programma "Program Editor", una clausola "Uses" contenente i riferimenti per
l'inclusione di altre unit nel programma, ed il blocco principale "Begin..End.".
Solitamente in questo blocco sono presenti solamente chiamate a metodi dell'oggetto
Application del progetto. Ogni progetto Delphi ha una variabile Application di tipo
TApplication (o come vedremo TWebApplication, TserviceApplication in base al tipo
di progetto scelto) che permette di accedere alle proprietà e metodi base
dell'applicazione stessa.

Quindi la struttura di un file di progetto di Delphi può essere riassunta così (in rosso i
nostri commenti):

Program NomeProgramma; //Intestazione del programma

Uses Unit1, Unit2, …, UnitN; //Clausola uses per l'inclusione


di altre unit nel programma

{$R *.RES}

Begin //Blocco principale del progetto

...

End.

Struttura e Sintassi delle unit

http://programmazione.html.it/delphi/delphi_11.htm (1 di 5) [21/05/2002 11.21.29]


DELPHI: Sintassi e Struttura del codice

Ogni unit è contenuta in un file .PAS e può contenere dichiarazioni di tipi, variabili,
funzioni, procedure. Come il file di progetto, anche la unit inizia con una intestazione
(che ha inizio con la parola chiave Unit e dal nome della unit stessa) seguita da altre
sezioni come riportato di seguito:

Unit Unit1; {Intestazione}

Interface

Uses {Lista di unit}

Implementation

Uses {Lista di unit}

{Codice per la definizione di tipi,


variabili, funzioni e procedure}

Initialization

{Codice di inizializzazione della unit}

Finalization

{Codice di finalizzazione della unita}

End.

Come si può vedere abbiamo: una sezione definita "Interface" che conterrà le
definizioni di tipi, variabili, dichiarazioni di funzioni e procedure che si vuole siano
accessibili da altre unit; una sezione definita "Implementation" che conterrà il codice
"privato" della unit, non accessibile dall'esterno, consistente in dichiarazioni di tipi,
variabili, funzioni, procedure e il codice di implementazione delle funzioni o procedure
stesse. Le sezioni "Initialization" e "Finalization" sono facoltative e servono ad
eseguire codice per l'inizializzazione della unit e per la finalizzazione della unit
rispettivamente al momento del caricamento della unit stessa e al momento del
rilascio. Se in una clausola uses sono presenti più di una unit, la sequenza con cui
saranno eseguite le sezioni "Initialization" sarà la stessa di quella della clausola
uses. Per esempio se la clausola uses riporta

uses unit1, unit2, unit3;

l'ordine con cui verranno eseguite le sezioni di inizializzazione sarà: unit1, unit2,
unit3. Per quanto riguarda le sezioni di finalizzazione l'ordine sarà inverso rispetto a
quello di inizializzazione. Con riferimento all'esempio di prima avremo: unit3, unit2,
unit1.

Riguardo alla clausola uses di un programma o libreria, come nell'esempio di


programma Windows che abbiamo visto in precedenza, c'è la possibilità di indicare il
percorso in cui si trova la unit da includere nel progetto specificando dopo il nome
della unit la parola chiave "in" seguita dal percorso assoluto o relativo racchiuso tra
apici. Per esempio mettiamo che la unit "Unit1.pas" nel seguente progetto si trovi in
un percorso diverso da quello in cui sitrova il file principale del progetto avremo:

http://programmazione.html.it/delphi/delphi_11.htm (2 di 5) [21/05/2002 11.21.29]


DELPHI: Sintassi e Struttura del codice

Program Program1;

Uses Unit1 in 'c:\progetti\mioprogetto2\Unit1.pas';

In questo caso ho fatto riferimento ad un percorso assoluto ma avrei potuto


specificare un percorso relativo. Per esempio, se il Program1 si trovasse nella
directory "c:\progetti\mioprogetto1" e all'interno ci fosse una directory chiamata
"extra" in cui ripongo delle unit, nell'esempio precedente avrei potuto scrivere:

Uses Unit1 in 'c:\progetti\mioprogetto1\extra\Unit1.pas';

oppure

Uses Unit1 in '..\extra\Unit1.pas';

considerando sempre il fatto che il file principale del programma si trovi in


"c:\progetti\mioprogetto1".

Un'ultima cosa riguardo le unit. Come è facilmente intuibile, nella clausole uses di
una unit posso fare riferimento ad altre unit. In questo caso bisogna fare attenzione ai
"riferimenti circolari", ovvero fare attenzione a non far riferimento in una unit ad
un'altra che a sua volta si riferisce alla prima. Leggendo potrebbe venire in mente
che chi sta scrivendo questo corso sia un pazzo. In effetti è più semplice capire ciò
che ho detto con un esempio.

Program Program1;

Uses Unit1;

Const Pippo = Pluto;

...

Unit Unit1;

Interface

Uses Unit2;

Const Pluto = Paperino;

...

Unit Unit2;

Interface

Const Paperino = 1;

...

Nell'esempio precedente non ci sono problemi di "riferimenti circolari" e tutto funziona


bene.

http://programmazione.html.it/delphi/delphi_11.htm (3 di 5) [21/05/2002 11.21.29]


DELPHI: Sintassi e Struttura del codice

Program Program2;

Uses Unit1;

Const Pippo = Pluto;

...

Unit Unit1;

Interface

Uses Unit2;

Const Pluto = Paperino;

...

Unit Unit2;

Interface

Uses Unit1;

Const Paperino = 1;

...

In questo esempio invece abbiamo un problema di "riferimento circolare". Infatti la


Unit1 fa riferimento nella sua clausola uses alla Unit2 che a sua volta fa riferimento
nella sua clausola uses alla Unit1. Al momento della compilazione del progetto
questo errore viene segnalato dal compilatore. Nel caso in cui sia necessario
utilizzare questa struttura di progetto, per evitare un "riferimento circolare" tra la Unit1
e la Unit2 è sufficiente spostare la clausola uses con il riferimento alla Unit 1 della
Unit2 nella sezione "Implementation" come riportato di seguito:

Program Program2;

Uses Unit1;

Const Pippo = Pluto;

...

Unit Unit1;

Interface

Uses Unit2;

Const Pluto = Paperino;

...

Unit Unit2;

Interface

Const Paperino = 1;

Implementation

Uses Unit1;

...

Se abbiamo due unit che dichiarano una variabile, costante, tipo, routine con lo

http://programmazione.html.it/delphi/delphi_11.htm (4 di 5) [21/05/2002 11.21.29]


DELPHI: Sintassi e Struttura del codice

stesso nome, possiamo accedere alla variabile, costane, tipo, routine che ci interessa
fornendo anche il nome della unit nel modo seguente:

Unit Unit1;

Interface

Const Pluto = 1;

...

Unit Unit2;

Interface

Const Pluto = 2;

...

Uses Unit1, Unit2;

...

Topolino := Unit1.Pulto;
//La variabile Topolino assumerà valore 1

Topolino := Unit2.Pluto;
//La variabile Topolino assumerà valore 2

...

N.B. Chi conosce già il C++, può notare che le unit del Delphi o più in generale
dell'Object Pascal corrispondono ai "NameSpace" e che la clausola uses corrisponde
alla direttiva "#Include".

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_11.htm (5 di 5) [21/05/2002 11.21.29]


DELPHI: La sintassi (parte prima)

LEZIONE 12: La sintassi (parte prima)

L'Object Pascal usa il set di caratteri ASCII, comprendente le lettere dalla A alla Z e
dalla a a alla z, le cifre da 0 a 9 e gli altri caratteri standard. Non fa distinzione tra
maiuscole e minuscole, come il C++ o il Java o Javascript. Tutti i caratteri quali lo
spazio (ASCII 32) ed i caratteri di controllo, come il carattere di ritorno o di fine riga
etc. (ASCII 0 a 31), vengono chiamati Blank. La più piccola unità significativa di testo
in un programma è il token. Questo è composto da una istruzione o da
un'espressione o da una dichiarazione. Quindi un programma è una sequenza di
token separati da separatori (spazio o commento). È quindi legale scrivere nel
codice

Pippo:=1;Pluto:=2;

Per motivi di leggibilità e di convenzione, però, si preferisce utilizzare il formato


seguente:

Pippo := 1; Pluto : = 2;

I token si suddividono in simboli speciali, identificatori, parole riservate, direttive,


numerali, etichette, stringhe di caratteri.

Quelli che seguono sono simboli speciali:

#$&'()*+,-./:;<>=@[]^{}

Anche la combinazione di alcuni di essi rientra nei simboli speciali:

.. // (* (. *) .) := <> <= >=

Non fanno parte dei simboli speciali i seguenti caratteri:

! "(doppio apostrofo) ? % \ _(sottolineatura)| (pipe)~ (tilde)

Identificatori
Gli identificatori indicano variabili, costanti, procedure, funzioni, tipi, etc. Possono
avere una lunghezza qualunque e devono iniziare con una lettera o con un carattere
di sottolineatura (_) e non possono contenere spazi. Se è necessario, per una
maggiore leggibilità del codice, si può utilizzare il carattere sottolineatura (_).

Alcuni esempi di identificatori:

Count / Count / cOunt / counT / _Pippo / _pippo / Items_Count / Items_count /


Search_for

Numerali
In Object Pascal i numeri reali ed interi possono essere rappresentati sia in forma

http://programmazione.html.it/delphi/delphi_12.htm (1 di 4) [21/05/2002 11.21.37]


DELPHI: La sintassi (parte prima)

decimale che in forma esadecimale. La prima forma è composta da sequenze di


numeri separati o no da punti e possono essere precedute dai segni + o - per
indicarne il segno. Per i numeri interi non è possibile utilizzare il punto (ovviamente,
se sono interi!). Un esempio di numero intero è 23989, +23989 oppure -56739. I
numeri reali possono altresì essere rappresentati in forma esponenziale inserendo
all'interno della sequenze di numeri il carattere E o e seguito da un numero (positivo
o negativo). Questo carattere sta ad indicare che il numero alla sua sinistra va
moltiplicato per 10 elevato al numero alla sua destra. Per esempio:

7E-2 equivale a 7 x 10-2

12.25e6 e 12.25E+6 equivalgono entrambi a 12.25 x 106

Stringhe di caratteri
In Object Pascal le stringhe di caratteri sono racchiuse tra apostrofi ('). Due apostrofi
che non racchiudono alcuna carattere identificano una stringa null. Ad esempio ''
(occhio, sono due apostrofi adiacenti non le virgolette, quelle sopra il 2 nella
tastiera!). Alcuni si chiederanno come è possibile rappresentare all'interno di una
stringa il carattere apostrofo visto che quest'ultimo delimita la stringa stessa. Bene,
per rappresentare un apostrofo all'interno di una stringa basta ripeterlo due volte, per
esempio 'L''anno scorso'. Una stringa composta solamente da un apostrofo appare
così ''''. Uno spazio, invece, apparirà così ' '. È possibile inserire all'interno di una
stringa di caratteri, sequenze di caratteri di controllo utilizzando il simbolo # seguito
dal codice ASCII del codice stesso. Ad esempio, la stringa 'Questa stringa termina
con un a capo ed avanzamento riga.'#13#10.

Commenti
Come in quasi tutti i linguaggi di programmazione, per documentare o "commentare"
il codice sorgente, è possibile inserire, tramite particolari costrutti, dei commenti. In
Pascal esistono vari modi per inserire commenti all'interno del codice sorgente. Si
può far precedere il commento da //. Per esempio

Var Pippo : Integer; //Commento

In questo caso il commento terminerà con la fine della riga del codice. Per utilizzare
commenti su più righe, si possono impiegare le parentesi graffe {}. Ad esempio

Unit Unit1;

{ Questo commento

si sviluppa

su più righe }

...

Direttive del compilatore


Le direttive del compilatore sono particolari direttive che non influiscono
sull'esecuzione del programma ma sulla compilazione del codice sorgente. Queste
sono dei commenti che hanno come primo carattere, all'interno delle parentesi graffe
{} o dei simboli (* *), il carattere $ (dollaro). Un esempio di direttiva lo abbiamo già
visto nel primo esempio di programma all'inizio del corso

http://programmazione.html.it/delphi/delphi_12.htm (2 di 4) [21/05/2002 11.21.37]


DELPHI: La sintassi (parte prima)

{$APPTYPE CONSOLE}

In questo caso la direttiva indica al compilatore che il programma dovrà essere


compilato per essere eseguito dalla console e non nell'ambiente grafico di windows.

Esistono molte direttive per il compilatore e per un elenco completo si rimanda ai


manuali del linguaggio od alla guida in linea.

Operatori
Un operatore si comporta come le funzioni che fanno parte del linguaggio. Per
esempio se sia ha qualcosa del tipo (X + Y), che è composta da due operandi (X , Y)
e da un operatore (+), nel caso in cui X ed Y rappresentino dei numeri, l'espressione
restituirà la loro somma.

In Object Pascal esistono vari tipi e categorie di operatori. Ci sono operatori


aritmetici, per logica booleana, per la manipolazione logica a livello di bit, di stringa,
di puntatore, per i set, relazionali e classe.

Operatori aritmetici
Segue un elenco degli operatori aritmetici definiti in Object Pascal:

Operatore Operazione Tipo Operandi Tipo Risultato Esempio


+ Addizione Integer, real Integer, real X+Y
- Sottrazione Integer, real Integer, real X -Y
* Moltiplicazione Integer, real Integer, real X*Y
/ Divisione (reale) Integer, real Real X/Y
Div Divisione (intera) Integer Integer X div Y
Mod Modulo Integer Integer X mod Y

Come è facilmente comprensibile, tutti gli operatori sopra riportati sono operatori
binari, accettano cioè due operandi.

Per i prime quattro operandi non si sono problemi. Vediamo invece come si comporta
l'operatore div. Questo operatore effettua la divisione tra operandi di tipo integer e
restituisce un integer. In pratica questo operando restituisce il risultato di X / Y
arrotondandolo all'intero per difetto.

Vediamo un esempio:

X / Y (con X = 9 e Y = 2) resituisce 4,5

X div Y (con X = 9 e Y = 2) resituisce 4

L'operatore mod restituisce il resto della divisione intera dei suoi operandi. Accetta
operandi di tipo integer e il risultato è di tipo integer. Con riferimento all'esempio
precedente abbiamo che

X mod Y (con X = 9 e Y = 2) resituisce 1

perché equivale a

X - (X div Y) * Y = 9 - (9 div 2) * 2 = 1 (N.B. l'operatore di moltiplicazione, come altre


che vedremo di seguito, ha la precedenza rispetto alla sottrazione quindi prima viene
risolto il contenuto tra parentesi, poi viene effettuata la moltiplicazione tra il risultato
precedente e 2 ed infine viene sottratto a 9 il risultato delle operazioni precedenti: 9
div 2 = 4; 4 * 2 = 8; 9 - 8 = 1)

http://programmazione.html.it/delphi/delphi_12.htm (3 di 4) [21/05/2002 11.21.37]


DELPHI: La sintassi (parte prima)

IMPORTANTE: da segnalare che nelle espressioni X / Y, X div Y, X mod Y, ove Y


sia uguale a 0 (zero) si verifica un errore di divisione per zero

L'operatore + e l'operatore - sono impiegati anche come operatori unari ovvero si


applicano ad un solo operando. Essi servono a definire il segno di una espressione.
Si applicano ad operandi di tipo integer o real e il risultato restituito è dello stesso tipo
dell'operando a cui si è applicano ma con il segno opportunamente cambiato.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_12.htm (4 di 4) [21/05/2002 11.21.37]


DELPHI: La Sintassi (parte seconda)

LEZIONE 13: La Sintassi (parte seconda)

Operatori logici booleani


Segue un elenco degli operatori booleani definiti in Object Pascal:

Operatore Operazione Tipo Operandi Tipo Risultato Esempio


Not Negazione Boolean Boolean Not X
And Congiunzione Boolean Boolean X and Y
Or Disgiunzione Boolean Boolean X or Y
Xor Disgiunzione esclusiva Boolean Boolean X xor Y

Poco c'è da dire su questo tipo di operatori, essi seguono infatti le regole della logica
booleana.

Una considerazione importante va fatta sulle diverse modalità di valutazione che


l'Object Pascal offre. Questa è di default impostata sulla valutazione parziale, ma è
possibile impostare la valutazione totale utilizzando la direttiva del compilatore {$B+}
e disattivarla con la direttiva {$B-}. La valutazione parziale è da preferire perché
garantisce tempi di esecuzione più brevi, questo perché la valutazione
dell'espressione viene eseguita rigidamente da sinistra verso destra interrompendola
nel momento in cui la valutazione risulta completa. Questo tipo di valutazione si
applica sia all'operatore and che all'operatore or. In pratica quando viene valutata
una espressione del tipo X and Y e X assume valore False Y non viene valutata in
quanto il risultato dell'espressione è già noto; infatti una espressione del tipo X and Y
può risultare True in un solo caso, ovvero quando X ed Y sono True, in tutti gli altri
casi assume valore False.

Operatori logici con bit


Segue un elenco degli operatori logici applicabili ai bit definiti in Object Pascal:

Operatore Operazione Tipo Operandi Tipo Risultato Esempio


Not Negazione Integer Integer Not X
And Congiunzione Integer Integer X and Y
Or Disgiunzione Integer Integer X or Y
Xor Disgiunzione esclusiva Integer Integer X xor Y
Shl Spostamento a sinistra Integer Integer X shl 3
Shr Spostamento a destra Integer Integer X shr 3

Gli operatori shl e shr eseguono uno spostamento di bit, pari al numero indicato
dopo l'operando, rispettivamente a sinistra ed a destra. Per esempio, se X contiene il
valore 20 (in binario 010100) X shl 1 restituirà 40 (in binario 101000). Con riferimento
all'esempio precedente, se al posto di shl ci avessimo shr il risultato ottenuto
sarebbe stato 10 (in binario 001010). Quindi quando si utilizza l'operatore shl
equivale a moltiplicare il primo operando per 2 elevato al secondo operando.
Seguendo sempre l'esempio precedente questo equivale a X * 21, mentre nel caso
dell'operatore shr l'equivalente operazione è quella di divisione del primo operando
per 21.

Operatori relazionali
Segue un elenco degli operatori relazionali definiti in Object Pascal:

http://programmazione.html.it/delphi/delphi_13.htm (1 di 3) [21/05/2002 11.21.45]


DELPHI: La Sintassi (parte seconda)

Operatore Operazione Tipo Operandi Tipo Risultato Esempio


= Uguaglianza Vari Boolean X=2
<> Disuguaglianza Vari Boolean X <>Y
< Minore di Vari Boolean X<Y
> Maggiore di Vari Boolean X>Y
<= Minore uguale a Vari Boolean X <= 2
>= Maggiore uguale a Vari Boolean X >= 2

Normalmente i tipi degli operandi devono essere compatibili, eccezion fatta per i tipi
real ed integer che possono essere confrontati tra di loro.

Per quanto riguarda le stringhe, vengono confrontate in base al set di caratteri ASCII
esteso. Il confronto viene effettuato da sinistra verso destra, carattere per carattere.
Ad esempio la stringa 'Delphi' è minore della stringa 'Pascal' in quanto la lettera 'D'
viene nel set di caratteri prima della lettera 'P'. Ovviamente qui la differenza tra le due
stringhe è immediata; se avessimo avuto una stringa 'Delphi' ed una stringa 'Delpho',
quest'ultima sarebbe stata maggiore della prima ma solamente grazie all'ultimo
carattere che costituisce la stringa. Nel caso di una stringa Str1 = 'Delphi' e di un'altra
stringa Str2 = 'Delphi', il confronto Str1 = Str2 restituirebbe True.

Alcuni degli operatori che abbiamo già visto possono essere applicati anche in altri
casi. Per esempio gli operatori +, -, *, <=, >=, = , <> si applicano anche ai set
(insiemi) con rispettivamente i seguenti significati: unione, differenza, intersezione,
sottoinsieme di, superset, uguaglianza, disuguaglianza. Per i tipi set esiste anche un
altro operatore, l'operatore in che indica se un elemento è presente in un insieme.
Per esempio, se disponiamo di un set S composto dai seguenti elementi [1, 2, 3, 4]
l'espressione 3 in S restituirà True poiché 3 è un elemento del set S.

Per quanto riguarda i puntatori, gli operatori applicabili a quest'ultimi sono: +, -, ^, =,


<>. Il più importante (o il più usato) fra questi, è l'operatore ^ che serve a
dereferenziare un puntatore. Gli altri operandi servono ad aggiungere uno
scostamento all'indirizzo puntato, a calcolare lo scostamento tra gli indirizzi puntati da
due puntatori, a confrontare gli indirizzi puntati da due puntatori.

Un altro operatore importante è @ che serve a restituire l'indirizzo di una variabile,


l'indirizzo di ingresso di una subroutine o di un metodo di classe.

Regole di precedenza degli operatori


Espressioni complesse vengono valutate seguendo delle regole per ciascuno degli
operatori visti. Di seguito è presente una lista degli operandi e dei rispettivi livelli di
precedenza.

Operatore Precedenza
@, Not Prima (più alta)
*, /, div, mod, and, shl, shr, as Seconda
+, -, or, xor Terza
=, <>, <, >, <=, >=, in, is Quarta (più bassa)

In una espressione vengono valutati prima gli operatori con precedenza più alta. Per
esempio l'espressione

2 + 3 * 4 da come risultato 14 e non 20!

Infatti viene valutata prima la moltiplicazione tra 3 e 4 (12) poiché la moltiplicazione


ha una precedenza più alta ed in seguito viene aggiunto al risultato 2 (12 + 2 = 14).

Per modificare la precedenza degli operatori, si utilizzano le parentesi tonde '(', ')'.
Racchiudendo tra parentesi tonde una espressione, questa verrà valutata per prima
e tutto il resto a seguire seguendo sempre le regole di precedenza.

Con riferimento all'esempio precedente, potremmo ottenere come risultato 20,


modificandolo come segue:

http://programmazione.html.it/delphi/delphi_13.htm (2 di 3) [21/05/2002 11.21.45]


DELPHI: La Sintassi (parte seconda)

(2 + 3) * 4

Così facendo, verrà valutato prima il contenuto tra parentesi (2 + 3 = 5) ed il risultato


moltiplicato per 4 (5 * 4 = 20).

Le parentesi possono essere utilizzate anche quando la precedenza è già definita


dall'operatore, senza influenzare la valutazione, consentendo una maggiore
leggibilità ed interpretazione del codice. Sempre con riferimento all'esempio
precedente scrivendo

2 + (3 * 4)

il risultato non cambierebbe, mettendo bene in evidenza quale parte


dell'espressione viene valutata prima.

Per gli operatori che godono della stessa precedenza, la valutazione avviene
normalmente da sinistra verso destra. Ad esempio

2+3-4=1

poiché 2 + 3 = 5 e 5 - 4 = 1.

Conversione di tipi
Un'altra funzionalità che ci mette a disposizione il Delphi, è la possibilità di effettuare
delle conversioni di tipo. Ciò è possibile racchiudendo tra parentesi tonde
l'espressione da convertire e facendo precedere il tutto (senza spazi) dal tipo in cui si
vuole convertire l'espressione. La sintassi esatta è la seguente

tipo(espresione)

Ad esempio potrebbe essere utile convertire in boolean un valore intero come 0 nel
modo seguente: boolean(0).

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_13.htm (3 di 3) [21/05/2002 11.21.45]


http://programmazione.html.it/delphi/delphi_14.htm

LEZIONE 14: I tipi (parte prima)

Nelle sezioni precedenti del corso abbiamo visto, in particolare nelle sezioni che
spiegavano gli operatori, cose come Integer, String, Boolean. Si tratta dei tipi di dati.
Un tipo è essenzialmente un nome che identifica un particolare genere di dati. Un
tipo determina anche il range di valori validi per quel tipo. Quando si definisce una
variabile si deve specificarne il tipo.

L'Object Pascal è un linguaggio fortemente tipizzato. Ciò significa che distingue un


gran numero di dati. Solitamente, questa caratteristica permette al compilatore di
trattare i dati in maniera intelligente e di poter rilevare degli errori in fase di
compilazione che altrimenti sarebbero difficili da diagnosticare.

Esistono diversi tipi di dati in Object Pascal; questi sono organizzati in tipi
fondamentali (definiti direttamente dal linguaggio), generici, predefiniti e definiti
dall'utente.

I tipi fondamentali sono implementati direttamente nel linguaggio Object Pascal; i


loro formati e intervalli di validità sono gli stessi in tutte le implementazioni e sono
indipendenti dalla CPU e dal sistema operativo. I tipi generici invece, possono variare
da un'implementazione all'altra e sono specifici della piattaforma. È consigliabile
l'utilizzo dei tipi generici in quanto facilitano il porting tra piattaforme differenti.

I tipi predefiniti sono automaticamente riconosciuti dal compilatore e non hanno


necessità di essere dichiarati.

I tipi Object Pascal possono essere classificati come segue: simple, string,
structured, pointer, procedural, variant.

I tipi Simple
I tipi simple definiscono insiemi ordinati di valori. Appartengono a questa categoria di
tipi, i tipi ordinal e real.

I tipi ordinal

I tipi ordinal definiscono insiemi ordinati di valori in cui tutti i valori hanno un
predecessore ed un successore univoci ad esclusione del primo valore e dell'ultimo.
Sono tipi ordinal i tipi integer, boolean, character, subrange. Ogni valore è
caratterizzato da una ordinalità che ne determina l'ordinamento nel tipo. Solamente
per il tipo integer l'ordinalità corrisponde con il valore stesso. In tutti gli altri tipi, ad
esclusione del subrange, il primo valore ha ordinalità 0, successivo 1 e così a
seguire. Delphi, o meglio l'Object Pascal, mettono a disposizione diverse funzioni
predefinite che permettono di lavorare sulla ordinalità di questa categoria di tipi.

Le più importanti tra queste sono le seguenti:

Funzione Valore restituito


Ord restituisce l'ordinalità di un valore
Pred restituisce il predecessore del valore specificato
Succ restituisce il successore del valore specificato
High restituisce il valore più alto nel tipo
Low restituisce il valore più basso nel tipo

Vediamo alcuni esempi: High(Byte) restituisce 255 in quanto il valore più alto del tipo
è 255; Succ(4) è 5 poiché 5 è il successore di 4.

http://programmazione.html.it/delphi/delphi_14.htm (1 di 4) [21/05/2002 11.21.52]


http://programmazione.html.it/delphi/delphi_14.htm

Esistono due funzioni deifnite in Object Pascal che che si usano per incrementare e
decrementare il valore di una variabile ordinal e sono Inc e Dec. La prima incrementa
il valore ed equivale al Succ del valore stesso, la seconda lo decrementa ed equivale
al Pred del valore indicato.

I tipi Integer

I tipi Integer sono un sottoinsieme dei numeri interi. Esistono vari tipi di interi in
Object Pascal che si differenziano per intervallo di valori rappresentabili e per
formato. Per quanto riguarda i tipi interi generici abbiamo il tipo Integer e Cardinal. I
tipi integer fondamentali sono: Shortint, Smallint, Longint, Int64, Byte, Word,
Longword. Nella tabella seguente sono riportate le differenze tra i tipi menzionati.

Tipo Intervallo Formato


Integer -2147483648..2147483647 32 bit + segno
Cardinal 0..4294967295 32 bit
Shortint -128..127 8 bit + segno
Smallint -32768..32767 16 bit + segno
Longint -2147483648..2147483647 32 bit + segno
Int64 -263..263-1 64 bit + segno
Byte 0.255 8 bit
Word 0..65535 16 bit
Longword 0..4294967295 32 bit

Incrementando l'ultimo valore o decrementando il primo valore di un tipo integer il


risultato sconfina all'inizio od alla fine dell'intervallo valido per quel tipo. Ad esempio,
nel caso del tipo Byte che ha come intervallo 0..255 l'esecuzione del codice

var I : Byte;

...

I := High(Byte);

I := I + 1;

assegna ad I il valore 0. Da notare che se è stato attivato il controllo sugli intervalli, il


codice precedente genererà un errore di runtime.

I tipi character

Fanno parte dei tipi ordinal anche i tipi character i cui tipi fondamentali sono
AnsiChar e WideChar. Gli AnsiChar sono caratteri che occupano un byte (8 bit)
mentre i tipi WideChar sono caratteri che occupano due byte ovvero una word (16
bit). Il rpimo tipo è ordinato secondo il set di caratteri esteso ANSI mentre il secondo
è ordinato secondo il set UNICODE i cui primi 256 caratteri corrispondono ai caratteri
ANSI.

Il tipi generico è Char che nell'implementazione corrente equivale ad AnsiChar.

Esistono anche per questo tipo delle funzioni predefinite dell'Object Pascal come la
Chr che restituisce il carattere associato ad un intero compreso nell'intervallo
AnsiChar o WideChar. Ad esempio Chr(65) restituisce 'A'. Come per tutti i tipi
ordinali, è possibile applicare al tipo character la funzione predefinita Ord che
restituisce l'ordinale del valore character. Ad esempio, riprendendo l'esempio
precedente, Ord('A') restituisce 65 che è appunto l'ordinalità del carattere 'A'
all'interno di AnsiChar o WideChar.

Valgono anche per i tipi character gli sconfinamenti come visto per i tipi integer.

I tipi Boolean

http://programmazione.html.it/delphi/delphi_14.htm (2 di 4) [21/05/2002 11.21.52]


http://programmazione.html.it/delphi/delphi_14.htm

I tipi boolean predefiniti sono quattro e sono: Boolean, ByteBool, WordBool e


LongBool. Si utilizza preferibilmente il tipo boolean mentre gli altri esistono per
compatibilità con altri linguaggi e con l'ambiente Windows.

Una variabile boolean equivale a ByteBool ed occupa un byte (8 bit) mentre una
WordBool occupa due byte (16 bit) ed una LongBool due word (32 bit). L'intervallo di
valori per questo tipo è composto da due costanti predefinite che sono True e False.

Un valore boolean viene considerato True quando la sua ordinalità è diversa da zero.
Da notare che se questo valore compare in un contesto in cui è atteso un valore
boolean, questo viene automaticamente convertito dal compilatore in True la dove la
sua ordinalità sia diversa da 0.

I tipi Enumerated

Il tipo enumerated serve a definire insiemi ordinati di valori. Da notare che l'ordinalità
di questi valori segue l'ordine con cui sono stati elencati i valori al momento della
dichiarazione del tipo.

Per dichiarare un tipo enumerated si utilizza la seguente sintassi

Type EnumeratedTypeName = (val1, val2, ..., valn);

Un esempio pratico potrebbe essere la dichiarazione di un tipo enumersto "Seme" in


un gioco di carte

Type Seme = (Cuori, Quadri, Fiori, Picche);

Nel caso del nostro esempio, i valori validi per il tipi "Seme" da noi dichiarato sono:
Cuori, Quadri, Fiori, Picche. In pratica quando si dichiara un tipo enumerated si
dichiarano delle costanti (val) del tipo enumerated dichiarato
(EnumeratedTypeName). Sempre facendo riferimento al nostro esempio, Cuori,
Quadri, Fiori, Picche sono costanti di tipo Seme.

Bisogna fare attenzione, al momento della dichiarazione del tipo, a non utilizzare,
per gli identificatori dei valori, nome che possano andare in conflitto con altri nomi
dichiarati nello stesso campo d'azione. Ad esempio, dichiarando un tipo

Type TSound = (Click, Clack, Clock);

ed utilizzando il valore Click come nel codice seguente, si verificherà un conflitto di


nome; questo perché Click è anche il nome di un metodo della classe TControl e di
tutti i sui discendenti (nel senso degli oggetti che discendono da esso!).

Procedure TForm1.Edit1Exit(Sender : TObject);

Var Snd : TSound;


Begin

...

Snd := Click;

...

End;

Si può aggirare questo tipo di inconveniente facendo riferimento al valore Click


qualificandolo con il nome della unit in cui è stato dichiarato. Ammettiamo che il tipo
TSound sia dichiarato nella unit SoundFuncs, si potrebbe utilizzare il seguente

http://programmazione.html.it/delphi/delphi_14.htm (3 di 4) [21/05/2002 11.21.52]


http://programmazione.html.it/delphi/delphi_14.htm

codice

Snd := SoundFuncs.Click;

Comunque, benché sia possibile utilizzare il codice precedente, si consiglia di


cercare di definire, per i valori dei tipi eneumerated, dei nomi che non entrino in
conflitto con altri identificatori. Una buona pratica è quella di anteporre al nome del
valore enumerated le iniziali del nome del tipo enumerated. Nell'esempio precedente
abbiamo dichiarato un tipo TSound; possiamo ridefinire i nomi dei suoi valori
anteponendo "ts" al loro nome in questo modo

Type TSound = (tsClick, tsClack, tsClock);

Come anche

Type TMyType = (mtVal1, mtVal2, mtVal3);

I tipi Subrange

Come dice il nome stesso, questi tipi identificano dei sottoinsiemi di valori
appartenenti ad un altro tipo ordinale che viene definito tipo base. Per definire un
tipo subrange, si utilizza la seguente sintassi

Type SubrangeTypeName = Low..High;

dove Low ed High appartengono allo stesso tipo ordinal e Low è minore di High.
Questo costrutto definisce quel sottoinsieme di valori compresi tra Low ed High.

Per esempio

Type PrimiDieciNumeri = 1..10;

definisce un tipo subrange di nome PrimiDieciNumeri che conterrà i valori 1, 2, 3, 4,


5, 6, 7, 8, 9, 10.

Non valgono per questo tipo gli sconfinamenti visti per i tipi precedenti.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_14.htm (4 di 4) [21/05/2002 11.21.52]


DELPHI: I tipi (parte seconda)

LEZIONE 15: I tipi (parte seconda)

I tipi real
I tipi real definiscono insiemi di numeri che possono essere rappresentati con la
notazione in virgola mobile.

I tipi real fondamentali sono riportati di seguito

Cifre Dimensione
Tipo Intervallo
significative in byte
Real48 2.9 x 10-39..1.7 x 1038 11-12 6
Single 1.5 x 10-45..3.4 x 1038 7-8 4
Double 5.0 x 10-45..1.7 x 10308 15-16 8
3.6 x 10-4951..1.1 x
Extended 4932 19-20 10
10
Comp -263 + 1..263 - 1 19-20 8
-
Currency 922337203685477.5808.. 19-20 8
922337203685477.5807

Il tipo real generico equivale al double.

Seguono alcune considerazioni sui tipi real fondamentali

● Il tipo Real48 viene mantenuto per compatibilità con il tipo real delle versioni
precedenti di Object Pascal. Non essendo un tipo che utilizza un formato di
memorizzazione non nativo per le CPU intel determina prestazioni più lente.
● Extended è il tipo real che permette maggiore precisione ma a sua volta è meno
portabile.
● Il tipo Comp (computazionale) è nativo per le CPU Intel e rappresenta un integer
a 64 bit. Pur essendo un tipo integer viene classificato come real in quanto non si
comporta come tipo ordinal; non è infatti possibile incrementare o decrementare
un valore comp. Viene mantenuto per compatibilità con le versioni precedenti. Al
suo posto si consiglia l'uso del tipo Int64.
● Currency è un tipo di dati che viene utilizzato per i calcoli valutari poiché ne
riduce al minimo gli errori di arrotondamento. È caratterizzato da punto decimale
fisso e viene memorizzato come un integer a 64 bit scalato, con le ultime quattro
cifre meno significative che rappresentano i decimali. Valori di questo tipo
vengono automaticamente moltiplicati o divisi per 10000 quando vengono usati in
espressioni con altri tipi real.

I tipi String
L'Object Pascal possiede, a differenza di altri linguaggi come ad esempio il C ed il C++,
tipi di dati dedicato alla gestione dell stringhe. I tipi string supportati dall'Object Pascal
sono i seguenti

Tipo Lunghezza massima Memoria richiesta Usato per


compatibilità con versioni
ShortString 255 caratteri da 2 a 256 byte
precedenti
AnsiString ~231 caratteri da 4 byte a 2GB caratteri a 8 bit (ANSI)
caratteri UNICODE; server e
WideString ~230 caratteri da 4 byte a 2GB
interfacce COM

AnsiString è il tipo preferito per la maggior parte della applicazioni. A volte viene anche

http://programmazione.html.it/delphi/delphi_15.htm (1 di 7) [21/05/2002 11.22.00]


DELPHI: I tipi (parte seconda)

chiamato LongString.

I tipi string possono essere mischiati nelle assegnazioni e nelle espressioni in quanto il
compilatore effettua automaticamente le conversioni del caso. Bisogna invece fare
attenzione nel passaggio di parametri alle funzioni ed alle procedure che deve rispettare il
tipo dei parametri dichiarati insieme alla funzione o procedura.

Il tipo generico per il tipo string è string. Non si tratta proprio di un tipo ma di una parola
chiave del linguaggio che identifica un tipo di dati contenete una stringa. Come stato
predefinito la parola chiave string si riferisce al tipo AnsiString (se non è seguita da un
numero fra parentesi quadre). È possibile cambiare string in ShortString tramite la
direttiva del compilatore {$H-}.

Anche per il tipo string esistono funzioni predefinite come Length che restituisce la
lunghezza in caratteri di una stringa e SetLength che ne regola la lunghezza.

Due variabili di tipo string possono essere confrontate tramite gli operatori relazionali. In
tal caso il confronto avviene confrontando l'ordinalità dei caratteri che occupano la stessa
posizione. Ad esempio la stringa "AB" risulterà minore di "AC". Confrontando stringhe di
differente lunghezza, la dove i caratteri della stringa più lunga non hanno corrispondenza
nella stringa più corta, vengono considerati maggiori. Di conseguenza la stringa "AB"
risulterà maggiore della stringa "A".

Una variabile string può essere indicizzata come se si trattasse di un array. I tal caso è
possibile accedere i caratteri di una stringa utilizzando la seguente sintassi.

S[i] dove S è una variabile di tipo string e i un intero indicante la posizione all'interno della
stringa S.

Per cui, S[2] := 'A' assegna alla posizione 2 della stringa S il carattere 'A'.

Molta attenzione va fatta nella manipolazione delle stringhe tramite la tecnica vista in
precedenza in quanto, in presenza di stringhe di tipo AnsiString o WideString che in realtà
rappresentano stringhe terminate con null, la sovrascrittura dell'ultimo carattere (il null
appunto) potrebbe causare un errore di violazione di accesso.

I tipi Short String

Come visto nella tabella dei tipi string, il tipo ShortString può essere lungo da 0 a 255
caratteri. Mentre la sua lunghezza varia dinamicamente, la memoria che occupa viene
allocata staticamente e corrisponde a 256 byte; all'interno del primo byte è riportata la
lunghezza in caratteri della stringa. Ne consegue che per ottenere la lunghezza di una
stringa ShortString è possibile leggere il valore ordinale del carattere presente alla
posizione 0 secondo la rappresentazione ad array. L'operazione Ord(S[0]) è equivalente
a Length(S) dove S è una variabile string.

L'Object Pascal supporta anche dei sottotipi di ShortString la cui lunghezza varia da 0 a
255. Questi tipi vengono definiti specificando un numerale tra parentesi quadre dopo la
parola chiave string. Per esempio

Type String50 = String[50];

definisce un tipo String50 la cui lunghezza massima è di 50 caratteri. Questo tipo di


dichiarazione permette di risparmiare spazio in memoria poiché per contenere un
variabile del tipo String50 verranno allocati solamente 51 byte a differenza dei 256 del tipo
base ShortString.

I tipi Long String (AnsiString)

Come accennato in precedenza il tipo AnsiString viene anche definito LongString. Il tipo
AnsiString rappresenta una stringa allocata dinamicamente la cui lunghezza massima è
limitata solamente dalla memoria disponibile. Una variabile di questo tipo non è altro che
un puntatore che occupa 4 byte. Il sistema utilizzato, totalmente gestito in maniera
invisibile all'utente, è simile ad una lista di caratteri. Ciò permette appunto di avere una
lunghezza dipendente solamente dalla memoria disponibile nel sistema. Un'altra

http://programmazione.html.it/delphi/delphi_15.htm (2 di 7) [21/05/2002 11.22.00]


DELPHI: I tipi (parte seconda)

caratteristica di questo tipo è che, trattandosi di puntatori, due o più variabili di questo tipo
possono far riferimento allo stesso valore senza spreco aggiuntivo di memoria.

I tipi Wide String

L'allocazione di variabili di questo tipo è il medesimo di quello per variabili di tipo


AnsiString con la differenza che le stringhe rappresentate utilizzano caratteri UNICODE
a 16 bit. Si differenzia solamente per efficienza che risulta minore in quanto non utilizza
accorgimenti come il contatore dei riferimenti utilizzato invece dal tipo AnsiString. Questo
tipo è utilizzato prevalentemente per la compatibilità con i tipi nelle funzionalità COM.

I tipi strutturati
Fanno parte di questa categoria di tipi i Set, gli Array, i Record, i file oltre alle classi ed
alle interfacce. Una istanza di tipo strutturato raccoglie sotto di se più valori. I tipi Set
possono a loro volta contenere valori di tipo strutturato eccezion fatta per il tipo Set che
può contenere solamente valori di tipo ordinale.

Set

Il tipo set implementa quello che in matematica viene definito insieme. Un set può
contenere solamente valori dello stesso tipo ordinale senza seguire alcun ordinamento
intrinseco. Altresì non ha significato che un elemento sia inserito due volte in un Set.
L'intervallo di validità dei valori di un tipo Set dipende dall'intervallo di validità del tipo
ordinal utilizzato per creare il Set. Questo tipo ordinale che origina il Set viene chiamato
tipo base. Il costrutto utilizzato per dichiarare un tipo Set è il seguente

Set of TipoBase

Esempi di dichiarazione di tipi Set sono i seguenti:

Type TChars = 'A'..'Z';

TSetOfChars = Set of TChars;

Per assegnare dei valori a varibili di tipo Set, facendo riferimento all'esempio precedente,
si procede come segue:

Var Caratteri : TSetOfChars;

...

Caratteri := ['A', 'F', 'B', 'T', 'X'];

Con analogia alla matematica ed agl'insiemi, un tipo Set può essere vuoto e viene
rappresentato con [].

Array

Gli array rappresentano una raccolta indicizzata di elementi tutti appartenenti ad uno
stesso tipo che, come per i tipi Set, prende il nome di Tipo Base. A differenza dei tipi Set,
un array può contenere, proprio perché indicizzato, più volte lo stesso valore. I tipi base
utilizzabili nella dichiarazione di array, possono essere qualsiasi dai tipi ordinal ai tipi
string, puntatori, altri array etc. Gli array possono essere dichiarati staticamente o
dinamicamente ed essere mono o multi dimensionali. Un array monodimensionale
rappresenta un vettore di elementi, mentre array multidimensionali rappresentano matrici
di elementi.

http://programmazione.html.it/delphi/delphi_15.htm (3 di 7) [21/05/2002 11.22.00]


DELPHI: I tipi (parte seconda)

Array statici

Il costrutto che si utilizza per dichiarare array statici è il seguente

array [TipoIndice1, ..., TipoIndicen] of TipoBase;

Il numero di elementi che può contenere un array così definito è dato dal prodotto delle
dimensioni dei TipoIndice racchiusi tra parentesi quadre.

Gli array monodimensionali o vettori, hanno un solo TipoIndice come in questo esempio

Type TVettore100 = Array [1..100] of Integer;

In questo esempio è stato definito un vettore che può contenere al massimo 100 valori
integer.

Per accedere ai valori di una variabile array come sopra dichiarata si utilizza la sintassi
MioVettore[50] dove MioVettore è il nome della variabile di tipo TVettore100 e 50 l'indice
dell'elemento 50 all'interno dell'array. Quando si definisce una variabile di tipo array e non
si assegnano valori a tutti gli elementi, gli elementi non assegnati vengono comunque
allocati e conterranno valori non definiti.

Si possono anche dichiarare array multidimensionali come di seguito

Type TMatrice10X10 = Array [1..10, 1..10] of Integer;

In questo caso è stato dichiarato un tipo TMatrice10X10 composto da 10 righe e 10


colonne. La dichiarazione precedente equivale a dichiarare un tipo array di array come di
seguito

Type TMatrice10X10 = Array [1..10] of Array [1..10] of Integer;

Per accedere i valori di una variabile dichiarata come TMatrice10X10 si procede in


maniera simile agli array monodimensionali tranne per il fatto che, essendo questi a più
dimensioni, bisogna indicare un indice per ognuna delle dimensioni dell'array. Nel caso
dell'esempio precedente MiaMatrice[2, 3], dove la variabile MiaMatrice è di tipo
TMatrice10X10, accederà al valore contenuto alla riga 2 colonna 3 della matrice.

In Object Pascal sono definite funzioni per operare con gli indici e le dimensioni degli
array statici. Con Low e High si ottiene rispettivamente il limite inferiore e quello
superiore del primo tipo dell'array. Con Length si ottiene il numero di elementi nella prima
dimensione dell'array.

Array dinamici

A differenza degli array statici, gli array dinamici non hanno dimensioni e lunghezza fisse
e quindi la memoria viene riallocata quando si assegna un valore all'array o quando si
utilizza la funzione SetLength per impostarne le dimensioni.

Per dichiarare deigli array dinamici si utilizza il costrutto

Type NomeTipo = Array of TipoBase;

Quando si dichara un array dinamico questo non occupa memoria fino a quando non si
effettua una chiamata alla funzione SetLength.

http://programmazione.html.it/delphi/delphi_15.htm (4 di 7) [21/05/2002 11.22.00]


DELPHI: I tipi (parte seconda)

Per esempio, se è stata dichiarata una variabile MioArray di tipo TMioArray = Array of
Integer per creare effettivamente l'array in memoria effettuando la chiamata
SetLength(MioArray, 100), si allocherà memoria per un array di 100 elementi integer. Da
ricordare che l'indicizzazione degli array dinamici si basa sempre su integer e comincia da
0. In pratica gli array dinamici sono puntatori similmente ai LongString. Per disallocare un
array dinamico, è possibile assegnare alla variabile che lo referenzi il valore Nil oppure
passare la variabile stessa alla funzione Finalize; questo verrà disallocato solamente nel
caso in cui non siano presenti altri riferimenti ad esso. Array di lunghezza 0 assumono il
valore Nil.

Anche per gli array dinamici sono disponibili le funzioni standard Length, Low ed High.
Queste restituiscono rispettivamente il numero di elementi dell'array, 0 in ogni caso, e
l'indice superiore dell'array. Nel caso di array vuoti High restituisce -1. Interessante notare
che si avrà in questo caso High < Low (-1 < 0).

Record

Per fare subito un confronto con altri linguaggi, tra cui C e C++, questo tipo corrisponde ai
tipi struct (Struttura). Esso rappresenta un insieme di elementi eterogenei fra loro, ovvero
un tipo record può raggruppare in se tipi diversi di elementi. Gli elementi di un record
vengono detti campi; nella dichiarazione di un tipo record occorre specificare un nome
per il tipo record ed un nome ed un tipo per ciascuno dei suoi campi. Ecco la struttura
della dichiarazione di un record

Type NomeTipoRecord = Record

Campo1 : Tipo1;

...

Campon : Tipon;

End;

Come per gli array dinamici, la dichiarazione di una un tipo record, non alloca memoria
per i campi in esso contenuti. La memoria viene allocata al momento della dichiarazione
di una variabile di quel tipo record.

Un esempio di tipo record è

Type TPersona = Record

Nome : String[40];

Cognome : String[40];

Eta : Integer;

End;

Per accedere ai campi del record si utilizza la sintassi seguente

NomeVariabile.Nomecampo

Dall'esempio precedente

http://programmazione.html.it/delphi/delphi_15.htm (5 di 7) [21/05/2002 11.22.00]


DELPHI: I tipi (parte seconda)

Var Persona : TPersona;

...

Persona.Nome := 'Carlo';

Persona.Cognome := 'Marona';

Persona.Eta := 25;

Se si devono accedere più volte i campi del record è possibile utilizzare il costrutto
With..do nel modo seguente

With Persona do

Begin

Nome := 'Carlo';

Cognome := 'Marona';

Eta := 25;

End;

Quando si dichiara un tipo record, si ha la possibilità di inserire nella sua dichiarazione


delle varianti alla sua struttura. Queste varianti vanno inserite dopo aver dichiarato tutti i
campi del record. L sintassi è la seguente

Type NomeTipoRecord = Record

Campo1 : Tipo1;

...

Campon : Tipon;

Case SelettoreVariante : TipoOrdinale of

ListaCostanti1 : (Variante1);

...

ListaCostantin : (Varianten);

End;

Il SelettoreVariante è facoltativo; se si omette è necessario omettere anche i due punti


successivi. Il SelettoreVariante deve essere di tipo ordinal.

Ogni ListaCostanti può essere composta da una costante dello stesso tipo di
SelettoreVariante o da un elenco di costanti, sempre del tipo di SelettoreVariante,
separate da virgole.

Ogni Variante è un elenco di dichiarazioni di con la stessa sintassi vista per la


dichiarazione dei campi della parte principale del record.

Le parti variant dei record condividono lo stesso spazio di memoria. Si può leggere o
scrivere nei campi variant dei record in qualsiasi momento, tenendo conto che se si scrive
in un campo di un variant e subito dopo si scrive in un campo di un altro variant, i dati

http://programmazione.html.it/delphi/delphi_15.htm (6 di 7) [21/05/2002 11.22.00]


DELPHI: I tipi (parte seconda)

possono essere sovrascritti. Se si specifica il un SelettoreVariante, questo funziona come


un campo normale nella parte principale del record. Per un trattazione più approfondita si
rimanda alla guida al linguaggio.

Tipi file

I tipi file rappresentano un insieme ordinato di elementi tutti dello stesso tipo. Un tipo file
viene dichiarato come segue

Type NomeTipoFile = File of Tipo;

dove NomeTipofile è un nome di identificatore valido e Tipo un tipo di dato qualsiasi di


dimensioni fissate. Non possono essere utilizzati quindi nelle dichiarazioni di tipi file
puntatori, array dinamici, long string, altri file o qualsiasi altro tipo strutturato che contenga
uno qualsiasi dei tipi precedentemente elencati.

Con riferimento all'esempio per i tipi record possiamo dichiarare un tipo file come segue

Type TFileElenco = File of TPersona;

...

Var FileElenco : TFileElenco;

Dichiarare un tipo od una variabile facendo uso solamente della parola file darà origine
ad un tipo di file non tipizzato, senza tipo.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_15.htm (7 di 7) [21/05/2002 11.22.00]


DELPHI: I tipi (parte terza)

LEZIONE 16: I tipi (parte terza)

Puntatori e tipi pointer


Probabilmente saprete già cosa sono i puntatori. La programmazione ad oggetti,
tipica oramai di tutti i più recenti linguaggi ad alto livello, fa grande uso dei puntatori.
Un puntatore è una variabile che indica un indirizzo di memoria. Si dice che un
puntatore punta ad una variabile quando contiene l'indirizzo in memoria della
variabile stessa. Conseguentemente il puntatore fa riferimento alla memoria ed ai
dati di quella variabile. Per i tipi strutturati, come array, record ecc., un puntatore ad
una variabile di questo tipo punta all'indirizzo del primo elemento della struttura.

Anche i puntatori hanno un tipo che serve ad indicare il tipo di dati a fanno
riferimento. Esiste comunque un tipo generico di puntatore, il tipo pointer che può
puntare a qualsiasi tipo di dato. La memoria occupata dai puntatori equivale a 4 byte.

Per definire un puntatore ad un tipo si utilizza la sintassi

Type NomeTipoPuntatore : ^Tipo;

per esempio

Type PInteger : ^Integer;

definisce un tipo puntatore puntatore ad un tipo integer.

L'operatore "^" può essere utilizzato in due differenti modi: uno è quello che abbiamo
già visto, l'altro è utilizzarlo per dereferenziare il puntatore, cioè per accedere al
valore contenuto all'indirizzo di memoria indicato dal puntatore stesso.

Per esempio, avendo una variabile I di tipo PInteger che punta ad un indirizzo
contenente il valore 2, I^ restituisce 2.

Esiste anche un altro operatore ed è "@" che viene utilizzato per ottenere l'indirizzo
di una variabile.

Sono definite anche alcune funzioni standard dell'Object Pascal per operare con i
puntatori e sono: New, GetMem, Addr, Ptr. Le prime due funzioni assegnano ad un
puntatore esistente un indirizzo di memoria (vengono utilizzate per creare
dinamicamente delle variabili), le altre due restituiscono puntatori rispettivamente ad
un indirizzo o ad una variabile.

La costante speciale Nil, già vista in precedenza, serve ad indicare che un puntatore
non fa riferimento a nulla e può essere assegnata a qualsiasi tipo di puntatore.

Tipi procedurali
I tipi procedurali vengono utilizzati per trattare le funzioni e le procedure come valori
che possono essere assegnati alle variabili o ad altre procedure o funzioni.

Per dichiarare un tipo procedurale, per un certo tipo di funzione o procedura, basta

http://programmazione.html.it/delphi/delphi_16.htm (1 di 4) [21/05/2002 11.22.08]


DELPHI: I tipi (parte terza)

prendere la dichiarazione della procedura o funzione ed eliminarne l'identificatore


dopo la parola procedure o function.

Ad esempio, avendo una funzione dichiarata come

Function Bisestile(Anno : Integer) : Boolean;

potremmo dichiarare un tipo

Type TBisestileFunc = Function (Anno : Integer) : Boolean;

dichiarare una variabile

Var FBsisestile : TBisestileFunc;

ed assegnarle la funzione Bisestile come segue

FBisestile := Bisestile;

Quelli visti possono essere definiti tipi procedurali semplici ma esiste un altro tipo
procedurale, il tipo puntatore a metodo. Vengono utilizzati per fare riferimento a
metodi di classi e per la loro definizione occorre aggiungere le parole chiave "of
object" dopo la dichiarazione del tipo.

Type TNotifyEvent = Procedure (Sender : TObject) of object;

In questo esempio si è definito un tipo TNotifyEvent (tipo già esistente in Delphi ed


utilizzato dai componenti della VCL) come puntatore ad un metodo. Ritroveremo i tipi
puntatore a metodo più avanti quando tratteremo la stesura di componenti.

Nell'esempio precedente abbiamo visto come assegnare ad una variabile di tipo


procedurale l'indirizzo di una procedura. Approfondiamo il comportamento dei tipi
procedurali nelle assegnazioni e nelle espressioni.

1. Nelle assegnazioni, l'interpretazione viene determinata dal tipo della variabile


presente a sinistra dell'operatore ":=". Se la variabile è di tipo procedurale e,
ovviamente, a destra abbiamo una funzione, avremo l'assegnamento alla
variabile procedurale dell'indirizzo della funzione specificata a destra come
già visto. Se la variabile a sinistra non è una variabile procedurale allora, se il
tipo della variabile corrisponde al tipo restituito dalla funzione a cui fa
riferimento la variabile specificata alla destra dell'operatore ":=" allora avremo
l'assegnazione alla prima variabile del valore restituito dalla funzione riferita
dalla seconda.
2. Nelle espressioni, viene sempre eseguita la funzione e viene poi valutata
l'espressione in base al valore restituito dalla funzione stessa. Quindi
confrontando in una espressione, ad esempio in una istruzione If, due
variabili procedurali puntanti a funzioni, verranno confrontati i valori restituiti
dalle rispettive funzioni e non le variabili stesse.
3. Per confrontare il contenuto di due variabili procedurali, occorre ricorrere
all'operatore "@". Per ottenere l'indirizzo di memoria di una variabile
procedurale, occorre utilizzare un doppio operatore "@".

Vediamo alcuni esempi

http://programmazione.html.it/delphi/delphi_16.htm (2 di 4) [21/05/2002 11.22.08]


DELPHI: I tipi (parte terza)

FBisestile := Bisestile; // Assegna a FBisestile l'indirizzo


della funzione Bisestile

Var B : Boolean;

F : TBisestileFunc;

...

F := Bisestile;

B := F; // Assegna a B il valore restituito dalla


funzione Bisestile riferita da F

If F1 = F2 then... // In questo caso vengono


confrontati i risultati delle
funzioni riferite dalle due variabili

If @F1 = @F2 then... // Vengono confrontati i valori


contenuti nelle variabili F1 ed F2

If @@F1 = @@F2 then... // Vengono confrontati gli indirizzi


di memoria delle variabili F1 ed F2

Come i tipi pointer una variabile procedurale può contenere il valore nil indicando
pioè che la non punta a niente. Effettuare una chiamata ad una variabile procedurale
puntante a nil provoca un errore. Per verificare se un variabile procedurale non
contiene il valore nil si utilizza la funzione Assigned che restituisce false qualora la
variabile contenga il valore nil.

Il costrutto

If Assigned(NomeEvento) then NomeEvento(Parametri)

viene molto utilizzato durante la stesura dei componenti per testare se ad una
proprietà evento è stata assegnata un procedura per la gestione dell'evento stesso
ed evitare quindi di generare errori.

Tipi variant
I tipi variant, come dice il nome stesso, sono tipi che possono variare ovvero una
variabile di tipo variant può assumere valori appartenenti a tipi diversi. Vengono
utilizzati quando non si può determinare in fase di compilazione il tipo che verrà
assegnato alla variabile. Questi tipi offrono una grande flessibilità ma occupano più
memoria e rallentano l'elaborazione. Si ha anche maggiore probabilità di provocare
errori runtime poiché non è possibile individuarli al momento della compilazione
proprio perché non hanno un tipo specifico.

Vengo utilizzate soprattutto negli oggetti COM, OLE. Il valore speciale Null indica
valori sconosciuti o mancanti. I tipi variant occupano 16 byte di memoria e al
momento della loro creazione vengono inizializzati al valore Unassigned.

Per una trattazione approfondita di questo tipo si rimanda ai manuali del linguaggio.

http://programmazione.html.it/delphi/delphi_16.htm (3 di 4) [21/05/2002 11.22.08]


DELPHI: Le variabili

LEZIONE 12: Le variabili

Cos'è una variabile probabilmente lo sapete già; si tratta in definitiva di segnaposto


per accedere, sia il lettura che scrittura, a una sepcifica locazione di memoria. Per
ogni variabile è necessario specificare il tipo di dati che questa conterrà.

In Object Pascal, una variabile viene dichiarata con il costrutto

Var NomeVariabile : Tipo;

Si possono anche definire contemporaneamente più variabili dello stesso tipo


specificando al posto del NomeVariabile un elenco di nomi validi separati da virgole.

Quando si effettuano dichiarazioni consecutive di variabili, non occorre ripetere la


parola chiave Var.

Var I : Integer;

A, B : Real;

Nome : String;

Le variabili all'interno di un programma si dividono in globali e locali. Le variabili


locali sono quelle variabili definite all'interno di procedure o funzioni, mentre le
globali sono tutte le altre. Al momento dalla loro dichiarazione, le variabili globali
possono essere inizializzate fornendo un valore dopo la loro definizione.

Var I : Integer = 10;

Se queste non vengono esplicitamente inizializzate, il compilatore le inizializza tutte


a 0. Non è possibile fornire un valore di inizializzazione la dove si è utilizzata una
dichiarazione multipla di variabili come nell'esempio precedente per le variabili A, B.
A differenza delle variabili globali, le variabili locali non possono essere inizializzate
ne vengono inizializzate dal compilatore; esse conterranno dei valori non definiti,
casuali.

Quando si dichiara una variabile, si riserva della memoria sufficiente a contenere un


dato del tipo spcificato per la variabile. Questa memoria viene liberata solamente
quando il programma non fa più uso di quelle variabili. Per le varibili locali questo
avviene solamente quando l'esecuzione del programma esce dalla funzione o
procedura in cui sono state dichiarate.

In Object Pascal è possibile dichiarare variabili che facciano riferimento a


determinate locazioni di memoria, tecnica utilizzata fondamentalmente per la scrittura
di drivers di dispoditivi. Per dichiarare una variabile di questo tipo occorre specificare
dopo il tipo la parola chiave absolute e l'indirizzo di memoria a cui deve fare
riferimento.

http://programmazione.html.it/delphi/delphi_17.htm (1 di 2) [21/05/2002 11.22.14]


DELPHI: Le variabili

Var CrtMode : Byte absolute $0040;

Dichiarazioni di variabili di questo tipo non possono contenere inizializzazioni.

Non sempre le variabili occorrono essere create al momento della dichiarazione.


Potrebbe essere il caso di liste di valori create dinamicamente a run-time. Per creare
varaibili a run-time, ovvero variabili dinamiche, esistono diverse funzioni che allocano
memoria per un certo tipo di variabile e ritornano un puntatore per accedere a quella
locazione di memoria. Queste funzioni sono GetMem, New. Queste variabili vengono
allocate sullo heap e non vengono gestite automaticamente; ciò significa che una
volta che queste non sono più necessarie occorre liberare la memoria da loro
occupata con le funzioni FreeMem, se la variabile è stata creata con GetMem,
oppure con Dispose se la variabile è stata creata con New.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_17.htm (2 di 2) [21/05/2002 11.22.14]


DELPHI: Le costanti

LEZIONE 12: Le costanti

Come in molti altri linguaggi anche in Object Pascal possono essere definite delle
costanti. Alcune costanti sono definite dal linguagio stesso, come abbiamo visto per
le costanti true, false e nil altre sono, diciamo per natura, costanti come i numerali e
i letterali stringa, per esempio 'Hello World!'. Esistono due tipi di costanti in Object
Pascal, le costanti vere e quelle con tipo.

Una costante vera è un identificatore il cui valore, dichiarato al momento della


dichiarazione della costante, non può cambiare. Il tipo della costante, viene definito
automaticamente dall'espressione presente alla destra dell'operatore "=".

Const Retry = 5;

Per dichiarare una costante occorre specificare un nome di identificatore valido dopo
la parola chiave Const, seguito dal segno di uguale ed una espressione costante che
ne identifica il valore. Per espressione costante si intende una espressione valutabile
dal compilatore senza bisogno di eseguire il programma. Sono espressioni costanti i
numerali, le stringhe di caratteri, le costanti vere, i valori di tipi enumerati, le costanti
speciali ed espressioni costruite con questi elementi. Ovviamente non possono
comparire in questo tipo di espressioni delle variabili od altri elementi non calcolabili
senza eseguire il programma. Sono invece ammesse le seguenti funzioni predefinite:

Abs, Exp, Length, Ord, Sqr, Addr, Frac, Ln, Pred, Sqrt, ArcTan, Hi, Lo, Round, Succ,
Chr, High, Low, Sin, Swap, Cos, Init, odd, SizeOf, Trunc.

Le costanti con tipo si comportano in maniera differente dalle variabili vere.


Queste possono contenere elementi come array, record, procedurali, pointer. Con le
impostazioni standard, il compilatore permette di assegnare alle costanti con tipo
valori nuovi in fase di esecuzione del programma. Questo comportamento può
essere modificato con la direttiva del compilatore {$J-}, per disattivarlo, {$J+} per
riattivarlo. Una costante con tipo può essere dichiarata come segue

Const NomeCostante : Tipo = Valore;

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_17a.htm [21/05/2002 11.22.19]


DELPHI: Procedure e Funzioni

LEZIONE 17: Procedure e Funzioni

A differenza di altri linguaggi, come il C o il C++, l'Object Pascal fa distinzione tra


funzioni e procedure. Queste sono definite a livello generale come routines o
subroutines. Esse sono blocchi di codice indipendenti che vengono utilizzate per
centralizzare del codice che necessita di essere utilizzato più volte in punti diversi del
programma. Ciò permette di ridurre la quantità di codice da scrivere e riduce il
numero di errori.

L'Object Pascal distingue tra funzioni, che restituiscono dei valori, e le procedure
che non restituiscono alcun valore.

Per dichiarare una funzione o una procedura è nessario indicarne in nome, ed i


parametri ed i rispettivi tipi che verranno passati ad essa, il blocco di codice che
implemeterà le funzionalità della routine. Nel caso di una funzione, occorre anche
indicare un tipo per il valore restituito. Ecco la sintassi per la dichiarazione di funzioni
e procedure

Procedure

Per dichiarare un procedura si utilizza la sintassi seguente

Procedure NomeProcedura(ListaParametri);

DichiarazioniLocali;

Begin

CodiceProcedura;

End;

dove NomeProcedura è un nome valido di identificatore, ListaParametri è una lista


di parametri e relativi tipi, DichiarazioniLocali sono dichiarazioni di variabili, tipi,
altre funzioni o procedure locali alla procedura stessa e CodiceProcedura il codice
principale della procedura.

La lista dei parametri può essere formata da un elenco di identificatori separati da


virgole e dal tipo a cui appartengono; può essere un elenco di identificatori di tipo
diverso accompagnati dai rispettivi tipi, separati da punti e virgola; una combinazione
delle possibilità precedenti. Strutturalmente

Elenco di identificatori dello stesso tipo:


A, B, C : Integer

Elenco di identificatori di tipo diverso:


A : Integer; B : Real; C : String

Una combinazione dei precedenti:


A, B : Integer; C : String

I parametri passati ad una procedura possono essere di due tipi: per riferimento o
per valore.

http://programmazione.html.it/delphi/delphi_18.htm (1 di 2) [21/05/2002 11.22.23]


DELPHI: Procedure e Funzioni

Per passare un parametro per valore è sufficiente utilizzare la sintassi


precedentemente vista. Per passare parametri per riferimento occorre specificare la
praola chiave Var prima del nome dell'identificatore che rappresenta il parametro da
passare per riferimento.

Procedure MiaProcedura(A, B : Integer; Var C : Real);

Nell'esempio, A e B sono passate per valore, ovvero come costanti, C per


riferimento. I parametri passati per valore sono costanti e partecipano
all'elaborazione in localmente ovvero, modifiche a loro apportate non vengono
trasferite all'esterno della procedura. Questo perchè al momento della chiamata alla
procedura viene allocata memoria per questi parametri e viene liberata all'uscita della
procedura provocando la perdita dei valori in essi contenuti.

Il passaggio per riferimento, permette di trasmettere le modifiche effettuate sul valore


del parametro per riferimento all'estreno della procedura una volta terminata la sua
esecuzione. Con riferimento all'esempio precedente, si potrebbe pensare alla
MiaProcedura come una procedura che restituisca il risultato della divisione di A per
B attraverso il parametro C. In questo caso A, B sono locali alla procedura mentre C
viene restituito alla fine dell'esecuzione.

In alcuni casi, dopo la dichiarazione della procedura è necessario indicare una


direttiva che specifichi il comportamento a livello di chiamata della procedura. Ciò è
necessario ad esempio nelle librerie a collegamento dinamico, dove per permettere a
programmi scritti in linguaggi differenti dal Delphi, si utilizza la direttiva StdCall che
garantisce la compatibilità del passagio di parametri.

Per un approfondimento delle direttive si rimanda alla documentazione del


linguaggio.

Funzioni

Le dichiarazioni di funzioni sono in tutto e pertutto uguali alle dichiarazioni di


procedure tranne per il fatto che per le prime occorre specificare il tipo del valore
restituito.

Function NomeFunzione(ListaParametri) : TipoRestituito; DichiarazioniLocali; Begin


CodiceFunzione; End;

Per assegnare il valore da restituire, si possono seguire due strade: una è assegnare
il valore al nome della funzione; l'altra è utilizzare la variabile Result.

Esempio

Function Bisestile(Anno : Integer) : Boolean; Begin ... Bisestile := ...; End;

oppure

Function Bisestile(Anno : Integer) : Boolean; Begin ... Result := ...; End;

il risultato è lo stesso.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_18.htm (2 di 2) [21/05/2002 11.22.23]


DELPHI: Gestire il flusso: le istruzioni condizionali

LEZIONE 19: Gestire il flusso: le istruzioni condizionali

Durante l'esecuzione del codice, è possibile modificarne il percorso di esecuzione


tramite istruzioni condizionali o cicliche.

Le istruzioni condizionali ci permettono di effettuare delle scelte tra differenti strade


da seguire per proseguire nell'esecuzione del codice.

Le istruzioni cicliche ci permettono di ripetere alcune istruzioni o gruppi di istruzioni


più volte senza la necessità che queste vengano scritte nel codice tante volte quante
devono essere eseguite.

L'Object Pascal dispone di due tipi di istruzioni condizionali e tre tipi di istruzioni
cicliche che sono rispettivamente: istruzione IF, CASE, REPEAT, FOR, WHILE.

Le istruzioni condizionali

Le istruzioni condizionali presenti in Object Pascal sono due: IF, CASE.

La prima istruzione consente di effettuare una scelta in base ad una espressione


condizionale e permette di scegliere tra due blocchi di istruzioni da eseguire.
L'espressione condizionale che definisce il blocco da eseguire deve restituire
necessariamente un valore booleano: vero o falso. Questo ci fa capire anche perchè
questa istruzione ci permette di scegliere solamente due strade da seguire.

La sintassi dell'istruzione IF è la seguente

If Espressione then

BloccoEspressioneVera

else

BloccoEspressioneFalsa

Per blocco ovviamente si intende un insieme di istruzioni racchiuse tra le parole


chiave Begin..End, ma potrebbe anche trattarsi di una sola istruzione ed in tal caso
l'uso delle parole chiave Begin..End sarebbe facoltativo. Una importante precisazione
va fatta per quanto riguarda la riga di codice che precede la parola ELSE; questa
infatti non deve mai terminare con il punto e virgola (;). La ragione di ciò sta nel fatto
che il punto e virgola indica la fine di una istruzione di codice ed in questo caso
l'istruzione comprende anche la parola ELSE percui il punto e virgola andrà inserito
solamente alla fine dell'istruzione condizionale. Qualora si inserisse il punto e virgola
prima della parola ELSE, il compilatore lo segnalerebbe in fase di compilazione del
codice.

N.B.

All'interno del blocco di codice del ramo THEN, tutte le istruzioni devono terminare
con il punto e virgola!

Vediamo degli esempi per chiarire le idee

http://programmazione.html.it/delphi/delphi_19.htm (1 di 5) [21/05/2002 11.22.29]


DELPHI: Gestire il flusso: le istruzioni condizionali

If A < B then

Begin

A := A + 1;

B := B - 1;

End //Notate la mancanza del punto e virgola

Else

Begin

B := B + 1;

A := A - 1;

End;

If A < B then

Scrivi('A è minore di B')

Else

Scrivi('A è maggiore di B');

Il ramo ELSE è facoltativo quindi potremmo avere istruzioni IF con solamente il ramo
THEN.

If A < B then

A := A + 1;

Si possono avere anche istruzioni IF nidificate che possono portare qualche difficoltà
in più. Un esempio di istruzione IF nidificata potrebbe essere la seguente

If A > B then

If A < 4 then

Scrivi('A è maggiore di B e minore di 4')

Else

Scrivi('A è maggiore di B ma maggiore di 4');

Non c'è alcun segnale che indichi al compilatore come considerare i blocchi di
istruzioni all'interno di questo costrutto. Il codice precedente infatti potrebbe essere
interpretato in due modi differenti

If A > B then [If A < 4 then


Scrivi('A è maggiore di B e minore di 4') Else
Scrivi('A è maggiore di B ma maggiore di 4')];

oppure

http://programmazione.html.it/delphi/delphi_19.htm (2 di 5) [21/05/2002 11.22.29]


DELPHI: Gestire il flusso: le istruzioni condizionali

If A > B then [If A < 4 then


Scrivi('A è maggiore di B e minore di 4')] Else
Scrivi('A è maggiore di B ma maggiore di 4');

Il compilatore segue sempre la divisione dell'istruzione nel primo modo.

Per evitare problemi con le istruzioni IF nidificate è consigliabile racchiudere in


blocchi Begin..End il codice da eseguire, come è stato evidenziato in precedenza con
le parentesi quadre. Nel primo caso avremmo

If A > B then

Begin

If A < 4 then

Scrivi('A è maggiore di B e minore di 4')

Else

Scrivi('A è maggiore di B ma maggiore di 4')

End;

nel secondo

If A > B then

Begin

If A < 4 then

Scrivi('A è maggiore di B e minore di 4')

End

Else

Scrivi('A è maggiore di B ma maggiore di 4');

Ovviamente il codice può essere indentato come si vuole, potrebbe anche essere
scritto tutto su una linea ma per motivi di leggibilità l'indentazione del codice che
trovate in queste lezioni è quella da me utilizzata nel codice che scrivo normalmente
e ritengo che sia sufficientemente leggibile.

L'istruzione CASE si differenzia dalla precedente istruzione poichè permette di


scegliere tra più di due blocchi di codice. Ciò è dettato dal fatto che che l'espressione
condizionale è di tipo ordinale e quindi potrò seguire tante strade differenti quanti
sono i valori del tipo ordinale.

La sintassi dell'istruzione CASE è la seguente

http://programmazione.html.it/delphi/delphi_19.htm (3 di 5) [21/05/2002 11.22.29]


DELPHI: Gestire il flusso: le istruzioni condizionali

Case EspressioneOrdinale of

Caso1 : BloccoIstruzioni1;

Caso2 : BloccoIstruzioni2;

Caso3 : BloccoIstruzioni3;

...

CasoN : BloccoIstruzioniN;

Else

BloccoIstruzioniCasoGenerale;

End;

Occhio al tipo di espressione che deve essere di tipo ordinale quindi non possiamo
utilizzare tipi string come espressioni condizionali per l'istruzione CASE come
accade per alcuni altri linguaggi.

Funzionalmente, l'esecuzione dell'istruzione CASE provoca un salto alla riga di


codice contente alla sinistra dei due punti il valore restituito dall'espressione
condizionale. Ad esempio

Case N of //dove N contiene il valore 3

1 : ...;

2 : ...;

3 : Begin //Questo è il codice che verrà eseguito

...

End;

4 : ..;

Else

Scrivi('Valore non valido');

End;

Se il valore restituito dall'espressione condizionale non è presente nella lista di valori


dell'istruzione CASE, viene eseguito il codice contenuto nel ramo ELSE. Ciò è utile
per gestire tutti i casi che non rientrano nell'elenco fornito nell'istruzione come visto
nell'esempio precedente.

Anche nell'istruzione CASE il ramo ELSE è facoltativo ma in caso di omissione,


bisogna fare attenzione al valore assunto dall'espressione condizionale specificata;
qualora il valore restituito da questa non fosse presente nella lista dei valori possibili,
l'esecuzione del codice continuerebbe dall'istruzione immediatamente successiva
all'istruzione CASE.

L'istruzione CASE è equivalente ad un condizionale IF annidato

Come valori alla sinistra dei due punti possono essere specificati anche elenchi di
valori

http://programmazione.html.it/delphi/delphi_19.htm (4 di 5) [21/05/2002 11.22.29]


DELPHI: Gestire il flusso: le istruzioni condizionali

Case N of

1, 2, 4 : ...;
//Blocco di istruzioni valido per il valori 1, 2, e 4

3 : ...;

5 : ..;

End;

oppure

Case N of

1..4 : ...;
//per tutti quei valori che cadono nell'intervallo 1-4
//estremi compresi

5 : ...;

End;

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_19.htm (5 di 5) [21/05/2002 11.22.29]


DELPHI: Gestire il flusso: le istruzioni cicliche

LEZIONE 20: Gestire il flusso: le istruzioni cicliche

Le istruzioni cicliche

Object Pascal mette a disposizione tre tipi di istruzioni per il controllo dei cicli: FOR,
WHILE, REPEAT. Sono istruzioni che si differenziano molto l'una dall'altra per quanto
riguarda sintassi e logica di esecuzione.

L'istruzione FOR richiede la specificazione del numero di ripetizioni da effettuare. La sua


sintassi è la seguente

For Contatore := ValoreIniziale to ValoreFinale do

BloccoIstruzioniDaRipetere;

oppure

For Contatore := ValoreIniziale downto ValoreFinale do

BloccoIstruzioniDaRipetere;

L'istruzione For..To assegna inizialmente a Contatore il valore specificato in


ValoreIniziale ed esegue il BloccoIstruzioniDaRipetere incrementando (nel caso di
For..To) o decrementando (nel caso di For..downto) dopo ogni esecuzione il valore di
Contatore fino a raggiungere il valore indicato in ValoreFinale.

È scontato ricordare che i valori di ValoreIniziale e di ValoreFinale devono essere


compatibili con il tipo di Contatore.

Quando il valore di Contatore raggiunge il valore indicato in ValoreFinale l'esecuzione


prosegue eseguendo ancora una volta il BloccoIstruzioniDaRipetere e quindi
quest'ultimo viene eseguito in totale tante volte quanti sono i valori contenuti
nell'intervallo ValoreIniziale-ValoreFinale. Per esempio

For I := 1 to 10 do

...

eseguirà il codice contenuto nel blocco da ripetere per 10 volte.

Ci possono essere dei casi in cui il ciclo For non venga eseguito neanche una volta e ciò
può capitare quando il ValoreIniziale è maggiore (nel caso di For...To) o minore (nel
caso di For...downto) di ValoreFinale.

All'interno del ciclo For è possibile utilizzare il valore che la variabile contatore assume
ad ogni iterazione

http://programmazione.html.it/delphi/delphi_20.htm (1 di 3) [21/05/2002 11.22.38]


DELPHI: Gestire il flusso: le istruzioni cicliche

For I := 1 to 10 do

Begin

A := 11 - I;

Writeln('Valore di A:' + IntToStr(A));

End;

Il codice in esempio restituisce la stampa a video dei seguenti valori: 10, 9, 8, 7, 6, 5, 4,


3, 2, 1.

For I := 10 downto 1 do

Begin

A := 11 - I;

Writeln('Valore di A:' + IntToStr(A));

End;

Il codice precedente restituisce la stampa a video dei seguenti valori: 1, 2, 3, 4, 5, 6, 7, 8,


9, 10.

Attenzione al valore del contatore al termine del ciclo For; questo ha volre non definito
quindi non è consigliabile utilizzare il valore in esso contenuto fuori dal blocco di
istruzioni da ripetere.

Il valore delle espressioni ValoreIniziale e ValoreFinale vengono valutate solamente


una volta ed all'inizio dell'esecuzione del ciclo. Ne deriva che l'esecuzione del ciclo non è
influenzata da possibili alterazioni dei valori citati come invece può accadere nei cilci
WHILE.

Il ciclo WHILE si differenzia da ciclio for per il tipo di espressione di controllo e per la
valutazione della stessa che avviene all'inizio di ogni iterazione. La sintassi dell'istruzione
WHILE è la seguente

While Espressione do

BloccoIstruzioniDaRipetere;

L'espresione dell'istruzione While deve essere di tipo boolean e l'esecuzione del codice
prosegue fintanto che l'espressione Espressione è verificata, ovvero restituisce True. Si
dice che l'istruzione While ripete per Vero. L'esecuzione del codice termina quando
l'espressione Espressione assume valore False.

Esempio

While A < B do

Inc(A);

Il codice precedente porta all'esecuzione dell'istruzione Inc(A) tante volte quante ne sono
necessarie per portare il valore di A ad essere uguale al valore di B.

http://programmazione.html.it/delphi/delphi_20.htm (2 di 3) [21/05/2002 11.22.38]


DELPHI: Gestire il flusso: le istruzioni cicliche

Nel caso in cui A sia maggiore od uguale a B il ciclo non viene eseguito neanche una
volta.

L'istruzione REPEAT si differenzia dal WHILE fondamentalmente per la valutazione


dell'espressione di controllo. Questa viene infatti verificata dopo ogni esecuzione e la
ripetizione del ciclo avviene finchè l'espressione restituisce un valore False. Si dice che
l'istruzione Repeat ripeta per Falso. La sua sintassi è la seguente

Repeat

BloccoIstruzioniDaRipetere;

Until Espressione;

Espressione deve essere come per il While di tipo boolean e affinchè il ciclo venga
ripetuto deve assumere valore False; quando assume valore True, il ciclo termina.
Caratteristica interessante da notare, è che l'istruzione Repeat viene eseguita almeno
una volta al contrario dell'istruzione While. Questo perchè, appunto, l'espressione di
controllo viene valutata alla fine del ciclo.

Riscrivendo l'esempio precedente per l'istruzione While utilizzando l'istruzione Repeat


avremo

Repeat

Inc(A);

Until A = B;

A differenza dell'istruzione While,questa volta anche se A è già uguale a B prima


dell'esecuzione del ciclo, dopo l'esecuzione di quest'ultimo A assumerà comunque valore
A + 1. Ammettendo che A prima del ciclo abbia valore 5 e B abbia valore 5 dopo il ciclo
avremo che A assumerà comunque il valore 6 appunto per il fatto che l'istruzione Repeat
viene eseguita almeno una volta.

Il flusso delle istruzioni di ciclo, può essere controllato tramite due altre istruzioni ovvero
Break e Continue. La prima interrompe l'esecuzione del ciclo in cui si trova, la seconda
riprende l'esecuzione dall'istruzione successiva nella sequenza del ciclo stesso.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_20.htm (3 di 3) [21/05/2002 11.22.38]


DELPHI: Classi ed Oggetti: introduzione

LEZIONE 22: Classi ed Oggetti: introduzione

Classi ed Oggetti

Una classe è una struttura composta da campi, metodi e proprietà, detti anche
componenti o membri. Una istanza di una classe è detta oggetto.

Un campo è assimilabile ai campi di un tipo record e rappresenta gli elementi di dati


presenti ciascuna istanza di classe. Un campo è come una varaibile che appartiene
all'oggetto e può essere di qualsiasi tipo. Questi devono trovarsi prima di qualsiasi
dichiarazione di proprietà o di metodo. Essi sono collegati in modo statico ed i relativi
collegamenti sono determinati in fase di compilazione.

Un metodo è una procedura o funzione che è associata ad una classe e può operare
su oggetti (istanze di classe) o sul tipo class stesso. Quest'ultimi vengono chiamati
metodi di classe. All'interno di una dichiarazione di classe, essi appaiono come
intestazioni di funzioni o procedure. Questo dovrà poi essere implementato in una
qualsiasi parte del modulo in cui è stata dichiarata la classe di appartenenza con una
definizione di dichiarazione all'interno della sezione implementation del modulo. Nella
definizione della dichiarazione del metodo, viene riportato il nome del metodo così
come è stato dichiarato nella dichiarazione di classe, preceduto dal nome della
classe stessa separati da un punto.

Es. Function MyClass.GetWidth : Integer;

Una proprietà è assimilabile ad un campo tranne per il fatto che differisce


nell'implementazione. La proprietà infatti è soprattutto una interfaccia verso i dati
contenuti nei campi dell'oggetto, mascherando le modalità di acesso ad essi. Infatti
queste hanno degli specificatori di accesso che definiscono la maniera in cui i dati
vengono scritti o letti nella proprietà.

Tutti gli oggetti vengono allocati dinamicamente nella memoria, utilizzando strutture
caratteristiche dipendenti dalla definizione del tipo class di appartenenza. Ogni
oggetto ha la sua copia univoca dei campi definiti nella classe, tutte le istanze di una
classe condividono gli stessi metodi.

Per la creazione e distruzione degli oggetti, esistono dei metodi speciali definiti
Costruttori e Distruttori.

In definitiva, una variabile di tipo class è effettivamente un puntatore ad un oggetto. A


differenza dei normali puntatori, questi non necessitano della dereferenziazione per
accedere ai metodi, proprietà e campi del tipo class a cui appartiene l'oggetto. Non è
necessario scrivere MioOggetto^.MioMetodo, ma è sufficiente scrivere
MioOggetto.MioMetodo.

Prima di essere istanziato, un tipo class deve essere dichiarato. La dichiarazione di


tipi class può avvenire solamente nella sezione Type di livello più alto del programma
o della unit. Pertanto non è possibile dichiarare tipi class nella sezione di
dichiarazione di funzioni o procedure. La sintassi per la dichiarazione di un tipo class
è la seguente:

Type NomeClasse = Class(ClasseAntenato)

ElencoMembri

End;

http://programmazione.html.it/delphi/delphi_21.htm (1 di 2) [21/05/2002 11.23.19]


DELPHI: Classi ed Oggetti: introduzione

NomeClasse è ovviamente un qualsiasi identificatore riconosciuto valido dal


compilatore; ClasseAntenato indica la classe da cui eredità la nuova classe e può
essere facoltativo, con il risultato che in tal caso la classe erediterà direttamente dalla
classe predefinita TObject; ElencoMembri contiene l'elenco di tutti i campi, proprietà
e metodi che andranno costituire la nuova classe. Quest'ultimo, può essere vuoto ed
in tal caso è possibile omettere la parola chiave end.

La struttura di ElencoMembri è simile a quella vista nella dichiarazione di tipi record


con alcune differenze riguardanti il campo di visibilità dei membri, la dichiarazione di
proprietà (non presenti nei tipi record) e nella dichiarazione dei metodi.

Una dichiarazione di classe, organizza i suoi membri in tre, quattro o cinque sezioni
definite come campi di visibilità. Queste sezioni indicano infatti il campo di visibilità
dei membri in esse contenuti. I campi di visibilità disponibili sono: Private, Protected,
Public, Published, Automated.

Tutti i membri che si trovano all'interno di una sezione Private sono invisibili
dall'esterno della unit o programma in cui la classe viene dichiarata. Vengono
normalmente dichiarati in questa sezioni i campi della classe.

Tutti i membri che si trovano all'interno di una sezione Protected sono visibili ed
accessibili all'interno della unit o programma in cui la classe viene dichiarata e
all'interno di qualsiasi classe discendente indipendentemente dal modulo in cui è
dichiarata.

Tutti i membri che si trovano all'interno di una sezione Public sono visibili ed
accessibili da qualsiasi punto sia possibile referenziare la classe stessa.

Tutti i membri che si trovano all'interno di una sezione Published hanno la stessa
visibilità dei membri Public con la differenza che il compilatore genera per questi le
informazioni RTTI (Informazioni Runtime di Tipo) che lo stesso Delphi utilizza in fase
di sviluppo per accedere per esempio alle proprietà dall'Object Inspector. Queste
informazioni infatti permettono ad un'applicazione di interrogare i campi per
ottenerene in modo dinamico i metodi. Non tutti i tipi di dati possono essere utilizzati
in una sezione Published; tipi validi sono: tipo ordinal, string, class, interface,
puntatori a metodi. Anche i tipi Set possono essere utilizzati a condizione che questi
possano essere contenuti in un Byte o in una Word o in una DoubleWord.

Tutti i membri che si trovano all'interno di una sezione Automated hanno la stessa
visibilità dei membri Public con la differenza che le informazioni di tipo Automation
vengono generate solamente per i membri automated. Per la trattazione di questo tipi
di sezione si rimanda alla guida del linguaggio.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_21.htm (2 di 2) [21/05/2002 11.23.19]


DELPHI: Classi ed Oggetti: i metodi

LEZIONE 23: Classi ed Oggetti: i metodi

Metodi

Abbiamo visto in precedenza cosa è e come viene definito un metodo. Ora vediamo
di approfondire l'argomento. In Object Pascal ci sono due parole chiave che rientrano
nella creazione ed utilizzo dei metodi; queste sono Inherited e Self.

Inherited permette di richiamare i metodi dell'antenato da cui deriva la classe a cui


appartiene il nuovo metodo. Per meglio dire, se la classe da cui abbiamo ereditato
definisce un metodo MioMetodo scrivendo il codice seguente richiamo quel metodo

Procedure MiaClasse.MioMetodo(Valore : Integer);

Begin

Inherited;

End;

Ciò permette di aggiungere funzionalità al metodo di base ereditato.

Non specificando nessun identificatore dopo la parola riservata Inherited, questa


farà riferimento al metodo dell'antenato avente lo stesso nome del metodo in cui si
trova la parola chiave Inherited. Se dopo di essa non viene specificato alcun
parametro, verranno passati al metodo ereditato gli stessi parametri passati al
metodo della classe che si sta definendo.

Se invece la parola Inherited è seguita da un identificatore di metodo, questa


effettuerà una chiamata al metodo corrispondente all'identificatore indicato
ricercandolo a partire dall'antenato immediato.

La parola chiave Self permette di fare riferimento, all'interno dell'implementazione di


un metodo, all'oggetto dal quale il metodo viene chiamato.

Collegamento dei metodi

I metodi possono essere di tre tipi: statici, virtuali, dinamici. Quando un metodo viene
definito, per impostazione predefinita è statico.

Nella chiamata di un metodo statico, l'implementazione da attivare viene determinata


in base al tipo, dichiarato in fase di compilazione, della classe che viene utilizzata per
la chiamata del metodo.

Quindi per esempio, avendo le dichiarazioni di classi seguenti

http://programmazione.html.it/delphi/delphi_22.htm (1 di 5) [21/05/2002 11.23.25]


DELPHI: Classi ed Oggetti: i metodi

Type TClasse1 = Class

Procedure Metodo1;

End;

TClasse2 = Class(TClasse1)

Procedure Metodo1;

End;

ed eseguendo il seguente codice

Var Classe1 : TClasse1;

Classe2 : TClasse2;

Begin

Classe1 := TClasse1.Create;

Classe1.Metodo1;
//L'implementazione utilizzata è quelle di TClasse1

Classe1.Destroy;

Classe1 := TClasse2.Create;

Classe1.Metodo1;
//L'implementazione utilizzata è quelle di TClasse1

TClasse2(Classe1).Metodo1;

Classe1.Destroy;

Classe2 := TClasse2.Create;
//L'implementazione utilizzata è quelle di TClasse2

Classe2.Metodo1;

Classe2.Destroy;

End;

avremo che l'esecuzione della seconda chiamata "Classe1.Metodo1" determinerà


l'esecuzione dell'implementazione del metodo nella classe "TClasse1" anche se la
variabile "Classe1" fa riferimento ad un oggetto di tipo "TClasse2".

I metodi dichiarati virtual o dynamic possono essere ridefiniti nelle classi discendenti.
Per dichiarare dei metodi come virtual o dynamic occorre specificare dopo la loro
dichiarazione le parole chiave virtual o dynamic. A differenza dei metodi static la
determinazione dell'implementazione del metodo da utilizzare nella chiamata viene
determinata in base al tipo run-time. Per ridefinire un metodo virtual o dynamic
occorre specificare, nella classe discendente, un metodo con lo stesso nome del
metodo della classe antenato e utilizzare la parola chiave override dopo la sua
dichiarazione.

Apportando le modifiche del caso, riprendiamo l'esempio visto in precedenza per i tipi
static e vediamo le differenze

http://programmazione.html.it/delphi/delphi_22.htm (2 di 5) [21/05/2002 11.23.25]


DELPHI: Classi ed Oggetti: i metodi

Type TClasse1 = Class

Procedure Metodo1;

End;

TClasse2 = Class(TClasse1)

Procedure Metodo1; override;

End;

Var Classe1 : TClasse1;

Classe2 : TClasse2;

Begin

Classe1 := TClasse1.Create;

Classe1.Metodo1;
//L'implementazione utilizzata è quelle di TClasse1

Classe1.Destroy;

Classe1 := TClasse2.Create;

Classe1.Metodo1;
//L'implementazione utilizzata è quelle di TClasse2

TClasse2(Classe1).Metodo1;

Classe1.Destroy;

Classe2 := TClasse2.Create;

Classe2.Metodo1;
//L'implementazione utilizzata è quelle di TClasse2

Classe2.Destroy;

End;

Come ripostato nel commento del codice, la seconda chiamata a "Classe1.Metodo1"


utilizzerà l'implementazione di "TClasse2" poichè il tipo run-time al momento della
chiamata è "TClasse2".

Le differenze tra metodi virtuali e dinamici consiste essenzialmente


nell'implementazione dellq chiamata al metodo in fase di esecuzione. I metodi viruali
sono ottimizzati per velocità, mentre i metodi dinamici per dimensione.

Un'altro tipo interessante di metodo e quello dei metodi astratti. I metodi astratti
permettono di definire metodi senza alcuna implementazione del metodo stesso. Ciò
risulta utilie quando si vogliono creare delle classi base che stabiliscano l'interfaccia
base dei metodi ma che permettano alle classi discendenti di definire
l'implementazione di quei metodi. Questo tipo di metodo è un particolare tipo di
metodo astratto o dinamico.

Per dichiarare un metoto astratto, occorre specificare dopo le parole chiave virtual o
dynamic la praola chiave abstract.

Altri due tipi di metodi importanti nella programmazione ad oggetti sono i metodi
costruttori e distruttori. Questi si occupano, come dicono le parole stesse, di creare
ed inizializzare un'istanza di classe o di distruggere e liberare la memoria occupata
dall'istanza di classe precedentemente creata.

I metodi costruttori sono caratterizzati dalla parola chiave constructor al posto della
parola chiave procedure o function. L'implementazione di questi metodi, prevede la
creazione dell'istanza di classe allocando memoria nello heap, ed l'inizializzazione
automatica a zero dei valori ordinal, a nil dei campi puntatore e classe, l'azzeramento

http://programmazione.html.it/delphi/delphi_22.htm (3 di 5) [21/05/2002 11.23.25]


DELPHI: Classi ed Oggetti: i metodi

di tutti i campi string. Solitamente all'interno di un metodo costruttore viene prima di


tutto chiamato il metodo costruttore ereditato dall'antenato per inizializzare il codice
comune, dopo di che viene eseguito il codice necessario ad inizializzare il codice
specificao per la classe discendente. Potrebbe capitare che durante l'esecuzione del
codice di inizializzazione si generino delle eccezioni; in tal caso verrà eseguito
automaticamente il distruttore destroy per disturggere l'oggetto non completato. Nella
creazione di un oggetto, occorre chiamare il metodo costruttore facendolo precedere
dal nome della classe di appartenenza dell'oggetto che si vuole creare. In tal modo, il
metodo costruttore restituirà, al termine della chiamata, un puntatore all'istanza
appena creata. Se il metodo costruttore viene chiamato utilizzando un riferimento ad
un oggetto, questo non restituisce nulla.

I metodi distruttori seguono le stessa regola dichiarativa dei metodi costruttori tranne
per il fatto che utilizzano la parola chiave destructor al posto di constructor. I metodi
distruttori si occupano di distruggere l'oggetto e deallocare la memoria da esso
occupata. Per chiamare un metodo distruttore bisogna fare riferimento ad un oggetto
istanza come per esempio

MioOggetto.Destroy;

La chiamata al metodo destroy produce l'immediata esecuzione del codice contenuto


nell'implementazione del metodo destroy stesso. All'interno del metodo distruttore
viene eseguito tutto quel codice atto ad eliminare eventuali altre istanze di oggetti
incorporati e nella liberazione della memoria da loro occupata. L'ultima istruzione di
un metodo destroy , è solitamente una chiamata al metodo destroy ereditato in
maniera da eliminare i campi e ripulire la memoria occupata dal codice ereditato.
Come detto in precedenza, se durante la creazione di un oggetto viene sollevata una
eccezione, viene eseguito il metodo destroy. In tal caso, questo metodo deve essere
pronto ad interagire con codice non completamente inizializzato che, in particolare
per i campi di tipo class, significa controllare se il cmapo ha valore Nil prima di
effettuare la chiamata al metodo destroy della classe di quel campo. Per questo
Delphi mette a disposizione il metodo Free definito nella classe TObject che permette
di gestire in automatico il controllo sulla presenza di valori Nil prima di eseguire la
distruzione dell'oggetto.

Gestori di messaggi

Un altro tipo di metodo messo a disposizione in Delphi è il gestore di messaggi. Un


gestore di messaggi sono metodi che rispondono ai messaggi inviati da Windows, da
altre applicazioni o dall'applicazione che stiamo realizzando. Esistino messaggi
predefiniti e messaggi che possono essere dichiarati dallo sviluppatore.

Per dichiarare un gestore di messaggi, si include la direttiva message dopo la


dichiarazione del metodo seguito da un identificatore integer costante di messaggio il
cui valore deve essere compreso tra 1 e 49151. Alcuni dei messaggi utilizzati da
Windows e quelli utilizzati dai controlli della VCL sono definiti nella unit messages.
Nella definizione di ogni messaggio è necessario dichiarare una costante contenente
l'ID del messaggio ed una struttura (un record) che descriva il contenuto del
messaggio. Ecco la dichiarazione di un gestore di messaggio er il messaggio
WM_CHAR

Type TTextBox = Class(TCustomControl)

Private

Procedure WMChar(Var Message : TWMChar); message WM_CHAR;

...

End;

Il codice seguente, ripreso dal manuale del linguaggio, mostra come implementare il
gestore di evento dichiarato in precedenza affinchè venga eseguito un codice
particolare alla pressione del tasto invio in un controllo di tipo TTextBox e
l'esecuzione del codice ereditato per tutti gli altri tasti

http://programmazione.html.it/delphi/delphi_22.htm (4 di 5) [21/05/2002 11.23.25]


DELPHI: Classi ed Oggetti: i metodi

Procedure TTextBox.WMChar(Var Message : TWMChar);

Begin

If Chr(Message.ChrCode) = #13 then

ProcessEnter

Else

Inherited;

End;

Tramite l'istruzione Inherited viene ricercato nelle classi antenato una


implementazione del gestore per quel messaggio e qualora non ne sia disponibile
una, viene eseguito il metodo gestore predefinito DeafultHandler definito nella classe
TObject. Per richiamare un gestore di messaggio, si utilizza il metodo Dispatch
dichiarato nella classe TObject. Questo metodo richiede che gli venga passato un
record il cui primo campo sia di tipo cardinal e contenga l'ID del messaggio.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_22.htm (5 di 5) [21/05/2002 11.23.25]


DELPHI: Classi ed Oggetti: le proprietà

LEZIONE 24: Classi ed Oggetti: le proprietà

Le proprietà sono simili ai campi di un oggetto. La differenza tra i campi e le proprietà


sta nel fatto che i primi fanno riferimento a delle locazioni di memoria mentre le
seconde possono definire delle azioni per l'accesso in lettura e scrittura del loro
contenuto. La sintassi di dichiarazione di una proprietà è il seguente

Property NomeProprieta[Indici]:
Tipo index CostanteIntera Specificatori;

NomeProprieta è, neanche a dirlo, un identificatore riconosciuto valido che identifica la


proprietà.

Indici è facoltativo ed è utilizzato nella dichiarazione di proprietà di tipo array ed indica


una sequenza di dichiarazioni di parametri del tipo identificatore1, identificatore2 : tipo
separate da punti e virgola.

Anche Index CostanteIntera è facoltativo e serve definire degli specificatori di indice


utili per condividere gli stessi metodi di accesso durante la rappresentazione di valori
differenti.

Specificatori è una sequenza dei seguenti specificatori: read, write, stored, default,
nodefault, implements. In ogni dichiarazione di proprietà è necessario indicare
almeno uno specificatore read o write.

N.B. Non è possibile utilizzare nelle proprietà l'operatore @ ne possono essere passate
come parametri var; questo perchè non è detto che la proprietà esista in memoria, la
potrebbe aver un metodo read che recupera il valore per esempio da un database o da
altra struttura di dati.

Per gli specificatori di accesso read e write possiamo dire che questi possono fare
riferimento a metodi o a campi. Nel caso di metodi, questi dovranno essere definiti
nella stessa classe in cui è stata dichiarata la proprietà. Per lo specificatore read nel di
un metodo, questo dovrà essere una funzione senza parametri il cui tipo restituito sia
dello stesso tipo della proprietà. Per i metodi relativi allo specificatore write, questi
dovranno essere procedure che abbiano un solo parametro d'ingresso dello stesso tipo
della proprietà.

Quello che segue è un esempio do dichiarazione di proprietà

Property TimeOut : Cardinal read GetTimeOut write SetTimeOut;

La dichiarazione dei metodi degli specificatori read e write è la seguente

Function GetTimeOut : Cardinal;

Procedure SetTimeOut(Value : Cardinal);

Il codice riportato potrebbe far parte di un controllo che implementi al suo interno un
timer di tipo TTimer che permette di impostare e leggere il valore di intervallo del timer
stesso. Avendo la classe TTimer una sua proprietà Interval i metodi dichiarati sopra
potrebbo essere implementati come segue considerando che l'istanza della classe
TTimer si chiami FTimer

http://programmazione.html.it/delphi/delphi_23.htm (1 di 3) [21/05/2002 11.23.32]


DELPHI: Classi ed Oggetti: le proprietà

Function TMioControllo.GetTimeOut : Cardinal;

Begin

Result := FTimer.Interval;

End;

Procedure TMioControllo.SetTimeOut(Value : Cardinal);

Begin

If Value <> FTimer.Interval then

FTimer.Interval := Value;

End;

Quando tra gli specificatori in una dichiarazione di proprietà compare solamente lo


specificatore read, la proprietà sarà di sola lettura e di sola scrittura nel caso compaia
solamente lo specificatore write. Un esempio di dichiarazione di proprietà sola lettura

Property TimeElapsed : Boolean read FTimeElapsed;

Come visto all'inizio della trattazione delle proprietà, queste possono contenere nella
dichiarazione dei parametri aggiuntivi come una sequenza di dichiarazione di
parametri. In questo caso abbiamo a che fare con proprietà indicizzate che possono
rappresentare elementi di un elenco (TStringList.Items), controlli figli o i pixel di una
bitmap.

Ecco alcune dichiarazioni di proprietà array (dal manuale del linguaggio)

Property Objects[Index : Integer] :


TObject read GetObject write SetObject;

Property Pixels[X, Y : Integer] :


TColor read GetPixel write SetPixel;

Property Values[const Name : string] :


string read GetValue write SetValue;

Il formato dell'elenco di parametri è lostesso di quello utilizzato nella dichiarazione di


procedure e funzioni eccezion fatta per le parentesi quadre al posto delle tonde.
Abbiamo praticamente a che fare con degli array dove però mentre nel vero tipo array
gli indici possono essere di tipo ordinal solamente per quanto riguarda le proprietà
array questi possono essere di qualsiasi tipo.

Le proprietà array non ammettono negli specificatori di accesso dei campi, ma


solamente dei metodi. Questo per via degli indici che vengono automaticamente
passati al metodo che si occuperà di recuperare o di scrivere i dati all'indice richiesto.
Un metodo per lo specificatore read deve necessariamente essere una funzione che
accetta lo stesso numero e tipo di parametri elencati nella lista degli indici della
proprietà, passatigli nello stesso ordine; in oltre il tipo restituito dalla funzione dovrà
essere dello stesso tipo della proprietà. Il metodo dello specificatore write dovrà
essere una procedura che accetti come parametri lo stesso numero e tipo di parametri
elencati nella lista degli indici della proprietà, passatigli nello stesso ordine ed in più il
classico parametro valore contenente il valore da settare nella proprietà. Ecco alcuni
esempi di metodi per proprietà array che fanno riferimento ai metodi dell'esempio
precedente

http://programmazione.html.it/delphi/delphi_23.htm (2 di 3) [21/05/2002 11.23.32]


DELPHI: Classi ed Oggetti: le proprietà

Function GetObject(Index : Integer) : TObject;

Function GetPixel(X, Y : Integer) : TColor;

Function GetValue(const Name : String) : String;

Procedure SetObject(Index : Integer; Value : TObject);

Procedure SetPixel(X, Y : Integer; Value : TColor);

Procedure SetValue(const Name, Value : String);

Le proprietà array possono essere accedute come classicamente si accede alla


locazione di un array nel seguente modo

Canvas.Pixel[5, 45] := clGreen;

quando si definisce una proprietà array, si ha la possibilità di impostarla come proprietà


predefinita della classe a cui appartiene. Ciò permette di accedere alla proprietà
specificando immediatamente dopo il nome della classe l'elenco di indici. Per diefinire
una proprietà predefinita per un classe occorre specificare dopo la dichiarazione della
proprietà la direttiva default. Ad esempio avendo una dichiarazione del tipo

Type TStringArray = Class

Public

Property Strings[Index : Integer] : String ...; default;

...

End;

Supponendo che l'istanza della classe TStringArray si chiami StringArray potremmo


accedere alla proprietà Strings della classe nel seguente modo

StringArray[10] := 'Carlo';

al posto di

StringArray.Strings[10] := 'Carlo';

Per ovvi motivi, una classe può avere solamente una proprietà predefinita.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_23.htm (3 di 3) [21/05/2002 11.23.32]


DELPHI: Classi ed Oggetti: gli specificatori

LEZIONE 25: Classi ed Oggetti: gli specificatori

Abbiamo visto che Delphi permette di specificare anche altri parametri nella
dichiarazione delle proprietà di una classe e tra questi ci sono gli specificatori di
indice che permettono la condivisione dello stesso metodo a più proprietà. L'indice
serve, appunto, a distinguere per quale propietà il metodo sta lavorando. Infatti
l'indice specificato nella dichiarazione della proprietà viene automaticamente passato
al metodo che decidarà le istruzioni da eseguire in base al valore dell'indice. Per
definire uno specificatore di indice occorre indicare nella dichiarazione della
proprietà, dopo il tipo della proprietà stessa, la direttiva index seguita da una
costante integer. Anche per gli specificatori di indice per gli specificatori di accesso
possono essere definiti solamente dei metodi che per lo specificatore di accesso
read devono contenere come ultimo parametro passato alla funzione un parametro
aggiuntivo di tipo integer e per lo specificatore write deve essere una procedura che
tra i parametri passatigili contenga un ulteriore parametro, sempre di tipo integer,
posizionato però dal secondo all'ultimo posto nella lista dei parametri e comunque
prima del parametro contenente il valore da assegnare alla proprietà.

Gli specificatori di memorizzazione

Gli specificatori di memorizzazione servono ad indicare a Delphi come gestire il


salvataggio delle informazioni RTTI , ovvero se salvare o meno i valori delle proprietà
published nei file form (.DFM). Questi sono: stored, default, nodefault e sono
specificatori facoltativi.

La direttiva stored indica a Delphi se il valore di quella proprietà deve essere salvato
o no nel file form. Questa deve essere seguita dal valore boolean True o False. Se
non viene specificato alcun specificatore stored Delphi salva automaticamente il
valore della proprietà.

La direttiva default indica a Delphi quale è il valore di di default per quella proprietà
se nel file form non è specificato alcun valore. Abbiamo visto l'uso di questa direttiva
nelle proprietà array, ma in questo caso si comporat diversamente. Infatti in questo
caso va seguita da una costante dello stesso tipo della proprietà; lo specificatore di
default è valido solamente per i tipi ordinal e set sempre che i loro valori massimi e
minimi siano compresi tra 0-31. Se nella dichiarazione della proprietà non viene
indicata ne la direttiva default o nodefault, Delphi considera come se fosse stata
utilizzata la direttiva nodefault.

Operatori di classe

Delphi mette a disposizione degli opreratori speciali per operare sulle classi. Questi
operatri utilizzano dei metodi definiti nel tipo TObject e che vengono ereditati da ogni
classe. Questi operatori sono is e as e fanno uso dei metodi derivati da TObject,
ClassType, ParentClass, InheritsFrom.

L'operatore is esegue il controllo dinamico del tipo di un oggetto in fase di


esecuzione per verificare la classe attuale dell'oggetto stesso. L'utilizzo dell'operatore
is è il seguente

Oggetto is Classe

Questa espressione restituisce True se l'istanza di Oggetto è un'istanza della classe


Classe o da uno dei suoi discendenti. Se Oggetto vale nil il risultato della valutazione
è False. Il tipo di Oggetto e Classe devono essere correlati fra loro ovvero essere
uno l'antenato dell'altro, altrimenti in fase di compilazione viene generato un errore.

L'operatore as serve ad eseguire conversioni di tipo ovvero dato un'istanza di

http://programmazione.html.it/delphi/delphi_24.htm (1 di 2) [21/05/2002 11.23.38]


DELPHI: Classi ed Oggetti: gli specificatori

Oggetto ed un classe Classe l'espressione

Oggetto as Classe

restituisce un riferimento all'oggetto Oggetto ma con il tipo indicato da Classe. Come


per l'operatore is, l'istanza Oggetto e la classe Classe devono essere correlati ovvero
essere uno l'antenato dell'altro altrimenti si avrà la generazione di un errore di
compilazione. In fase di esecuzione Oggetto deve essere un'istanza della classe
Classe o nil pena il sollevamento di un'eccezione. Un esempio dell'utilizzo
dell'operatore as

(Sender as TLabel).Caption := 'Elaborazione in corso...';

Le regole di precedenza dell'operatore as richiedono a volte che l'intera espressione


contenente l'operatore venga racchiusa tra parentesi. Nessun problema comunque
se si utilizza normalmente questa pratica per evitare errori e perdita di tempo nel
debug del codice.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_24.htm (2 di 2) [21/05/2002 11.23.38]


DELPHI: Gestione degli errori: Eccezioni

LEZIONE 26: Gestione degli errori: Eccezioni

Gli errori in Delphi vengono gestiti attraverso le eccezioni. Quando si verifica un


errore in fase di esecuzione, viene sollevata un'eccezione. Un eccezione non è altro
che un oggetto che contiene informazioni sull'errore (come per esempio il messaggio
di errore) che vengono passate ad un gestore di eccezioni. Ciò permette di
distinguere la logica di esecuzione del codice da quella che gestisce gli errori. Ogni
volta che si utilizza la unit SysUtils, tutti gli errori di runtime vengono trasformati
automaticamente in eccezioni. Esistono in Delphi diverse classi di eccezioni
predefinite, ma è anche possibile definirne di nuove.

La dichiarazione di una eccezione è uguale alla dichiarazione di un normale classe.


In genere tutte le eccezioni derivano dalla classe Exception definita nella unit
SysUtils. Come per una normale classe, è possibile raggruppare le eccezioni
utilizzando l'ereditarietà. Le seguenti dichiarazioni sono prese dalla unit SysUtils

Type EMathError = Class(Exception)

EInvalidOp = Class(EMathError);

EZeroDivide = Class(EMathError);

Proprio come in una normale classe, all'interno della dichiarazione di una eccezione
possono essere dichiarati dei campi che possono contenere informazioni addizionali
sull'errore come per esempio il codice dell'errore.

Type EInOutError = Class(Exception)

ErrorCode : Integer;

End;

Per creare un oggetto eccezione occorre utilizzare la seguente sintassi

Raise Oggetto at Indirizzo

oppure

Raise Oggetto.Create at Indirizzo

All'interno dell'istruzione raise occorre secificare un oggetto eccezione oppure


chiamare il costruttore della classe eccezione. Il parametro Indirizzo può contenere
un puntuntatore ad una procedura o funzione, utile per sollevare l'eccezione dal
punto dello stack precedente a quello in cui si è verificata l'eccezione. Sia il
parametro Oggetto che Indirizzo sono facoltativi. Se non viene indicato nessun
parametro dopo l'istruzione raise si avrà come risultato la rigenerazione
dell'eccezione attiva. Quando viene sollevata un'eccezione tramite l'istruzione raise il
controllo dell'esecuzione viene trasferito al gestore di eccezioni, ricercandolo
partendo dal gestore più interno alla classe data (ad esempio un blocco
Try...Except). Le eccezioni generate vengono automaticamente distrutte dopo
essere state gestite; quindi non si deve mai eliminare manualmente l'eccezione
creata.

http://programmazione.html.it/delphi/delphi_25.htm (1 di 3) [21/05/2002 11.23.43]


DELPHI: Gestione degli errori: Eccezioni

Try...Except

L'istruzione Try...Except permette di definire un gestore di eccezioni. Il formato


dell'istruzione è

Try

Codice

Except

GestioneEccezioni

End;

In pratica il codice eseguito tra le parole Try ed Except è protetto dal sollevamento
delle eccezioni. Qualora un'eccezione fosse sollevata all'interno del codice Codice il
controllo dell'esecuzione viene passato al codice rappresentato da
GestioneEccezioni.

Il blocco GestioneEccezioni può essere composto da una sequenza di istruzioni o da


una sequenza di gestori di eccezioni. Per definire un gestore di eccezione per una
certa eccezione, occorre utilizzare la seguente sintassi

On Identificatore : TipoClasseEccezione do Codice

Identificatore è facoltativo e permette di definire un riferimento all'oggetto eccezione


che è valido solamente per il codice contenuto nel blocco Codice.

TipoClasseEccezione indica invece il tipo della classe a cui corrisponde l'eccezione


da gestire.

Codice è il blocco di codice che viene eseguito se il tipo dell'eccezione sollevata


corrisponde al tipo indicato in TipoClasseEccezione o ne è un antenato.

Facoltativamente può essere definito dopo l'elenco dei gestori di eccezione un blocco
else che gestica tutte le eccezioni non gestite dai gestori definiti.

Ovviamente, se non si verisicano eccezioni durante l'esecuzione del codice Codice


l'esecuzione prosegue fino all'istruzione precedente la parola except e quindi salta
all'istruzione immediatamente successiva alla parola chiave end.

Ecco due esempi che raccolgono ciò che abbiamo visto

Try

...

Except

On EZeroDivide do GestisciZeroDivideException;

On EOverFlow do GestisciOverFlowException;

On EMathError do GestisciMathErrorException;

Else

GestisciTutteLeAltreEccezioni;

End;

Try

...

http://programmazione.html.it/delphi/delphi_25.htm (2 di 3) [21/05/2002 11.23.43]


DELPHI: Gestione degli errori: Eccezioni

Except

GestisciLeEccezioni;

End;

Try...Finally

Il blocco Try...Finally assicura che una determinata sequenza di codice venga


eseguita indifferentemente dal fatto che sia stata sollevata un'eccezione o no. Per
esempio è utile in alcuni casi assicurarsi di liberare le risorse occupate in ogni caso.
La sintassi è simile a quella di Try..Except

Try

Codice

Finally

CodiceDaEseguireComunque

End;

L'esecuzione del codice procede dalla prima istruzione del blocco di codice Codice.
Se si verificano eccezioni, l'esecuzione del codice Codice viene interrotta e riprende
con la prima istruzione contenuta nel blocco CodiceDaEseguireComunque. Al
termine dell'esecuzione del codice CodiceDaEseguireComunque, l'eccezione viene
sollevata di nuovo.

Se non si verifica alcuna eccezione, l'esecuzione procede fino all'ultima istruzione nel
codice Codice e prosegue con l'esecuzione del codice contenuto nel blocco
CodiceDaEseguireComunque.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_25.htm (3 di 3) [21/05/2002 11.23.43]


DELPHI: La VCL (Visual Component Library)

LEZIONE 27: La VCL (Visual Component Library)

Questa sezione del corso tratterà della libreria di componenti (visibili e non) e delle
classi messeci a disposizione da Delphi. Questa libreria prende il nome di VCL da
Visual Component Library. Essa è una gerarchia di classi che derivano tutte dalla
classe TObject. Di questa libreria fanno parte sia i componenti visibili nella
componente palette dell'IDE che le altre classi di uso generale.

Come già detto, tutte le classi della VCL discendono da TObject. Questa classe ha
dunque un'importanza particolare poichè ci permette per esempio di utilizzare il tipo
TObject in sostituzione di un qualsiasi altro tipo di classe. Ciò è visibile per esempio
nelle implementazioni dei gestori di eventi che contengono nella loro dichiarazione un
parametro (Sender) di tipo TObject che permette appunto il passaggio di qualsiasi
classe. Affianco a questo vantaggio, c'è lo svantaggio di dover conoscere il tipo
dell'oggetto per operare su di esso. Infatti, avendo un riferimento di tipo TObject ad
un qualsiasi oggetto, non è possibile accedere direttamente alle proprietà specifiche
di quell'oggetto. Come sempre però Delphi ci viene in contro e ci mette a
disposizione dei metodi per risalire al tipo dell'oggetto riferito. Questi metodi sono
implementati nella calsse TObject e quindi disponibili per tutti gli oggetti di qualsiasi
classe. Questo è l'elenco dei metodi: ClassName, ClassNameIs, ClassParent,
ClassInfo, ClassType, InheritsFrom, InstanceSize.

Vediamo a cosa servono questi metodi.

● ClassName: questo metodo resituisce il nome della classe in formato stringa


● ClassNameIs: verifica che il nome della classe corrisponda a quello
passatogli
● ClassParent: questo metodo restituisce un riferimento alla classe base
● ClassInfo: Restituisce un puntatore ai dai Run Time Type Information relativi
alla classe
● ClassType: può essere applicato solamente agli oggetti e restituisce un
riferimento alla classe dell'oggetto
● InheritsFrom: verfica se una classe deriva sia in maniera diretta che non
dalla classe passatagli come parametro. Questo metodo viene chiamato
anche quando si utilizza l'operatore is.
● InstanceSize: metodo molto interessante in quanto restituisce la dimensione
dell'oggetto a run time altrimenti non reperibile. Infatti l'istruzione SizeOf
restituirebbe la dimensione del riferimento all'oggetto, quindi la dimensione di
un puntatore che rimane fissa a 4 byte.

ClassParent e ClassType sono molto importanti poichè ci permettono di operare sulla


classe a run time.

La gerarchia delle classi di Delphi può essere raggruppata in tre aree principali:
oggetti in genere, componenti, eccezioni. I componenti sono quelli che generalmente
si usano in maniera visuale nell'IDE di Delphi, tramite il Form Designer e l'Object
Inspector. Tutte le altre classi sono accessibili tramite codice.

Componenti

I componenti rappresentano il fulcro della programmazione visuale in Delphi. Quando


si programma in Delphi, normalmente non si fa altro che scegliere dei componenti e
definirne le interazioni tra loro.

La maggior parte di questi componenti sono accessibili tramite l'IDE di Delphi nella
Component Palette, ma ce ne sono alcuni accessibili solamente da codice come
TForm e TApplication. Tutti i componenti derivano da TComponent che a sua volta
deriva da TPersistent. Ciò permette all'oggetto di essere memorizzato nel file DFM
grazie alle funzionalità di streaming della classe TPersistent. Questi possono essere
manipolati a Design Time in maniera visuale tramite gli strumenti messi a
disposizione dall'IDE di Delphi. I componenti possono contenere proprietà di tipo

http://programmazione.html.it/delphi/delphi_26.htm (1 di 3) [21/05/2002 11.23.49]


DELPHI: La VCL (Visual Component Library)

Published che verranno visualizzate nell'Object Inspector dell' IDE.

Abbiamo visto che la gerarchia delle classi di può dividere in tre aree; anche per i
componenti è possibile fare un raggruppamento.

I componenti si dividono in

● Controlli o Componenti visuali


● Componenti non visibili

I Controlli possono a loro volta dividersi in

● Controlli Windows
● Controlli Grafici

I controlli sono tutte quelle classi che derivano da TControl e cha hanno le
caratteristiche di occupare una posizione specifica nello schermo, avere dimensioni
proprie, essere manipolati a Design Time.

I controlli Windows o windowed, sono tutti quei componenti visuali che utilizzano
finestre del sistema operativo per la loro visualizzazione. Tecnicamente, questi
controlli possiedono un Handle assegnato loro dal sistema operativo e derivano da
TWinControl. Per quanto riguarda la funzionalità, tutti questi controlli, possono
ricevere il fuoco e, in alcuni casi, contenere altri controlli. Appartengono a questa
area, tutti i controlli che rappresentano i tipici controlli di windows, come Edit Box,
ListBox, ComboBox, ecc. Questi controlli sono in effetti delle classi wrapper pre i
common controls ci windows. Windows infatti possiede dei componenti che vengono
chiamati controlli. Questi controlli sono, tecnicamente, una finestra che mostra un
comportamento specifico e possiede alcuni stili. Altra caratteristica importante di
questi controlli è che sono in grado di rispondere ai messaggi specifici inviati loro dal
sistema operativo. Molti di questi controlli sono stati ereditati da Windows 3.1 e nelle
versioni successive, da Windows 95 in poi, ve se ne sono affiancati molti altri.

I controlli grafici o non windowed, non possiedono una finestra e quindi non hanno un
Handle. Sono sempre controlli visuali e la loro caratteristica è che non possono
ricevere il fuoco. Sono molto importanti quando si ha la necessità di risparmiare
risorse. Derivano tutti da TGraphicControl e vengono gestiti direttamente dalla form
che li contiene la quale invia loro tutti gli eventi relativi al disegno ad al mouse.

I componenti non visibili sono tutte quelle classi che non sono, per l'appunto, visibili e
che derivano quindi da TComponent ma non da TControl. A design time questi si
presentano sotto forma di icone mentre a run time possono essere visibili o no come
per esempio i box di dialogo per l'apertura ed il salvataggio dei files.

Dalla struttura della gerarchia di classi che abbiamo visto, deriva che molti
componenti anno proprietà comuni; queste riguardano soprattutto proprietà fisiche
degli oggetti (dimensioni, colore, stili, cursore, posizione), il nome dell'oggetto stesso
(name), l'owner ed il parent. Quest'ultimi due hanno particolare importaza poichè
rappresentano il proprietario dell'oggetto ed il contenitore dello stesso. La differenza

http://programmazione.html.it/delphi/delphi_26.htm (2 di 3) [21/05/2002 11.23.49]


DELPHI: La VCL (Visual Component Library)

tra Owner e Parent è importante; l'owner è chi ha creato l'oggetto ed il parent è il


contenitore dell'oggetto. Un esempio potrebbe essere una form di nome Form1 che
crea un componente Button1. In questo caso la form Form1 è sia Owner che Parent.
Se la form Form1 spostasse il componente Button1 nella form Form2, quest'ultima
sarebbe il parent di Button1, ma Form1 ne rimarrebbe sempre proprietaria. Anche la
proprietà name è importantissima; essa infatti permette di identificare l'oggetto ed
accedere ai suoi metodi e proprietà. Viene utilizzata anche dall'IDE per definire
automaticamente i nomi dei gestori degli eventi. Questa proprietà deve essere
conforme alle specifiche definite per qualsiasi identificatore Object Pascal valido. Non
può quindi contenere spazi o caratteri non validi.

Oltre a proprietà comuni, gli oggetti della VCL hanno anche eventi e metodi comuni.
Risultando troppo lunga la trattazione di questo argomento in questa sede, si
rimanda al testo di Marco Cantù "Programmare con Delphi 5".

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_26.htm (3 di 3) [21/05/2002 11.23.49]


DELPHI: Le interfacce grafiche!

LEZIONE 28: Le interfacce grafiche!

Tramite Delphi costruire un'interfaccia grafica è di una semplicità e velocità estrema.


È sufficiente selezionare i componenti da utilizzare dalla component palette e
sistemarli sulle schede (Forms).

Quando si crea un nuovo progetto, Delphi crea automaticamente una scheda detta
principale (MainForm) che sarà la prima ad essere creata in fase di esecuzione.
Aggiungendo altre schede al progetto, si ha la possibilità di decidere quale tra questa
sarà la scheda principale. Per default Delphi crea in fase di esecuzione tutte le
schede definite in fase di progetto a meno che non venga indicato altrimenti. In base
all'applicazione da realizzare si decide se occupare o no memoria con schede che
magari non hanno un uso continuo ed in questo caso vengono eliminate dalla liste
delle schede da creare automaticamente e si procederà poi tramite codice a creare
quest'ultime al momento opportuno. Per decidere quali schede creare
automaticamente in esecuzione, scegliere dal menu "Project" la voce "Options...";
solitamente, la finestra che compare visualizza per prima il foglio riguardante le
Forms ed è qui che possizmo decidere, spostando dalla lista di sinistra a quella di
destra le forms da creare manualmente o vice versa. Sempre tramite questa finestra
è possibile selezionare la form che sarà la MainForm dell'applicazione. Per creare
manualmente una form tramite codice utilizzare il codice seguente

Application.CreateForm(TForm1, Form1);

Ovviamente TForm1 e Form1 andranno sostituiti secondo le proprie esigenze.

Insieme alla classe TForm che rappresenta le schede (o le finestre) dell'applicazione,


ci sono due altre classi di rilevante importanza: TApplication e TScreen. La prima
raccoglie le proprietà fondamentali di un'applicazione, come l'icona, il titolo
dell'applicazione, il file di help associato, eventi riguardanti l'applicazione stessa
come OnActivate, OnMessage, OnIdle ed altri. La classe TScreen incapsula le
caratteristiche dello schermo sul quale viene eseguita l'applicazione. TScreen
permette anche di recuperare l'elenco dei monitor e le loro dimensioni in caso di
utilizzo di più monitor per la visualizzazione.

Proviamo a costruire una semplice applicazione con una semplicissima interfaccia


grafica.

Creiamo un nuovo progetto, se nell'IDE non ve ne è già uno nuovo, scegliendo dal
menu "File" il comando "New Application". Avremo così visualizzata una scheda
vuota pronta per ricevere i componenti per l'interfaccia grafica.

Selezioniamo la form nominata Form1 cliccandovi una volta ed andiamo ad


impostare le sue dimensioni nell'Object Inspector; assegnamo alla proprietà height il
valore 250 e alla proprietà width il valore 350.

Ora prendiamo dalla Component Palette (vedi lezioni riguardanti l'IDE) un


componente Button cliccando sull'icona e mettiamolo nella nostra form
cliccandoci semplicemente sopra. Vedremo il componente posizionato nella form nel
punto in cui abbiamo cliccato. Posizioniamolo al centro della form facendo clic con il
tasto destro del mouse sul componente Button appena piazzato e scegliamo il
comando "Align".

http://programmazione.html.it/delphi/delphi_27.htm (1 di 5) [21/05/2002 11.23.57]


DELPHI: Le interfacce grafiche!

Nella finestra che compare , nel gruppo "Orizontal" e "Vertical" scegliamo "Center in
Window"

Scegliendo "OK" il nostro bottone verrà automaticamente centrato nella form.

Ora modificahiamo il testo visualizzato sul tasto modificando, con lo stesso


procedimento utilizzato per modificare le dimensioni della form, la proprietà Caption
impostandola a "Press me".

Ora scegliendo il metodo che più vi è comodo tra quelli visti nelle lezioni riguardanti
l'ambiente di sviluppo, associamo del codice all'evento che si attiverà alla pressione
del nostro bottone; in questo caso, sapendo che l'evento predefinito per il
componente TButton è l'evento OnClick faccio doppio click su di esso perchè Delphi
crei il codice base dell'evento.

Scriviamo il seguente codice nel blocco del gestore dell'evento OnClick appena
creato

MessageDlg('Hai premuto un tasto', mtInformation, [mbOk], 0);

In questo modo alla pressione del bottone verrà visualizzata una finestra di dialogo

http://programmazione.html.it/delphi/delphi_27.htm (2 di 5) [21/05/2002 11.23.57]


DELPHI: Le interfacce grafiche!

modale recante il messaggio "Hai premuto un tasto". Da notare che la natura modale
della finestra di dialogo visualizzata arresta l'esecuzione del programma fintanto che
l'utente non ha premuto il buttone "OK".

Eseguiamo il codice appena scritto premendo il tasto rapido F9 oppure cliccando sul
tasto Bnt_Run.jpg
posto nella bara degli strumenti dell'IDE, oppure ancora scegliendo il
(718"Run" dal menu "Run".
comando
byte)
Ecco il risultato del nostro breve e semplice lavoro

Ora apportiamo una modifica al codice per dimostrare che l'esecuzione del codice si
arresta fino a che non viene premuto il bottone "OK" nella finestra di dialogo.

Aggiungiamo dopo la riga di codice scritta in precedenza la seguente riga

Beep;

Questa riga farà emettere un beep, o dalla scheda audio o dallo speaker del PC a
seconda della configurazione del sistema, dopo che avrete premuto il tasto "OK"
nella finestra di dialogo.

Proviamo ora ad aggiungere un'altro componente e a d aggiungere altro codice.


Aggiungiamo un componente Label che utilizzeremo per visualizzare il risultato di
una nostra elaborazione. Con lo stesso procedimento seguito per aggiungere alla
form il componente Button, aggiungiamo il componente Label rappresentato nella
Component Palette con l'icona . Ora modifichiamo la sua posizione all'interno
della form impostando per la proprietà Left il valore16 e per la proprietà Top il valore
16. Ora modifichiamo il testo della Label impostando la sua proprietà Caption a
"Elapsed time (ms):". Prendiamo un altro componente Label e posizioniamolo a 104,
proprietà Left, 16, proprietà Top. Lasciamo la proprietà Caption così come è. Ecco
come si presenta la nostra form dopo queste modifiche.

Ora aggiungiamo del codice all'evento OnClick del nostro bottone, per calcolare il
tempo che trascorre tra la visualizzazione della finestra di dialogo e la chiusura della
stessa dopo che l'utente ha premuto il tasto "OK" contenuto in essa.

http://programmazione.html.it/delphi/delphi_27.htm (3 di 5) [21/05/2002 11.23.57]


DELPHI: Le interfacce grafiche!

Modifichiamo il codice del gestrore dell'evento OnClick del bottone nel modo
seguente:

procedure TForm1.Button1Click(Sender: TObject);


Var Start, Elapsed : Integer;
begin
Start := GetTickCount;
MessageDlg('Hai premuto un tasto', mtInformation, [mbOk], 0);
Elapsed := GetTickCount - Start;
Label2.Caption := IntToStr(Elapsed);
Beep;
end;

La funzione GetTickCount è una funzione di Windows che restituisce il numero di


millisecondi trascorsi dall'avvio di Windows stesso. In Delphi questa funzione si trova,
insieme a tutte o quasi le altre funioni di windows, nella unit "windows.pas". La
variabile Start conterrà il numero di millisecondi trascorsi dall'avvio di Windows fino al
momento precedente la chiamata alla funzione MessageDlg. Elapsed ci serve per
contenere il numero di millisecondi trascorsi dal momento predente la chiamata alla
funzione MessageDlg alla chiusura della stessa. Il calcolo è molto semplice ed il
risultato è quello della differenza tra il numero di millisecondi trascorsi dall'avvio di
Windows e il numero di millisecondi trascorsi un momento prima di chiamare la
funzione MessageDlg. La penultima riga assegna, effettuando una conversione da
intero a stringa, il risultato dell'eleborazione alla proprietà Caption dell'etichetta
nominata Label2.

Aggiungiamo anche un gestore per l'evento OnShow della form nel quale puliamo il
valore della proprietà Caption di Label2. Dopo aver selezionato la form e tramite la
pagina Events dell'Object Inspector creato il gestore per l'evento OnShow dovremmo
inserire il codice seguente (badate non l'intestazione della procedura, solo la riga di
codice tra Begin e End; il resto dovrebbe averlo creato automaticamnte Delphi
tramite l'Object Inspector)

procedure TForm1.FormShow(Sender: TObject);


begin
Label2.Caption := '';
end;

Ora eseguiamo il programma. Ecco il risultato dell'esecuzione del programma.


Provate ad aprire a chiudere la finestra di dialogo più volte e vedrete il valore
riportato vicino a Label1 cambiare.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_27.htm (4 di 5) [21/05/2002 11.23.57]


DELPHI: Approfondimento sulle Forms

LEZIONE 29: Approfondimento sulle Forms

Dopo l'esempio di semplicissima applicazione realizzato nella sezione riguardante le


interfacce grafiche, vediamo ora di approfondire un po' il discorso sulle forms
presentandone le proprietà i metodi e gli eventi più interessanti.

Proprietà

Come abbiamo visto le forms sono principalmente dei contenitori per gli altri oggetti
dell'interfaccia grafica. Alucne delle loro proprietà permettono di adattarle alle
esigenze del progettista. Una form è per esempio caratterizzata da un titolo. Questo
titolo può essere impostato sia a design time che a run time agendo sulla proprietà
Caption della form. La proprietà Caption è una delle proprietà comuni a molti dei
componenti della VCL e fa riferimento di solito a testo che viene visualizzato come
intestazione del componente stesso (TLabel, TForm, TButton, ecc). Nella barra del
titolo, dove compare appunto il titolo della form, compaiono anche dei pulsanti il
comportamento è quello standard di tutte le finestre di Windows: massimizzare la
finestra, minimizzarla, chiuderla. Vi è anche un altro pulsante che non sempre
compare ed è quello dell'help contestuale. I Delphi, è possibile decidere quali
pulssanti visualizzare e quali no agendo sulla proprietà BorderIcons della form. La
proprietà BorderIcons è di tipo Set e può contenere tutti, alcuni o nessuno dei
pulsanti standard. Tramite questa proprietà è anche possibile decidere se disattivare
o no il menu di sistema che compare cliccando con il mouse sull'icona della finestra
in alto a sinistra (o tramite tastiera); questo contiene l'elenco dei comandi che
possono essere applicati alla finestra, come la massimizzazione, minimizzazione, la
chiusura, ecc. Tramite codice è anche possibile aggiungere comandi speciali a
questo menu. Da notare che le voci di menu rispecchiano lo stato dei pulsanti sulla
destra; percui se il tasto minimizza è disattivato, lo sarà anche la relativa voce nel
menu di sistema.

Tra le caratteristiche di una form c'è anche il bordo. Per tipo di bordo in Delphi si
intende la funzione che il bordo avrà. Una Form può avere un bordo che permetta di
ridimensionare la finestra, può non avere bordo (per esempio una splash form), può
avere un bordo non ridimensionabile, può avere un bordo che dia alla form le
caratteristiche tipiche di una finestra di dialogo (come quella utilizzata nell'esempio
delle interfacce grafiche) oppure il bordo può fare assumere alla form altre
caratterische come per esempio cambiare il tipo di finestra in ToolWindow sia
ridimensionabile che non (le tool window sono quelle finestre utilizzate da molti
software di grafica per contenere gli strumenti di lavoro, pennelli, colori, ecc).

Una proprietà molto importante che influisce sul comportamento della finestra è la
proprietà FormStyle che permette di definire se la form sarà una finestra di una
applicazione SDI (Single Document Interface), come per esempio il Notepad di
windows, oppure comportarsi come una finestra di un'applicazione MDI (Multiple
Document Interface) come Word. In questo caso abbiamo due tipi di finestre MDI una
finestra detta frame (cornice) che il contenitore per le altre form definite Children.
L'impostazione di default quando si crea una nuova Form è fsNormal che indica che
la finestra si comporterà come SDI. C'è anche un'altro valore che può essere
impostato per questa propietà ed è fsStayOnTop che indica che la finestra sarà
visualizzata sempre al disopra di tutte le altre. Ritorneremo sulle applicazioni SDI ed
MDI più avanti dove approfondiremo il discorso e realizzeremo degli esempi.

http://programmazione.html.it/delphi/delphi_28.htm (1 di 4) [21/05/2002 11.24.21]


DELPHI: Approfondimento sulle Forms

Un form è caratterizzata oltre ad una dimensione, anche da una posizione. Per


posizionare un Form nello schermo è sufficiente modificare i valori delle proprietà
Top e Left della form. Queste sono le coordinate espresse in pixels relative all'angolo
superiore dello schermo ed all'angolo superiore sinistro della form. Vi è un'altra
proprietà che influisce sul posizionamento della form nello schermo, è Position i cui
valori validi sono: poDefault, poDefaultPosOnly, poDefaultSizeOnly,
poDesktopCenter, poMainFormCenter, poOwnerFormCenter, poScreenCenter. Con
poDesigned la form appare posizionata nello schermo e con la stessa larghezza ed
altezza definita a design time. Con poDefault la form appare nello schermo in una
posizione e con delle dimensioni definite da Windows. Ad ogni avvio
dell'applicazione, la form si muove verso il basso e verso destra. Con
poDefaultPosOnly la form viene visualizzata con le dimensioni con cui è stata creata
a design time, ma la posizione è definita da Windows. Ad ogni avvio
dell'applicazione, la forma si sposta verso il basso e verso destra. Con
poDefaultSizeOnly la form appra nella posizione in cui è stata lascita a design time,
ma la dimensione è stabilita da Windows. Con poScreenCenter la form mantine le
dimensioni definite a design time, ma viene posizionata al centro dello schermo. Con
poDesktopCenter la form mantiene le dimensioni definite a design time, ma viene
posizionata al centro del desktop. Con poMainFormCenter la form mantiene le
dimensioni assegnatele a design time, ma è posizionata al centro della form
principale dell'applicazione. Questa posizione può essere solamente usata con le
forms secondarie. Se utilizzata in una form principale l'effetto è lo stesso di
poScreenCenter. Con poOwnerFormCenter la form mantiene le dimensioni
assegnatele a design time, ma è posizionata al centro della form indicata nella
proprietà Owner della form stessa. Se in Owner non viene specificata alcuna form,
questa impostazione restituisce lo stesso risultato di poMainFormCenter.

Si può anche decidere di rendere una form autodimensionante in base ai componenti


in essa contenuti o allo spostamento di essi. Ciò è possibile impostando la proprietà
AutoSize della form stessa. Normalmente questa proprietà è impostata a False e fa
si che qualora dei componenti non risultassero visibili nell'area client della form,
vengano visualizzate le barre di scorrimento orizzontale e verticale.

Si può decidere il comportamento delle barre di scorrimento sia orizzontale che


verticale. Se viene impostata la proprietà AutoSize della form a True, non si ha la
comporsa automatica delle barre di scorrimento ma la form verrà ridimensionata sia
in orizzontale che verticale per accogliere i componenti che prima non erano
completamente visibili nell'area client.

Le Form di Delphi permettono anche di definire su aquale monitor visualizzare la


form nel caso di un ambiente fornito di più monitor, come nel caso delle schede
Matrox DualHead. Per fare ciò bisogna impostare adeguatamente la proprietà
DeafultMonitor della form che vuole venga visualizzata in un monitor particolare.

Un'altra proprietà interessante è quella che permette di stabilire come una form viene
visualizzata alla sua prima visualizzazione. La proprietà WindowState permette di
scegliere tra wsNormal, che indica lo stato di visualizzazione normale della form,
wsMaximized che indica che la form è massimizzata o wsMinimized che indica che la
form è minimizzata. Questa proprietà può anche essere letta per verificare lo stato
attuale della form.

Ancora più interessante è la proprietà KeyPreview che peremtte di intercettare la


pressione dei tasti della tastiera prima che il componente attivo nella form si accorga
dell'accaduto. In poche parole quando viene premuto un tasto sulla tastiera, il
componente attivo in quel momento riceve un messaggio di Windows che lo informa
dell'accaduto. Impostando la proprietà KeyPreview a True, questo comportamento
viene alterato permettendo alla form di intercettare il messaggio di Windows prima
che questo giunga al componente attivo.

Vediamo ancora un'altra proprietà delle forms ed è la proprietà Icon che permette di
definire l'icona visualizzata in alto a sinitra nella barra del titolo della form. Se a
questa proprietà non viene assegnata una icona, la form prenderà l'icona
dell'applicazione, che di default Delphi 5 imposta a .

Metodi

Dopo aver visto le proprietà più interessanti delle froms, vediamone ora i metodi.
Sicuramente il più importatnte è il metodo Create comune a tutti i componenti della

http://programmazione.html.it/delphi/delphi_28.htm (2 di 4) [21/05/2002 11.24.21]


DELPHI: Approfondimento sulle Forms

VCL. Questo metodo crea un oggetto di classe TForm. Da notare che il metodo crea
solamento l'oggetto, percui non ci si deve aspettare di vedere visualizzare la form
dopo la sua chiamata.

Della visualizzazione della form si occupano due metodi: Show e ShowModal. Il


primo visualizza la form e restituisce il controllo all'applicazione, mentre il secondo
visualizza la form e attende che questa venga chiusa prima di restituire il controllo
all'applicazione. L'effetto è quello che abbiamo visto nell esempio delle interfacce
grafiche: la finestra di dialogo. Il metodo ShowModal restituisce anche un valore che
identifica il tipo di chiusura della finestra. Un elenco dei valori validi è: mrNone, mrOk,
mrCancel, mrAbort, mrRetry, mrIgnore, mrYes, mrNo, mrAll. Per chiudere una
finestra modale è sufficiente assegnare alla proprietà ModalResult della stessa un
valore diverso da zero

Il metodo Hide nasconde la form. Da notare che la form viene solamente nascosta
ma non viene eliminata dalla memoria. Quindi, giocando con i metodi Show e Hide, si
può visualizzare e nascondere la form fino a che la forma non sarà chiusa. Per
eliminare dalla memoria la form, chiamare il metodo Free, come per tutti i
componenti della VCL. La chiamata a questo metodo imposta la proprietà Visible
della form a False.

Il metodo Close chiude la form. Anche questo metodo non distrugge l'oggetto form.
per distruggerlo occorre chiamare il metodo Free oppure assegnare alla variabile
Action nell'evento OnClose della form il valore caFree. Così alla chiusura della form
questa verrà anche eliminata dalla memoria automaticamente.

Eventi

Sicuamente l'evento più utilizzato di una form è l'evento OnShow che viene invocato
quando appunto la form viene visualizzata tramite il metodo Show o impostando la
proprietà visible della form a True.

Come per il metodo Show esiste l'evento OnShow, così per il metodo Hide esiste il
metodo OnHide. Questo viene appunto attivato quando viene chiamato il metodo
Hide della form oppure impostata a False la sua proprietà Visible.

L'evento OnCreate permette di effettuare operazioni speciali durante la creazione


della form. Se vengono creati degli oggetti in questo evento, questi dovranno essere
eliminati nell'evento OnDestroy della form. Evitare di lavorare su componenti inseriti
nella form in questio evento poichè potrebbero non essere accessibili e si avrebbe il
sollevamento di una eccezione di "Access Violation". Se la proprietà Visible della
form è impostato a True, si ha la seguente sequenza di eventi alla creazione della
stessa: OnCreate, OnShow, OnActivate, OnPaint.

L'evento Onclose viene attivato quando la form viene chiusa. Questo viene invocato
immediatamente prima della chiusura della form. In questo evento è disponibile un
parametro che permette di definire l'azione da eseguire in chiusura della form. Valori
validi sono: caNone, caHide, caFree, caMinimize.

L'evento OnCloseQuery viene invocato prima dell'evento OnClose e permette di


eseguire del codice per decidere se la form può o no chiudersi. Potrebbe essere il
caso di un editor di testo che controlla se il contenuto del file aperto è stato
modificato e visualizza una finestra di dialogo che chiede se salvare o no le
modifiche. In questo evento è presente un parametro che permette appunto di
annullare la chiusura della form. Per fare ciò occorre impostare il parametro
CanClose a False. Da notare che il parametro viene passato all'evento con il valore
True, percui se non si deve arrestare la chiusura della form non occorre assegnargli
questo valore.

L'evento OnActivate viene invocato quando la form riceve il fuoco. Una form riceve il
fuoco quando l'utente vi fa click con il mouse. Nel caso di form di tipo MDIChild,
l'evento OnActivate della form si genera solamente quando il fuoco cambia da una
form child all'altra.

L'evento OnResize permette di eseguire del codice speciale quando la form viene
ridimensionata. Per l'esesattzza, l'evento viene attivato a conclusione del
ridimensionamento della form. L'Evento OnResize è una degli eventi comuni a molti
controlli della VCL. Parallelamente all'evento OnResize, c'è l'evento OnCanResize

http://programmazione.html.it/delphi/delphi_28.htm (3 di 4) [21/05/2002 11.24.21]


DELPHI: Approfondimento sulle Forms

che permette di intercettare il ridimensionamento della form e di decidere se il


ridimensionamento richiesto debba essere consolidato oppure annullato.

Ci sono poi gli eventi attivati in risposta alle azioni generate dal mouse, come
OnClick relativo a quando un utente fa click con il mouse sulla form, OnMouseMove,
relativo al movimento del mouse sopra la form, OnDblClick che intercetta il doppio
click con il mouse sopra la form, OnMouseDown ed OnMouseUp che vengono
generati in risposta realtivamente alla pressione del tasto del mouse ed al suo
rilascio.

Come sono presenti eventi collegati alle azioni del mouse così sono presenti eventi
collegati alle azioni relative alla tastiera Quindi c'è l'evento OnKeyPress, OnKeyDown
ed OnKeyUp che rispettivamente si riferiscono alla pressione generica di un tasto
sulla tastiera, alla pressione ed al rilascio di un tasto.

Ovviamente quelli trattati in questa sezione del corso sono solo una parte delle
proprietà, dei metodi e degli eventi definiti dalla classe TForm e per una trattazione
più dettagliata si rimanda ai manuali sia in linea che cartacei del linguaggio.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_28.htm (4 di 4) [21/05/2002 11.24.21]


DELPHI: Componenti standard

LEZIONE 30: Componenti standard

In questa sezione daremo un'occhiata agli oggetti messi a disposizione da Delphi così
come viene fornito. Come già precisato all'inizio del corso, si fa riferimento alla versione 5
di Delphi Professional. Con molta probailità, essendo questa una versione intermedia tra
la Standard e la Enterprise, negli oggetti presentati in questa sezione potrebbero
essercene alcuni in più od in meno se si usa una versione differente.

Per la maggior parte, i componenti messici a disposizione da Delphi non sono altro che
dei wrapper dei Common Controls forniti con le varie versioni di Windows.

Come visto nella presentazione dell'ambiente di sviluppo, i componenti sono organizzati


nella Component Palette in pagine. Nell'installazione tipica abbiamo 15 pagine che
raccolgono i componenti in base a categorie. Ecco un elenco delle pagine standard nella
Component Palette: Standard, Additional, Win32, System, Data Access, Data Control,
Interbase, Internet, Fastnet, QReport, Dialogs, Win3.1, Samples, ActiveX, Servers. Ecco
come appaiono i componenti nelle rispettive pagine

Standard

Fig. 1

Additional

Fig. 2

Win32

Fig. 3

System

Fig. 4

Data Access

Fig. 5

Data Control

Fig. 6

Interbase

Fig. 7

Internet

Fig. 8

FastNet

Fig. 9

QReport

http://programmazione.html.it/delphi/delphi_29.htm (1 di 3) [21/05/2002 11.24.32]


DELPHI: Componenti standard

Fig. 10

Dialogs

Fig. 11

Win 3.1

Fig. 12

Samples

Fig. 13

ActiveX

Fig. 14

Servers

CP_Servers.jpg (8191 byte)


Fig.
15

Come potete vedere c'è praticamente quasi tutto per cominciare a realizzare delle
applicazioni complete. Vi sono componenti per l'accesso ai databases, per l'accesso ai
dati nei databases, per l'inserimento di testo, componenti a scelta multipla a tendina
(ComboBox), liste, liste ad albero, liste di elementi organizzabili in diversi modi (ad elenco,
ad icone grandi, con dettagli, simile alla sezione destra di esplora risorse), componenti per
la generazione di report da databases, etc.

Molti di questi componenti, soprattutto quelli visibili, sono identici ai componenti


solitamente visibili nelle applicazioni Windows. Questo proprio perchè derivano da classi
che inglobano i common controls di windows.

Non è possibile in questa sede trattare questi componenti uno ad uno in maniera completa
ma cercherò di illustrare gli aspetti più interessanti dei componenti più utilizzati.

La pagina standard

Nella pagina standard sono contenuti la maggior parte dei componenti da più tempo
inseriti in Windows. Abbiamo le caselle di testo (TEdit), le etichette (TLabel), i menu
(TMainMenu), i menu popup (TPopupMenu), caselle di testo su più righe (TMemo), i
bottoni (TButton), i le caselle di spunta (TCheckBox), i radio bottoni (TRadioButton), le
listbox (TListBox), le caselle di testo combinate (TComboBox), pannelli (TPanel), i pannelli
di raggruppamento (TGroupBox). Insieme a questi componenti derivanti direttamente
dall'ambiente Windows, ce ne sono due che invece sono implementati direttamente da
Delphi: i frames (TFrame) e la lista delle azioni (TActionList). Il primo di quest'ultimi due,
introdotto a partire dalla versione 5 di Delphi, permette, in un certo qual modo, di poter
creare visualmente componenti composti. Caratteristica molto interessante che permette
di ridurre notevolmente i tempi di sviluppo considerando che per realizzare un
componente composto bisogna, come per tutti i componenti, lavorare solamante a livello
di codice, con tutti i problemi di implementazione e debug che ne derivano. Un frame si
presenta sottoforma di una form con la differenza che l'area visualizzata a run time sarà
solamente l'area client della form a design time. Un frame non ha una barra del titolo.

La lista della azioni invece è un sistema che permette di centralizzare il codice che viene
richiamato da più componenti. Si può ad esempio definire una azione "Apri" il cui codice
associato viene eseguito dalla voce di menu apri, dal bottone rapido presente nella toolbar
e da qualsiasi altro controllo che lo richiede. Altra possibilità offerta dalle azioni è quella di
poter gestire lo stato di tutti i controlli associati a quell'azione. Riferendoci all'esempio
precedente, se per qualsiasi logica implementativa del programma l'azione "Apri" fosse
momentaneamente non disponibile, non dovremo andare a modificare lo stato dei controlli
associati a quell'azione uno ad uno, ma sarà sufficiente impostare la proprietà giusta
dell'oggetto azione ed automaticamente tutti i controlli verranno aggiornati. Le proprietà
più importanti di una azione (TAction) sono: Caption, Checked, Enabled, Hint,

http://programmazione.html.it/delphi/delphi_29.htm (2 di 3) [21/05/2002 11.24.32]


DELPHI: Componenti standard

HelpContext, Visible, ImageIndex, ShortCut. Queste sono le stesse proprietà della


maggior parte dei controlli che possono essere collegati alle azioni. Perciò, tornando al
nostro esempio, per disattivare tutti i controlli collegati all'azione "Apri", sarà sufficiente
impostare la proprietà Enabled dell'azione a False.

Creare un menu in Delphi è veramente semplice. Tramite il componente TMainMenu, è


sufficiente, dopo aver inserito il componente nella form in cui si vuole creare il menu, agire
tramite l'editor di proprietà del componente stesso (è possibile visualizzarlo facendo
doppio click con il mouse sul componente MainMenu inserito nella form) inserendo le voci
di menu che interessano, inserire separatori tra una voce e l'altra, assegnare delle azioni
alle varie voci di menu, creare sottomenu, assegnare del codice da eseguire a ciascuna
voce.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_29.htm (3 di 3) [21/05/2002 11.24.32]


DELPHI: Applicazioni MDI (parte prima)

LEZIONE 31: Applicazioni MDI (parte prima)

Le interfacce grafiche utente realizzabili in Windows sono fondamentalmente raggruppabili in


due tipologie: SDI e MDI. Le interfacce SDI (Single Document Interface) sono tutte quelle
interfacce, come quelle viste fino ad ora negli esempi di questo corso, che sono costituite da
una sola form e che visualizzano a richiesta le altre forms come fossero "slegate"
dall'applicazione stessa. Un esempio di applicazione SDI può essere il Notepad di Windows
che permette l'apertura di un solo documento all'interno della stessa istanza dell'applicazione.

Le applicazioni con interfaccia MDI (Multiple Document Interface) sono quelle applicazioni
costituite da una finestra principale definita "cornice" e che visualizzano le finestre secondarie
all'interno di questa cornice. Un esempio di applicazione MDI è il famoso Microsoft Word, che
permette l'apertura di più documenti all'interno della stessa istanza del programma. A livello
puramente tecnico-implementativo, la struttura MDI fornisce automaticamente al
programmatore servizi e funzioni che agevolano la gestione della struttura stessa. Alcune di
queste funzioni sono la possibilità di affiancare sia in orizzontale che verticale, le finestre
"figlie", di gestire automaticamente un elenco delle finestre figlie. Anche la gestione dei menu,
il merge, viene gestito in maniera automatica. Tutto questo, se realizzato direttamente con
chiamate dirette alle API di Windows comporta un discreto lavoro; in Delphi non è così, tutto è
più semplice. In questa sezione realizzeremo un semplicissimo editor di testo clone di notepad
ma strutturato come MDI. Vedremo anche l'impiego di alcuni componenti che non abbiamo
visto fino ad ora.

Per prima cosa creiamo una nuova applicazione scegliendo il comando "New Application" dal
menu "File". Salviamo il progetto assegnando alla form principale il nome "FMain.pas" e al
progetto il nome "NotePadX.prj".

Cominciamo con il modificare la proprietà FormStyle della form principale a fsMDIForm. Così
facendo, diciamo a Delphi che questa sarà la finestra frame che conterrà le altre finestre
dell'applicazione. Diamo un nome alla form assegnando alla proprietà Name della stessa il
valore "Main". Inseriamo un componente TActionList nella form scegliendolo come solito dalla
Component Palette nella pagina Standard. Definiamo quindi le azioni da assegnare al menu
della finestra richiamando l'editor di proprietà del componente TActionList facendo doppio click
su di esso. Aggiungiamo quattro azioni con i seguenti nomi: actNew, actSave, actSaveAs,
actOpen. Assegnamo alla proprietà Category di ognuna di esse il valore Commands per
raggrupparle all'interno della categoria comandi. Modifichiamo per ognuna di esse la proprietà
Caption assegnadogli rispettivamente i seguenti valori: "&New", "&Save", "Save &As...",
"&Open...". Ovviamente, non dovranno essere digitate le virgolette. Definiamo anche delle
combinazioni di tasti scorciatoia a alla proprietà ShortCut di ciascuna azione. Per actNew
inseriamo "Ctrl+N", per actOpen "Ctrl+O", per actSave "Ctrl+S", sempre senza virgolette.

Ora aggiungiamo un componente menu. Tramite l'editor di menu attivabile facendo doppio
click sul componente menu nella form, inseriamo un menu "File" . Per permettere all'utente di
accedere ai menu attraverso la tastiera, nel definire il nome dei menu nella proprietà Caption
del menu, inseriamo il carattere "&" prima della lettera che vogliamo utilizzare come
scorciatoia per accedere al menu. Nel nostro caso scriveremo "&File". Attenzione, all'utilizzo di
questa funzionalità poichè non possono esistere due voci di menu con lo stesso carattere
scorciatoia nella stessa sezione di menu. Assegnamo alla form principale il colore di sfondo
clAppWorkSpace agendo sulla proprietà Color della stessa. Selezionando la voce di menu
vuota che compare sotto la voce di menu "File", cominciamo a comporre la struttura del nostro
menu. Assegnamo alla nuova voce, tramite la proprietà Action, l'azione actNew che abbiamo
definito in precedenza. Così facendo, vedremo che automaticamente verrà creata una voce di
menu con la caption impostata nell'azione actNew ed una nuova voce. proseguendo con lo
stesso metodo, aggiungiamo le altre azioni nel seguente ordine: actOpen, actSave,
actSaveAs. A questo punto non rimane che aggiungere una voce di menu per chiudere
l'applicazione. prima però, aggiungiamo una linea per separare le altre voci da quest'ultima.
Per fare ciò digitiamo il carattere "-" (meno) nella proprietà Caption della voce di menu vuota.
Vedremo comparire una linea orizzontale che fa da separatore. Semplice no! Ora non rimane
che aggiungere l'ultima voce assegnando alla caption del menu item vuoto, la caption "&Exit"
e la scorciatoia "Alt+F4" nella proprietà ShortCut. Facendo doppio click sulla voce di menu
"Exit" appena creata assegneremo ad essa il gestore di evento OnClick che dovrà gestire la
chiusura dell'applicazione. Ecco il codice da inserire

http://programmazione.html.it/delphi/delphi_31.htm (1 di 5) [21/05/2002 11.26.46]


DELPHI: Applicazioni MDI (parte prima)

procedure TMain.Exit1Click(Sender: TObject);


begin
Close;
end;

La nostra applicazione può già essere eseguita per vedere come si presenta. Notiamo che nel
menu "File", le voci sono tutte disabilitate all'infuori della voce "Exit". Questo perchè non è
ancora stato assegnato del codice alle azioni. Per chiudere la nostra applicazione scegliamo il
comando "Exit" dal menu "File" oppure premiamo i tasti "Alt" e "F4", oppure clicchiamo sul
pulsante con la "X" nella bara del titolo.

Ora aggiungiamo alla nostra applicazione una nuova finestra tramite la voce "New Form" dal
menu file oppure tramite il corrispondente tasto nella toolbar. Assegnamo alla sua proprietà
FormStyle il valore fsMDIChild, alla proprietà Name il valore "EditorWin" e salviamola con il
nome "FEditorWin.pas". Se ora riavviamo la nostra applicazione, ci accorgiamo che la nuova
finestra inserita verrà visualizzata subito all'avvio dell'applicazione. Questo non è il
comportamento che noi vogliamo. Per cui, chiudiamo l'applicazione e modifichiamo questo
comportamento agendo sulle proprietà del progetto accedendovi tramite il comando "Options"
nel menu "Porject". Nella pagina Forms della finestra che compare, passiamo da sinistra a
destra la form EditorWin. In questo modo, per aprire la finestra, sarà necessario crearla
esplicitamente tramite codice.

Costruiamo la struttura sia grafica che funzionale della nuova form. Aggiungiamoci un
componente Memo che conterrà il testo dei documenti ed impostiamo la sua proprietà Align al
valore alClient. Così facendo il componente Memo andrà automaticamente ad occupare
l'intera area client della finestra e si ridimensionerà assieme ad essa. Eliminiamo dalla
proprietà Lines dell'oggetto Memo la stringa che riporta il suo nome. Assegnamo alla sua
proprietà Name il valore Doc. Quindi il nostro componente Memo ora si chiamerà Doc. Manca
ancora un componente da aggiungere alla nostra finestra secondaria: un componente
SaveDialog per acquisire il nome del documento da salvare quando si utilizza la funzione
"Save As" oppure si salva un nuovo documento. Una volta aggiunto alla form cmbiamogli il
nome in SaveDialog agendo sulla sua proprietà Name. Ora passiamo alla stesura del codice
che farà funzionare la nostra form. Ecco cosa dobbiamo aggiungere nella definizione della
classe TEditorWin della nostra form

type
TEditorWin = class(TForm)
Doc: TMemo;
SaveDialog: TSaveDialog;
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
private
{ Private declarations }
FDocCount: Integer;
FNewDoc : Boolean;
FFileName : TFileName;
function GetModified: Boolean;
Function OpenFile(FileName : TFileName) : Boolean;
Function SaveFile(FileName : TFileName) : boolean;
Function SaveFileAs(FileName : TFileName) : Boolean;
Procedure ResetModified;
public
{ Public declarations }
Constructor Create(AOwner : TComponent; Count : Integer;
NewDoc : Boolean; FileName : TFileName);

Function SaveDoc : Boolean;


Function SaveDocAs : Boolean;
Property DocCount : Integer read FDocCount;
Property Modified : Boolean read GetModified;
Property FileName : TFileName read FFileName;
end;

http://programmazione.html.it/delphi/delphi_31.htm (2 di 5) [21/05/2002 11.26.46]


DELPHI: Applicazioni MDI (parte prima)

Le parti evidenziate in rosso non vanno esplicitamente scritte, ma sono create


automaticamente da Delphi. Doc e SaveDialog, sono i due componenti che abbiamo aggiunto
alla form. Il resto rappresentano i gestori di eventi della form. Selezionando la form EditorWin,
e scegliendo il suo nome all'interno della tendina dell'Object Inspector, nella pagina Events
dello stesso Object Inspector, andiamo a definire il gestore per l'evento OnShow facendo
doppi click sulla sua casella. Dovremo digitare il codice seguente

procedure TEditorWin.FormShow(Sender: TObject);


begin
If FNewDoc then
Begin
Caption := 'New document' + IntToStr(FDocCount);
FFileName := Caption + '.txt';
End
Else
Begin
If Not OpenFile(FFileName) then
Begin
MessageDlg('Error opening "' + FFileName
+ '"!', mtError, [mbOk], 0);

Close;
End
Else
Caption := FFileName;
End;
end;

Seguendo lo stesso metodo digitiamo il seguente codice per il gestore di evento OnClose

procedure TEditorWin.FormClose(Sender: TObject; var


Action: TCloseAction);

begin
Action := caFree;
end;

Alla chiusura della finestra è necessario rimuoverla dalla memoria e per ridurre il quantitativo
di memoria richiesta e perchè, una finestra MDIChild quando viene chiusa non viene nascosta
coma una normale form, ma viene ridotta.

Per il gestore OnCloseQuery abbiamo

procedure TEditorWin.FormCloseQuery(Sender: TObject;


var CanClose: Boolean);
begin
If Modified then
Case MessageDlg('Save changes to "' + FFileName
+ '"?', mtConfirmation, [mbYes, mbNo, mbCancel], 0) of

mrYes : If FNewDoc then


Begin
If SaveFileAs(FFileName) then
CanClose := True
Else
CanClose := False;
End
Else
Begin
If SaveFile(FFileName) then
CanClose := True
Else

http://programmazione.html.it/delphi/delphi_31.htm (3 di 5) [21/05/2002 11.26.46]


DELPHI: Applicazioni MDI (parte prima)

CanClose := False;
End;
mrNo : CanClose := True;
mrCancel : CanClose := False;
End;
end;

Le parti evidenziate in verde vanno invece digitate sia nella dichiarazione della classe sia per
esteso nella sezione implementation. Ecco il codice

constructor TEditorWin.Create(AOwner: TComponent;


Count : Integer; NewDoc: Boolean; FileName: TFileName);

begin
Inherited Create(AOwner);

FDocCount := Count;
FNewDoc := NewDoc;
FFileName := FileName;
end;

function TEditorWin.GetModified: Boolean;


begin
Result := Doc.Modified;
end;

function TEditorWin.OpenFile(FileName: TFileName): Boolean;


begin
Try
Doc.Lines.LoadFromFile(FileName);
Result := True;
Except
Result := False;
End;
end;

function TEditorWin.SaveFile(FileName: TFileName): boolean;


begin
Try
Doc.Lines.SaveToFile(FileName);
FNewDoc := False;
ResetModified;
Result := True;
Except
Result := False;
End;
end;

function TEditorWin.SaveFileAs(FileName: TFileName): Boolean;


begin
SaveDialog.FileName := FileName;
If SaveDialog.Execute then
Begin
Result := SaveFile(SaveDialog.FileName);
If Result then
Begin
FNewDoc := False;
ResetModified;
FFileName := SaveDialog.FileName;
Caption := FFileName;
End;
End
Else
Result := False;
end;

procedure TEditorWin.ResetModified;
begin
Doc.Modified := False;

http://programmazione.html.it/delphi/delphi_31.htm (4 di 5) [21/05/2002 11.26.46]


DELPHI: Applicazioni MDI (parte prima)

end;

function TEditorWin.SaveDoc: Boolean;


begin
If FNewDoc then
SaveFileAs(FFileName)
Else
SaveFile(FFileName);
end;

function TEditorWin.SaveDocAs: Boolean;


begin
SaveFileAs(FFileName);
end;

Con questo codice abbiamo completato la nostra form. Nella prossima lezione ne
spiegheremo il funzionamento. In particolare ci soffermeremo sulle funzioni che gestiscono
l'apertura ed il salvataggio dei documenti.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_31.htm (5 di 5) [21/05/2002 11.26.46]


DELPHI: Applicazioni MDI (parte seconda)

LEZIONE 32: Applicazioni MDI (parte seconda)

La procedura OpenFile si occupa di caricare il contenuto del file il cui nome gli viene
passato come parametro. Il caricamento avviene sfruttando il metodo LoadFromFile della
proprietà Lines (che è di tipo TStrings) dell'oggetto Doc (TMemo). Anche la procedura
SaveFile utilizza un metodo del componente Doc. In questo caso si tratta del metodo
SaveToFile della proprietà Lines. La procedura SaveFileAs utilizza prima di chiamare la
procedura SaveFile una chiamata al metodo Execute dell'oggetto SaveDialog che
permette, tramite la finestra di dialogo standard di windows, di selezionare il nome del file
da assegnare al documento. Se il salvataggio avviene con successo, viene anche
aggiornato il titolo della finestra contenente il documento con il nuovo nome di file.

In questa form, abbiamo anche un esempio di ridefinizione del costruttore della form.

La proprietà Modified definita in questa form, ci servirà per controllare lo stato di


abilitazione delle azioni nella form principale. Il valore di questa proprietà viene restituito
dal metodo GetModified che controlla se la proprietà dell'oggetto Doc (TMemo) è True o
False. La classe TMemo infatti, sa riconoscere da sola quando il suo contenuto è stato
modificato. Ecco invece come sono state impostate le proprietà dell'oggetto SaveDialog

DefaultExt = '.txt'
Filter = 'Text files (*.txt)|*.txt|INI files (*.INI)|*.INI|
NFO files (*.NFO)|*.NFO|Any file (*.*)|*.*'

Title = 'Save as'

Per impostare la proprietà filter è disponibile un editor di proprietà che si attiva cliccando
sul tastino con i tre puntini vicino alla casella del valore della proprietà stessa. I filtri, sia
nel componente OpenDialog che SaveDialog, servono a filtrare appunto i files in base
alla loro estensione. La proprietà DefualtExt definisce invece l'estensione di default che
verrà aggiunta al nome del quando questo verrà salvato senza specificarne l'estensione.

Passiamo ora a completare la form principale. Aggiungiamo prima altre azioni


prendendole questa volta tra quelle predefinite che ci mette a disposizione Delphi.
Sempre dall'editor di proprietà dell'Oggetto ActionList, scegliamo "New standard action"
dal menu a tendina che viene visualizzato premendo il tasto con la freccia verso il basso
posto vicino al tasto "New action". Selezioniamo tutte le azioni che fanno parte della
categoria "Window" selezionando la prima e tenendo premuto shift selezionando l'ultima.
In questo modo avremo creato delle azioni che serviranno, senza scrivere nemmeno una
riga di codice a gestire la funzioni relative alla finestre figlie della nostra applicazione.
Aggiungiamo ancora un componente, il dialogo di apertura file (TOpenDialog)
chiamandolo OpenDialog e settando le proprietà seguenti

DefaultExt = '.txt'
Filter = 'Text files (*.txt)|*.txt|INI files (*.INI)|*.INI|
NFO files (*.NFO)|*.NFO|Any file (*.*)|*.*'

Title = 'Open'

Come per la form EditorWin, vediamo quale è il codice da inserire nella definizione della
classe TMain

http://programmazione.html.it/delphi/delphi_32.htm (1 di 5) [21/05/2002 11.26.54]


DELPHI: Applicazioni MDI (parte seconda)

type
TMain = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
ActionList1: TActionList;
actOpen: TAction;
actSave: TAction;
actSaveAs: TAction;
actNew: TAction;
New1: TMenuItem;
Save1: TMenuItem;
SaveAs1: TMenuItem;
N1: TMenuItem;
Exit1: TMenuItem;
OpenDialog: TOpenDialog;
Open1: TMenuItem;
Windows1: TMenuItem;
WindowArrange1: TWindowArrange;
WindowCascade1: TWindowCascade;
WindowClose1: TWindowClose;
WindowMinimizeAll1: TWindowMinimizeAll;
WindowTileHorizontal1: TWindowTileHorizontal;
WindowTileVertical1: TWindowTileVertical;
Arrange1: TMenuItem;
Cascade1: TMenuItem;
MinimizeAll1: TMenuItem;
TileHorizontally1: TMenuItem;
TileVertically1: TMenuItem;
N2: TMenuItem;
Close1: TMenuItem;
procedure CheckSaveEnabledState(Sender: TObject);
procedure Exit1Click(Sender: TObject);
procedure actNewExecute(Sender: TObject);
procedure actOpenExecute(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure actSaveExecute(Sender: TObject);
procedure actSaveAsExecute(Sender: TObject);
private
{ Private declarations }
NewDocCount : Integer;
Function CheckModified : Boolean;
Function NewDocument : Boolean;
Function OpenDocument(FileName : TFileName) : Boolean;
public
{ Public declarations }
end;

Anche qui il codice in rosso è quello gestito automaticamente da Delphi e quello in verde
quello che invece dobbiamo inserire noi.

Ecco di seguito il codice da inserire

function TMain.CheckModified: Boolean;


begin
Result := False;
If Assigned(ActiveMDIChild) then
Result := TEditorWin(ActiveMDIChild).Modified;
end;

procedure TMain.CheckSaveEnabledState(Sender: TObject);


begin
TAction(Sender).Enabled := CheckModified;
end;

procedure TMain.Exit1Click(Sender: TObject);


begin
Close;
end;

http://programmazione.html.it/delphi/delphi_32.htm (2 di 5) [21/05/2002 11.26.54]


DELPHI: Applicazioni MDI (parte seconda)

procedure TMain.actNewExecute(Sender: TObject);


begin
If Not NewDocument then
MessageDlg('Error creating new document!', mtError, [mbOk], 0);
end;

function TMain.NewDocument: Boolean;


Var NewForm : TEditorWin;
begin
Try
Inc(NewDocCount);

NewForm := TEditorWin.Create(Self, NewDocCount, True, '');


NewForm.Show;
Result := True;
Except
Result := False;
End;
end;

procedure TMain.actOpenExecute(Sender: TObject);


Begin
If OpenDialog.Execute then
OpenDocument(OpenDialog.FileName);
end;

function TMain.OpenDocument(FileName : TFileName): Boolean;


Var OpenForm : TEditorWin;
begin
Try
OpenForm := TEditorWin.Create(Self, 0, False, FileName);
OpenForm.Show;
Result := True;
Except
Result := False;
End;
end;

Procedure TMain.FormCreate(Sender: TObject);


begin
Caption := Application.Title;
end;

procedure TMain.actSaveExecute(Sender: TObject);


begin
If Assigned(ActiveMDIChild) then
TEditorWin(ActiveMDIChild).SaveDoc;
end;

procedure TMain.actSaveAsExecute(Sender: TObject);


begin
If Assigned(ActiveMDIChild) then
TEditorWin(ActiveMDIChild).SaveDocAs;
end;

Il metodo privato CheckModified si occupa di controllare se la finestra EditorWin


correntemente attivata abbia o no delle pendenze riguardo a modifiche apportate al
contenuto del documento. Tramite la proprietà ActiveMDIChild della form frame, si
ottiene un riferimento di tipo TForm alla form MDIChild attiva, ovvero la finestra che
possiede il fuoco. Poichè la nostra finestra child è una classe di tipo TEditorWin, per
accedere alla sua proprietà Modified, non presente nella classe TForm, occorre fare un
type cast, una conversione di tipo a TEditorWin. Lo stesso metodo viene utilizzato anche
negli altri metodi che ottengono il riferimento tramite ActiveMDIChild.

Nella funzione NewDocument che si occupare di creare un nuovo documento vuoto,


abbiamo utilizzato un contatore per distinguere i diversi documenti creati nell'ambito della
stessa sessione di lavoro. Questo viene passato al costruttore della form EditorWin che
lo utilizzerà per aggiornare il titolo della finestra stessa impostandone la proprietà
Caption.

http://programmazione.html.it/delphi/delphi_32.htm (3 di 5) [21/05/2002 11.26.54]


DELPHI: Applicazioni MDI (parte seconda)

Questo abbozzo di editor può essere ampliato aggiungendo toolbars, statusbar, la


stampa e così via. Potrebbe essere un buon esercizio completarlo con quello che
manca. I file del progetto, per una consultazione più accurata sono disponibili qui.

Un approfondimento sulle finestre MDI.


Abbimo visto che per utilizzare questo tipo di struttura in Delphi è molto semplice, è
sufficiente impostare per la finestra frame il valore della proprietà FormStyle a
fsMDIForm e per le finestre figlie a fsMDIChild. Tecnicamente, la prima finestra, quella
che contiene le altre, è formata da due finestre sovrapposte. La prima è il frame vero e
proprio, la tipica form di Delphi, l'altra è una finestra che non ha bordo ne titolo che
ricopre l'intera area client della finestra frame. Tutte le finestre figlie che vengono create,
sono figlie della finestra senza bordi, chiamata Client. Per cambiare il colore dello fondo
della finestra Client, ovvero della area client della nostra finestra frame, è sufficiente, in
Delphi, impostare la proprietà Color della form Frame, come è visibile nei sorgenti
dell'esempio realizzato. In definitiva possiamo riassumere così la gerarchia delle finestre
MDI

Finestra Frame -> Finestra Client -> Finestre Figlie

Ecco alcune proprietà e metodi della classe TForm che non sono stati utilizzati nel
nostro esempio ma che meritano ci essere mensionati. La classe TForm definisce due
metodi che permettono di selezionare le finestre figlie attive relativamente alla finestra
attiva. Per la precisione, il metodo Next attiva la finestra figlia successiva, nella lista
interna delle finestre figlie, a quella correntemente attiva. Al contrario, il metodo Previus
permette di selezionare la finestra precedente a quella correntemente attiva.

La proprietà ClientHandle contiene il riferimento (Window Handle) alla finestra Client di


cui abbiamo parlato sopra.

Le proprietà MDIChildCount e MDIChildren permettono di operare sulle finestre figlie.


In paricolare, MDIChildCount restituisce il numero corrente delle finestre figlie e
MDIChildren è un array di riferimenti alle finestre figlie. Queste due proprietà utilizzate in
combinazione, permettono di scorrere ed operare sulle finestre figlie correnti.

Un'altra caratteristica molto interessante, che non riguarda solamente le applicazioni


MDI, è la possibilità di gestire l'automerge dei menu delle varie finestre. Avendo una
finestra principale con un menu, è possibile far disporre automaticamente il menu di una
finestra secondaria definendo un ordinamento per i singoli menu. Per la trattazione di
questo argomento si ramanda alla documentazione del linguaggio oppure alla ottima
guida in linea dell'IDE.

Una cosa da tenere presente durante la realizzazione di una applicazione MDI è che,
per il funzionamento visto precedentemente, per applicare una immagine di sfondo alla
finestra principale dell'applicazione (finestra frame), non è sufficiente disegnare
l'immagine sulla form poichè questa viene coperta dalla finestra Client. Per ovviare a
questo problema occorre ricorre a una pratica, diciamo di basso livello. Chi programma
in ambiente Windows sa che ogni finestra è legata ad una Window Procedure. Che cos'è
una window procedure? Una window Procedure non è altro che la procedura che
consente alla finestra di gestire i messaggi a lei inviati: ridimensionamento, ridisegno,
posizionamento, etc. Questa pratica prevede di sostituire questa Window Procedure per
alterare il comportamento standard della finestra. Quello che dobbiamo fare è
intercettare il messaggio WM_ERASEBKGND inviato alla nostra finestra quando è
necessario il ridisegno dello sfondo. Per fare ciò, occorre salvare il riferimento alla
vecchia Window Procedure e sostituire la procedura originale con la nostra che sarà in
grado di gestire disegno dell'immagine nella finestra Client. Il procedimento completo è
rintracciabile sul libro di Marco Cantù "Programmare con Delphi 5".

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_32.htm (4 di 5) [21/05/2002 11.26.54]


DELPHI: Sviluppo di applicazioni Multithread

LEZIONE 33: Sviluppo di applicazioni Multithread

I sistemi operativi moderni moderni permettono l'esecuzione contemporanea (quasi!!)


di più applicazioni e per questo vengono detti Multitask. Una applicazione può quindi
essere eseguita contemporaneamente ad un'altra (per esempio quando navighiamo
su internet e contemporaneamente controlliamo la posta, oppure ricerchiamo un file
sul disco). Durante la sua esecuzione, una applicazione deve bloccare
completamente l'esecuzione quando aspetta che terminino processi lenti come
l'accesso ai dischi o la comunicazione con altri computer. Molte volte le applicazioni
possono essere organizzate in maniera tale da ovviare a questi colli di bottiglia o in
maniera da suddividere la loro esecuzione in più processi, magari ripartiti su più
processori in sistemi multiprocessore. Ecco che in alcuni dei moderni sistemi
operativi ci vengono in contro i Thread. In questo caso avremo a che fare con sistemi
operativi multithread. La differenza tra questi due tipi di sistemi operativi sta nel fatto
che nel primo tipo, l'unità elementare di esecuzione è il task (processo), mentre nel
secondo è il thread. Nell'ultimo tipo di sistema operativo, possiamo avere dei tasks
consistenti in più trhead. La differenza fondamentale tra task e trhead sta nel fatto
che i primi non possono condividere lo stesso spazio di indirizzamento mentre invece
i secondi, ovviamente se appartenenti alle stesso task (o processo), si. La creazione
di un nuovo thread non richiede l'impiego di grandi risorse di sistema. Normalmente
in una applicazione si ha un thread definito principale, ed è quello che gestisce i
messaggi di Windows e l'esecuzione del codice principale e nessuno, uno o più
trhead secondari.

La definizione, la creazione e la gestione di thread non è proprio semplice ed


intuitiva. Delphi però, come sempre, ci viene incontro semplificando notevolmente le
cose mettendoci a disposizione la classe TThread. L'uso di questa classe è molto
semplice. Questa contiene alcune proprietà e metodi tra cui il principale è il metodo
Execute. Questo è il metodo il cui codice viene eseguito all'interno del thread. Una
volta definita una nuova classe ereditata da TThread, per la creazione della classe è
necessario effettuare una chiamata al metodo Create della classe la quale ha il
seguente struttura

Create(Suspended : Boolean);

Il parametro Suspended da passare al metodo, permette di definire il


comportamento del thread in seguito alla sua creazione. Impostando questo
parametro a False (valore predefinito) il thread viene attivato immediatamente dopo
essere stato creato. Se il parametro Suspended ha valore True, il thread viene
creato e immediatamente sospeso, ovvero la sua esecuzione non ha inizio fino ad
una chiamata al metodo Resume della classe TThread. Per sospendere di nuovo il
thread dopo il suo avvio, è disponibile il metodo Suspend. Esistono altri metodi che
permettono di controllare l'esecuzione del thread come Terminate che decreta
l'arresto definitivo del thread.

I metodi descritti precedentemente sono affiancati da alcune proprietà che


permettono di acquisire informazioni riguardo allo stato di esecuzione del thread. La
proprietà Suspended ci informa sul fatto che il thread è stato sospeso; la proprietà
Terminated ci indica che il thread è stato terminato; Priority permette di impostare la
priorità di esecuzione del thread su 7 livelli differenti di priorità da un minimo di tpIdle
ad un massimo di tpTimeCritical; FreeOnTerminate che permette di specificare se
l'oggetto thread creato deve essere distrutto al termine dell'esecuzione del codice del
thread.

Vediamo ora come va implementato il codice da eseguire all'interno del thread vero
e proprio. Quando si definisce una classe erede di TThread, occorre anche riscrivere
il metodo Execute (che nella classe TThread è Virtual e Abstract, ovvero non ha
implementazione) facendo l'ovverride del metodo ereditato. Il codice che andremo a

http://programmazione.html.it/delphi/delphi_33.htm (1 di 3) [21/05/2002 11.27.08]


DELPHI: Sviluppo di applicazioni Multithread

scrivere all'interno del metodo Execute, sarà il codice che verrà eseguito all'interno
del thread. Ecco come si presenta la struttura del metodo Execute

Type NomeClasse = class(TThread)


private
{ Private declarations }
protected
{ Public declarations }
Procedure Execute; override;
end;

Implementation

Procedure NomeClasse.Execute;
Begin
//Inserire qui il codice da eseguire nel thread
End;

Da notare che il codice del metodo Execute, viene eseguito una volta sola quindi
raggiunta l'ultima istruzione del metodo Execute, l'esecuzione del thread si arresta.
Per eseguire più volte lo stesso codice all'interno del thread, occorre ricorrere ad un
ciclo e, cosa importante, ad ogni esecuzione del ciclo controllare il valore della
proprietà Terminated del thread per interrompere il ciclo quando l'esecuzione del
thread viene interrotta. Una tipica implementazione del metodo Execute è la
seguente

Procedure NomeClasse.Execute;
Begin
While not Terminated do
Begin
//Inserire qui il codice da eseguire nel thread
End;
End;

Una importante considerazione da fare riguardo al metodo Execute, sta nel fatto che
non si può eseguire codice che fa riferimento ad oggetti della VCL. Questo perchè gli
oggetti della VCL vengono eseguiti in un thread differente da quello in esecuzione
all'interno del metodo Execute e quindi si potrebbero avere problemi di accesso alle
loro proprietà ed ai lor metodi. Per evitare ciò, la classe TThread mette a
disposizione il metodo Synchronize che permette appunto di sincronizzare i due
threads in maniera tale da paermettere l'accesso sicuro alle proprietà ed ai metodi
dell'oggetto voluto. Esistono anche oggetti della VCL chiamati Thread-Safe che sono
stati realizzazti appositamente per l'utilizzo in ambienti multithread e che permettono
di gestire gli accessi al loro contenuto in maniera concorrente. Un esempio di questi
oggetti è la ThreadList.

Al termine dell'esecuzione del codice thread, è possibile eseguire del codice di


pulizia scrivendo un gestore di evento per l'unico evento che mette a disposizione la
classe TThread, l'evento OnTerminate.

La classe TThread permette anche di sincronizzare l'esecuzione del suo thread con
altri thread. Tramite il metodo WaitFor della classe TThread è possibile attendere il
termine dell'esecuzione di un'altro thread. Questo metodo non restituisce il controllo
finchè l'altro thread non è terminato.

Potrebbe capitare la necessità di intercettare quando un thread ha terminato una


certa operazione piuttosto che attendere che l'intero thread completi la sua
esecuzione. In questo caso esistono gli oggetti evento (TEvent). Quando un thread
deve comunicare ad altri di aver completato la un certa operazione, chiama il metodo
SetEvent dell oggetto evento. Per disattivare la segnalazione dell'evento è sufficiente
effettuare una chiamata al metodo ResetEvent dell'evento. Gli oggetti evento devono
essere creati in un campo d'azione globale per far si che tutti i thread interessati
possano accedervi. Il thread che necessita di attendere il completamento di una

http://programmazione.html.it/delphi/delphi_33.htm (2 di 3) [21/05/2002 11.27.08]


DELPHI: Sviluppo di applicazioni Multithread

determinata operazione, crea ed avvia i thread interessati ed effettua una chiamata la


metodo WaitFor dell'oggetto evento indicando un intervallo di tempo da attendere per
la ricezione del segnale. Il tempo da attendere per il settaggio del segnale è espresso
in millisecondi e può essere impostato anche su INFINITE. Al ritorno dalla sua
chiamata, il metodo WaitFor restituisce uno dei seguenti valori: wrSignaled,
wrTimeout, wrAbandoned, wrError.

L'oggetto evento, o meglio la classe TEvent, non è altro che una struttura wrapper
per gli oggetti event di Windows.

Altre strutture permettono di semplificare la gestione di multithread. Per esempio,


quando gli oggetti su cui si sta operando non possiedono metodi o proprietà
specifiche per operare in ambineti multithread, si può ricorrere alle sezioni critiche. Le
sezioni critiche permettono di far accedere ai metodi o alle proprietà di un
determinato oggetto un solo thread alla volta, onde evitare conflitti di accesso.
Questa struttura è definita nella classe TCriticalSection che possiede due metodi:
Acquire, Release. Quando un thread deve accedere alla sezione critica, deve prima
chiamare il metodo Acquire per bloccare l'accesso agli altri threads e rilasciare tale
blocco tramite il metodo Release al termine dell'operazione.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_33.htm (3 di 3) [21/05/2002 11.27.08]


DELPHI: Liste ed oggetti contenitore

LEZIONE 34: Liste ed oggetti contenitore

Delphi mette a disposizione dello sviluppatore alcune classi che permettono di


semplificare la gestione di insiemi di valori. La gestione di questi insiemi di valori
avviene tramite delle classi lista. La più generica, poichè permette di gestire insemi di
valori di tipo non omogeneo, è la classe TList che definisce una lista di puntatori.
Esiste una classe specizlizzata nella gestione di liste di stringhe, TStringList
derivata da TStrings. Un'altro gruppo di classi per la gestione di insiemi riguarda le
collezioni. Questo gruppo si compone fondamentalmente di due classi, TCollection e
TCollectionItem. La classe TCollection permette di gestire una lista di oggetti dello
stesso tipo, di cui la stessa lista è proprietaria.

Tutte queste liste mettono a disposizione dello sviluppatore una serie di metodi e
proprietà per manipolare il contenuto della lista stessa. Alcuni di questi metodi sono
Insert, Add, Delete, Remove per l'inserimento, aggiunta e l'eliminazione degli
elementi della lista. Sono disponibili alcuni metodi come IndexOf per la ricerca di
elementi all'interno della lista. Sono disponibili anche proprietà che permettono
l'accesso agli elementi della lista in base ad un indice come se si stesse lavorando
con un array. Per esempio, per accedere al secondo elemento di una lista di stringhe
(TStringList) è possibile utilizzare la proprietà Strings[1]. Da notare l'indice 1 per
identificare il secondo elemento della lista, poichè l'indice della lista è "zero based"
cioè il primo indice (ovvero il primo elemento) è 0. Quindi, se si vuole accedere
all'ultimo elemento della lista, sfruttando anche un'altra proprietà comune a tutte le
classi lista viste, utilizzando la proprietà Count che restituisce il numero di elementi
contenuti nella lista, dovremo scrivere Strings[NomeClasseLista.Count - 1]. Le liste
di stringhe hanno altresì la possibilità di associare un riferimento ad oggetto per
ciscuna stringa inserita nella lista attraverso il metodo AddObjects(Stringa,
PuntatoreObj). Così facendo verrà inserita una stringa con il valore contenuto in
Stringa e contemporaneamente le verrà associato il riferimento PuntatoreObj.
Parallelamente, per accedere ai valori della lista di oggetti di può ricorrere alla
proprietà Objects che funziona come la proprietà Strings già vista. Quindi all'indice
0 corrisponderà il riferimento oggetto relativo alla prima stringa e così via.

Con l'arrivo di Delphi 5 sono state introdotte alcuni nuovi tipi di liste con
caratteristiche molto interessanti definite classi-contenitore. Fondamentalmente
estendono le funzionalità di TList, aggiungendo il concetto di proprietà degli oggetti
contenuti e regole perticolari di estrazione ed inserimento. Una di queste nuove
classi è TObjectList che dal nome stesso è una lista che accetta come elementi
solamente oggetti derivati da TObject e non semplici puntatori. Caratteristica di
questa classe è la proprietà OwnsObjects che se impostata a true produce la
distruzione automatica dell'oggetto quando questo viene rimosso dalla lista. Esistono
anche altre classi come TComponentList per la gestione di liste di componenti,
TClassList per la gestione di liste di class reference.

Di particolare interesse ritengo siano le classi TStack e TQueue. Dal nome di queste
si intuisce già il loro scopo e funzionamento.

La classe TStack implementa una lista di puntatori in cui l'inserimento e l'estrazione


degli elementi avviene in testa alla lista. Si tratta dell'implementazione di una struttura
denominata pila in cui gli elementi vengono inseriti in cima alla pila e rimossi sempre
dalla cima. Questa strategia di inserimento ed estrazione è denominata LIFO (Last
In, First, Out). Per aggiungere elementi alla lista si fa ricorso al metodo Push e per
rimuovere elementi al metodo Pop. Si dispone anche del metodo Peek per leggere
l'elemento correntemente in cima alla pila senza estrarlo da essa. Una variante della
classe TStack è la classe TObjectStack che implementa un struttura simile tranne
per il fatto che gli elementi sono di tipo TObject.

L'altra classse, TQueue implementa un'altra struttura molto conosciuta in informatica,


ovvero la coda. Come per TStack, si tratta di una lista di puntatori la cui aggiunta
segue la strategia FIFO (First In, First Out) ovvero il primo elemento inserito viene
estratto per primo. Possiede gli stessi metodi della classe TStack. Come per la

http://programmazione.html.it/delphi/delphi_34.htm (1 di 2) [21/05/2002 11.27.15]


DELPHI: Liste ed oggetti contenitore

classe TStack esiste una variante per la gestione di liste di oggetti il cui nome della
classe è TObjectQueue. A differenza della TObjectList, le classi TObjectStack e
TObjectQueue non hanno la possibilità di distruggere gli oggetti al momenti
dell'estrazione dalla lista.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_34.htm (2 di 2) [21/05/2002 11.27.15]


DELPHI: BDE (Borland Database Engine)

LEZIONE 35: BDE (Borland Database Engine)

Delphi, fin dalle prime versioni, mette a disposizione dello sviluppatore la possibilità
di accedere a basi di dati (databases). Per far ciò la Borland ha realizzato il BDE
(Borland Database Engine); questo non è altro che un insieme di funzioni contenute
in alcune DLL distribuite con Delphi, attraverso le quali ci si interfaccia con vari tipi di
Database. Attraverso il BDE Dlphi è in grado di accedere direttamente ai seguenti tipi
di database: DBase, Paradox, Access, FoxPro, ASCII. Attraverso gli SQL Links
(forniti solamente con la versione Enterprise di Delphi) è possibile anche accedere ad
alcuni server SQL locali o remoti. Questi sono una serie di drivers sempre realizzati
dalla Borland per permettere di accedere direttamente a server SQL come Oracle,
SQL Server, Informix, SyBase, DB2. Il BDE permette altresì di accedere a qualsiasi
tipo di database purchè siano presenti nel sistema i relativi drivers ODBC. In questo
caso il BDE non accederà direttamente alle strutture del database ma gestirà queste
strutture attraverso il driver ODBC.

Nelle ultime versioni di Delphi Enterprise, vengono forniti anche degli oggetti ad hoc
per l'accesso a basi di dati tramite ADO, la tecnologia di Microsoft che tende a
centralizzare la gestione di differenti sorgenti di dati. Non è strettamente necessario
possedere la versione Enterprise di Delphi per lavorare con ADO, anche con la
versione Professional ciò è possibile purchè si trovino dei componenti per l'accesso
ad ADO, come ADOExpress disponibile nella versione Enterprise ed acquistabile
separatamente per la versione Professional, oppure oggetti di terze parti, magari
anche free (come TaoADODB), oppure implementando ciò che è necessari
ricorrendo magari all'importazione delle librerie di tipi (Type Library) di ADO
attraverso la funzione messa a disposizione dall'IDE di Delphi che esegue questa
procedura automaticamente creando le unit necessarie. Ovviamente, in tutti i casi
sopra trattati è necessario che nel sistema che ospita la nostra applicazione sia
presente o il BDE, ADO, o l'MDAC di Microsoft. Da notare che in base al tipo di
accesso che intendete utilizzare per la vostra base di dati, dovrete distribuire anche il
BDE o altre librerie necessarie al raggiungimento dello scopo.

Oltre a permettere l'accesso a tipi di database differenti, il BDE implementa alcune


funzionalità come per esempio le transazioni. Una transazione è un gruppo di
azioni che devono essere tutte portate a termine con successo su una o più tabelle di
un database prima che vengano rese permanenti. Se una di queste azioni non viene
completata con successo, tutte le altre azioni vengono annullate. Le transazioni sono
utili per prevenire sia problemi derivanti da guasti hardware nel mezzo
dell'esecuzione di un determinato comando, sia a gestire la simultaneità di accesso
da parte degli utenti in ambienti multiutente. Normalmente il supporto per le
transazioni non è disponibile con database di tipo locale ma il BDE fornisce un
supporto transazionale limitato per alcuni database di questo tipo.

Dalla versione Professional Delphi viene fornito corredato dei componenti per
l'accesso diretto all'RDBMS della Borland Interbase. Quest'ultimo viene anche fornito
nel CD d'installazione di Delphi. Questo database manager viene ora fornito
liberamente ed è disponibile nella versione 6 sia per piattaforma Windows che Linux.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_35.htm [21/05/2002 11.27.21]


DELPHI: Componenti per l'accesso ai databases

LEZIONE 36: Componenti per l'accesso ai databases

In Delphi l'accesso alle basi di dati è reso molto semplice grazie ai componenti per
l'accesso ai databases che si trovano nella pgina "Data Access" della component
palette.

Si tratta di otto componenti non visibili a run-time e sono: TDataSource, TTable,


TQuery, TStoredProc, TDatabase, TSession, TBatchMove, TUpdateSql.

Per accedere ai dati è necessaria una sorgente di dati. Questo non si riferisce
direttamente ai dati ma ad un dataset che può essere una tabella oppure una query,
una stored procedure o altri tipi di dataset come per esempio quelli ADO che
possono accedere anche a fogli di Excel ed altro. Se necessario, i dataset possono
essere collegati ad un database e quest'ultimo ad una sessione.

TDataSource

TDataSource è sostanzialmente una interfaccia tra un dataset ed un componente per


la manipolazione dei dati (data-aware). Attraverso questo componente i controlli data-
aware, possono visualizzare, modificare o navigare tra i dati presenti nel dataset. Se
si vogliono manipolare i dati contenuti in un dataset attraverso i controlli data-aware,
è necessario che ciascun datasource venga ssociato ad un dataset. Allo stesso
modo, ciascun controllo data-aware deve essere connesso ad un componente
datasource. I componenti datasource sono altresì utilizzati per collegare due dataset
in relazioni di tipo master-detail.

Le principali proprietà del componente TDataSource sono: AutoEdit, Dataset,


Enabled, State. Queste sono tutte accessibili a design-time tranne State, che è read-
only e accessibile solamente a run-time. La proprietà AutoEdit, se impostata a true,
permette di modificare automaticamente il contenuto di un campo del dataset
associato al datasource, nel momento in cui viene modificato il valore visualizzato nel
controllo data-aware associato al campo. La proprietà Dataset permette di
specificare il dataset a cui fa riferimento il datasource. A design-time, l'object
inspector, presenta una lista dei dataset presenti nel modulo corrente e/o in tutti i
moduli collegati al modulo corrente. La proprietà Enabled, stabilisce lo stato dei
controlli data-aware associati al datasource. Se impostata su false i controlli sono
disattivati e non è possibile modificare i loro valori. La proprietà State riporta lo stato
del dataset riferito dal componente datasource ed assume gli stessi valori della
proprietà State del componente TDataSet collegato.

I metodi principali del componente TDataSource sono: Edit, IsLinkedTo. Il metodo


Edit permette di attivare la modalità di edit sul dataset associato. Prima di attivare la
modalità edit, il metodo effettua un controllo sulla proprietà AutoEdit e State del
componente Datasource.

Per il componente DataSource gli eventi disponibili sono tre: OnDataChange,


OnStateChange, OnUpdateData. Il primo evento, viene attivato quando il record
corrente viene modificato e l'applicazione si sposta da un campo ad un altro od a un
altro record nel dataset associato. I controlli data-aware segnalano un cambiamento
di "data change" nei seguenti casi: spostamento ad un nuovo record, modifica dei
dati di un campo. L'evento OnStateChange viene attivato quando lo stato del
dataset associato al componente DataSource cambia. Si ha un cambiamento di stato
quando si passa per esempio dallo stato dsBrowse (consultazione dei dati) allo stato
dsEdit (per la modifica dei dati). Per controllare lo stato in cui si trova il dataset
sottostante il componente DataSource, controllare all'attivazione dell'evento

http://programmazione.html.it/delphi/delphi_36.htm (1 di 9) [21/05/2002 11.27.27]


DELPHI: Componenti per l'accesso ai databases

OnStateChange il valore della proprietà State del componente DataSource. L'ultimo


evento viene attivato immediatamente prima che i dati vengano salvati nel dataset
collegato.

TTable

Il componente Table incapsula una tabella appartenente ad un database. Il


componente Table è utilizzato per accedere ad una tabella di un database attraverso
il BDE. Questo componente permette di accedere direttamente a ciascun record e
campo della tabella associatagli. Attraverso questo componente + anche possibile
accedere ad un sottoinsieme dei dati contenuti nella tabella sottostante, facendo uso
dei filtri o degli intervalli. E' possibile accedere a tabelle di qualsiasi database
supportato dal BDE.

Le principali proprietà del componente Table sono: Active, BOF, CanModify,


DataSource, Database, DatabaseName, DefaultIndex, Exclusive, Exists, EOF, Filter,
Filtered, IndexName, ReadOnly, MasterSource, TableName, TableType,
SessionName, State. La proprietà Active indica se la tabella è aperta. Per apripre
una tabella è necessario, dopo aver assegnato le proprietà DatabaseName,
TableName, eseguire il metodo Open oppure impostare su true il valore della
proprietà Active. Questa proprietà può essere sia letta che scritta ed è accessibile
anche a design-time per attivare la tabella in fase di sviluppo. La proprietà BOF
(Begin Of File) indica quando il record corrente è il primo presente nella tabella.
Esattamente il contrario indica la proprietà EOF (End Of File). La proprietà
CanModify indica se i dati contenuti nella tabella riferita possono essere modificati.
Questa proprietà viene impostata automaticamente quando la tabella viene aperta ed
è una proprietà a sola lettura. Se CanModify è false significa che la tabella è in
modalità read-only oppure altri fattori possono impedire la modifica dei dati, come per
esempio un altro utente che sia connesso alla tabella in modalità esclusiva, oppure
non si abbiano i privilegi (ad esempio in un server di database come SQL server) per
accedere a quella tabella in modifica. La proprietà DataSource serve ad indicare
quale è il datasource che si riferisce alla tabella Master quando si sta utilizzando una
relazione di tipo Master-Detail dove la tabella corrente funge da dettaglio.
DatabaseName indica il database a cui fa riferimento il dataset. Questa proprietà
indica il nome di un componente TDatabase, oppure il percorso in cui si trovano le
tabelle database, oppure ancora il nome di un alias di database. DefaultIndex
identifica l'indice che verrà utilizzato per ordinare i record nella tabella quando questa
verrà aperta. La proprietà Exclusive, modificabile solamente a tabella chiusa,
permette di aprire la tabella in modalità esclusiva, ovvero qualsiasi altro utente che
richiedesse l'accesso a quella stessa tabella sarebbe impedito dal farlo. Exists è una
proprietà che permette di stabilire se esiste effettivamente la tabella fisica indicata nel
componente TTable. Questa restituisce true se la tabella esiste, false altrimenti.
Filter è una proprietà che permette di specificare un criterio in base al quale verra
limitata la visibilità dei record presenti nella tabella. Accetta valori di tipo stringa del
tipo: 'Campo1=valore1 or Campo2=valore2'. Possono anche essere utilizzati caratteri
jolly come "*". Attraverso la proprietà Filtered è possibile attivare o disattivare il
filtraggio dei record in base al filtro precedentemente definito nella proprietà Filter.
IndexName indica un indice secondario da utilizzare per definire l'ordinamento dei
records della tabella. Se questo valore non viene impostato, i records saranno
ordinati in base all'indice di default oppure, nel caso delle tabelle DBase, in base
all'ordine naturale dei records, ovvero la sequenza con cui i record sono stati aggiunti
nella tabella. La proprietà ReadOnly, permette di definire l'apertura della tabella
come di sola lettura, percui qualsiasi operazione di scrittura applicata a quella tabella
restituirà un messaggio di errore. Per impostare questa proprietà è necessario
impostare la proprietà Active su false (o chiudere la tabella), impostare il valore
desiderato, quindi reimpostare Active a true (o riaprire la tabella). Impostando
ReadOnly a true, automaticamente la proprietà CanModify assumerà valore False.
MasterSource contiene un riferimento ad un oggetto DataSource collegato ad una
tabella master. Questo valore va impostato solamente nel caso in cui si lavori con
tabelle in relazioni di tipo master-detail. TableName è una delle propietà più
importanti dell'oggetto Table; essa specifica il nome della tabella fisica a cui è
associato l'oggetto Table. TableType è una proprietà che specifica il tipo fisico di
tabella a cui fa riferimento l'oggetto Table stesso. Valori validi per questa proprietà
sono: ttDefault, ttParadox, ttDBase, ttFoxPro, ttASCII. Se impostato su ttDefault, il
tipo di tabella viene determinato automaticamente in base all'estenzione del nome
del file della tabella (ex.DB o niente=Paradox, DBF=DBase, TXT=ASCII).
SessionName identifica il componente Session all'interno del quale agisce la tabella.
La proprietà State riporta lo stato della tabella e valori possibili sono: dsInactive,

http://programmazione.html.it/delphi/delphi_36.htm (2 di 9) [21/05/2002 11.27.27]


DELPHI: Componenti per l'accesso ai databases

dsBrowse, dsEdit, dsInsert, dsSetKey, SetRange, dsCalcFields, dsFilter,


dsNewValue, dsOldValue, dsCurValue, dsBlockRead, dsInternalCalc, dsOpening.

I metodi principali dell'oggetto Table sono: Append, AppendRecord, Cancel, Close,


CreateTable, Delete, DeleteTable, DisableControls, Edit, EmptyTable,
EnableControls, FieldByName, FindFirst, FindLast, FindNext, FindPrior, FindNearest,
First, Insert, InsertRecord, IsEmpty, Last, Locate, LockTable, Lookup, MoveBy, Next,
Open, Post, Prior, RenameTable, UnlockTable. Il metodo Append serve ad
aggiungere un nuovo record vuoto alla fine della tabella e rende record corrente il
nuovo record aggiunto e lascia la tabella in modalità inserimento. AppendRecord
serve ad aggiungere alla fine della tabella un nuovo record. Questa funzione
permette di passare tutti i valori dei campi che mompongono il record direttamente e
di salvarne il contenuto con una unica operazione. Cancel ha lo scopo di annullare
lemodifiche apportate al contenuto dei records prima che queste vengano salvate
nella tabella ed imposta lo stato della tabella a dsBrowse. Close chiude la tabella
impostando la proprietà Active a false. Speculrmente, il metodo Open apre la tabella.
Quando la tabella è chiusa, non è possibile leggere o scrive dati in essa.
CreateTable è il metodo che permette di creare fisicamente la tabella su disco
basandosi sulle impostazioni contenute nelle proprietà FieldsDef e IndexDefs. Per
ulteriori informazioni a riguardo, consultare la guida in linea. Delete permette di
eliminare il record corrente. Il metodo DeleteTable ha lo scopo di eleiminare
fisicamente dal disco la tabella a cui è collegato l'oggetto Table. DisableControls
disattiva tutti i controlli (edit box, combobox, labels) collegati all'oggetto Table; al
contrario il metodo EnableControls li attiva. Il metodo Edit permette di impostare la
modalità modifica per la tabella. In questa modalità è possibile modificare il contenuto
dei campi dei records della tabella,ovviamente a tabella aperta. EmptyTable si
occupa di eliminare tutto il contenuto di una tabella. FieldByName viene utilissato
per recuperare un riferimento all'oggetto TField collegato al campo di una tabella.
Questo metodo restituisce un riferimento all'oggetto TField corrispondente al campo il
cui nome viene passato al metodo. FindFirst effettua una ricerca tra i records della
tabella alla ricerca del primo record corrispondente ai criteri di ricerca specificati.
FindLast, agisce in maniera simile al metodo FindFirst solamente che restituisce
l'ultimo tra i records corrispondenti ai criteri di ricerca. FindNext, FindPrior, agiscono
sulla precedente chiamata di uno dei metodi FindFirst, FindLast: il primo recupera il
record successivo tra quelli corrispondenti ai criteri di ricerca, mentre il secondo il
precedente. Per utilizzare questi metodi occorre prima effettuare una chiamata ad
uno dei metodi FindFirst, FindNext. I criteri di ricerca vengono specificati impostati
assegnando i un valore alla proprietà Filter della tabella. FindNearest permette di
spostare la posizione corrente della tabella al record che corrisponde il più possibile
ai criteri di ricerca specificati. Questo metodo riceve come parametri un array di valori
rappresentanti il contenuto (che si sta ricercando) dei campi del record. First
posizione il cursore della tabella al primo record in essa contenuto, mentre Last lo
posiziona sull'ultimo; Next e Prior effettuano uno spostamento rispettivamente al
record successivo o al record precedente. Insert predispone la tabella per
l'inserimento di un nuovo record nella posizione del cursore della tabella. Funziona
come Append solamente che l'aggiunta del nuovo record avviene nella posizione
corrente della tabella. Lo stesso dicasi per InsertRecord. IsEmpty permette di
stabilire se la tabella contiene records: restituisce true se non contiene alcun record,
false altrimenti. Locate è un metodo che permette di ricercare dei recortds all'interno
di una tabella. Accetta come parametri un array contenente i nomi dei campi in cui
effettuare la ricerca, una array di valori da ricercare nei corrispondenti campi
dell'array precendente ed un parametro opzione che stabilisce la modalità di ricerca.
Se il metodo trova un record corrispondente ai criteri di ricerca specificati, restituisce
true e posiziona il cursore della tabella in quella posizione, altrimenti restituisce false
e non effettua alcun spostamento. LockTable e UnlockTable sono due metodi che
permattono di bloccare la tabella ad altre applicazioni impedendone l'accesso in
lettura o scrittura a seconda del valore del specificato nel parametro. Lookup
permette di recuperare il contenuto, o parte, di un record corrispondente a
determinati criteri di ricerca. Il funzionamento è simile al metodo Locate tranne per il
fatto che in LookUp va specificato un elenco di campi di cui verrà restituito il valore e
come risultato della chiamata al metodo si otterrà un variant contenente i valori dei
campi speificati nell'array dei campi da restituire. In ogni caso, sia che il record venga
trovato sia no, la posizine corrente della tabella non cambia. MoveBy è un metodo
che permette di spostarsi tra i record della tabella relativamente alla posizione
corrente. Specificando valori negativi, si avrà uno spostamento indietro di tanti record
quanti indicati dal valore specificato, specificando valori positivi, lo spostamento
avverrà in avanti. RenameTable permette di rinominare la tabella fisica connessa
all'oggetto Table aggiornando automaticamente anche tutti gli altri files connessi alla
tabella (es. file di indice).

http://programmazione.html.it/delphi/delphi_36.htm (3 di 9) [21/05/2002 11.27.27]


DELPHI: Componenti per l'accesso ai databases

TQuery

Il componente Query è molto simile al componente Table tranne per il fatto che i dati
non vengono recuperati direttamente da una tabella, ma tramite una query SQL. Sia
la classe TTable che la classe TQuery discendono entrambe dalla classe
TDBDataSet. Il BDE ha integrato un semplice engine SQL (Local SQL) che permette
di effettuare semplici query anche su database che non siano dei server SQL, come
ad esempio delle tabelle dBase o Paradox. Molte delle proprietà e metodi dell'oggetto
Query sono ugualia quelle dell'oggetto Table. In questa sezione vedremo solamente
le proprietà ed i metodi per cui differiscono i due oggetti.

Principalmente le differenze tra gli oggetti Table e Query risiedono nelle seguenti
poprietà: Constrained, ParamCheck, Params, RequestLive, SQL, UniDirectional. La
proprietà Constrained riguarda le tabelle Paradox e DBase. Serve ad indicare se è
permesso o no l'inserimento e la modifica dei dati anche se i nuovi dati non sono
conformi con il comando SELECT utilizzato per ottenere i dati. Per maggiori
informazioni su questa proprietà consultare la guida in linea. La proprietà
ParamCheck indica se la lista dei parametri, utilizzati nel testo della query, va
rigenerata se il testo della query viene modificato a runtime. Ciò è utile quando
vengono create query a runtime utilizzando numero e tipi diversi di parametri.
Params è la lista dei paramteri utilizzati nel testo SQL definito nella proprietà SQL.
Per accedere ai singoli parametri si utilizza la sintassi degli array indicando l'indice
del paramtero che interessa reperire. Esiste anche una proprietà ParamCount, non
accessibile a design-time che permette di recuperare il numero di parametri presenti
nel testo SQL definito. RequestLive indica se l'oggetto Query restituirà un dataset
modificabile o no. È possibile infatti operare su un oggetto Query come su un oggetto
Table, inserendo e modificando i record da esso restituiti. Se RequestLive è
impostato a false, non sarà possibile inserire o modificare, attraverso i metodi
dell'oggetto stesso, i dati dei records restituiti dalla query. Da notare che non tutti
iresult set restituiti dalle query possono essere trattati in questa maniera; alcuni
costrutti infatti resituiscono comunque un result set read-only, percui per modificare i
valori dei records bisogna ricorrere all'esecuzione di una nuova query. SQL è la
proprietà più importante dell'oggetto Query. Esso contiene il testo della query che
verrà eseguita. Il tipo di questa proprietà è TStrings; ciò significa per aggiungere del
codice sql bisogna utilizzare il metodo Add della classe TStrings, oppure assegnare
direttamente il testo sql alla proprietà Text della classe TStrings. La proprietà
UniDirectional stabilisce il tipo di cursore che verrà utilizzato per spostarsi attraverso i
records restituiti dalla query. Impostato su true, lo spostamento sarà possibile
solamente in avanti. Per impostazione predefinita UniDirectional ha valore false,
percui è possibile spostarsi tra i records sia in avanti che in dietro.

Vediamo ora i metodi dell'oggetto Query: ExecSQL, ParamByName, Prepare,


UnPrepare. Il metodo ExecSQL permette di eseguire il testo sql definito in
precedenza nella proprietà SQL nel caso in cui questo non restituisca un result set,
come per esempio potrebbe essere una query di inserimento dati (INSERT) oppure
di eliminazione (DELETE), oppure di aggiornamento (UPDATE). Queste istruzioni sql
infatti non restituiscono un set di dati, come invece accade per SELECT.
ParamByName è un metodo che permette di accedere, in lettura o scrittura, ai valori
dei parametri definiti nel testo sql, specificando il nome del parametro. Ad esempio,
se nel testo sql abbiamo definito un parametro "pippo" (ovviamente le virgolette non
vanno indicate), potremmo accedere in scrittura al suo valore con
NomeOggettoQuery.ParamByName('pippo').Value = valore oppure in lettura con
NomeOggettoQuery.ParamByName('pippo').Value. Per definire un parametro nel
testo sql è necessario far precedere il suo nome dal segno ":" (due punti). Una volta
definito il testo contenente la query da eseguire nella proprietà SQL, completo dei
parametri che si volgioni definire, si possono modificare o specificare le proprietà dei
vari parametri agendo sulla proprietà Params. Qui è possibile impostare il tipo di dato
che esso conterrà (String, Integer, Boolean, ...) la direzione del parametro (parametro
di input o di output). Nella maggior parte dei casi non è necessario modificare questi
valori. I metodi Prepare e UnPrepare servono a ottimizzare la query prima di essere
eseguita. Il primo comunica al BDE o al server remoto il contenuto della query da
eseguire così che il server od il BDE possa predisporre le risorse necessarie
all'effettiva esecuzione della query. Questo permette di migliorare le performances
dell'applicazione. Delphi richiama automaticamente questo metodo se non lo si è
fatto esplicitamente prima di esguire la query. Il metodo UnPrepare agisce in maniera
opposta, liberando le risorse occupate per l'ottimizzazione dell'esecuzione della
query.

http://programmazione.html.it/delphi/delphi_36.htm (4 di 9) [21/05/2002 11.27.27]


DELPHI: Componenti per l'accesso ai databases

TStoredProc

Questo oggetto permette di operare con le Stored Procedures, ovvero quelle


procedure che è possibile definire in alcuni server database (come SQL Server).
Esse risiedono nel server e vengono eseguite nel server.

Le proprietà principali dell'oggetto StoredProc sono: ParamBindMode, Params,


StoredProcName. ParamBindMode determina l'ordine con cui i parametri vengono
assegnati ai parametri contenuti nella stored procedure contenuta nel server.
Possibili valori per questa proprietà sono pbByName o pbByNumber. Nel primo caso
viene seguito la nomenclatura dei parametri. Nel secondo caso viene seguita la
posizione. La proprietà Params è simile alla atessa proprietà dell'oggetto Query;
permette di definire i parametri in ingresso ed in uscita della stored procedure.
StoredProcedure è simile alla proprietà TableName dell'oggetto Table e serve a
specificare il nome della stored procedure presente nel server.

I metodi dell'oggetto StoredProc sono: CopyParams, DescriptionsAvailable,


ExecProc, GetResults, ParamByName, Prepare, UnPrepare. Il metodo CopyParams
fornisce un metodo rapido per copiare i parametri di una stored procedure ad
un'altra. Il metodo DescriptionsAvailable serve a determinare se nel server sono
disponibili informazioni riguardo i parametri della stored procedure; questo metodo
ritorna true se le informazioni sono disponibili o false altrimenti. ExecProc è il
metodo da chiamare per eseguire la stored procedure nel server. Prima di effettuare
la chiamata a questo metodo, occorre specificare i parametri necessari alla stored
procedure ed effettuare una chiamata al metodo Prepare. GetResults serve a
forzare l'invio dei parametri di output da parte dei server Sybase o MSSql. Questi
server necessitano il raggiungimento dell'ultima posizione nel result set prima di
inviare il contenuto dei parametri di output al client. ParamByName ha la stessa
funzione del metodo ParamByName dell'oggetto Query. Prepare e UnPrepare, lo
abbiamo già visto, hanno la funzione di assegnare i valori a i parametri della stored
procedure e all'ottimizzazione prima dell'esecuzione della stessa. UnPrepare rilascia
le risorse occupate con il metodo Prepare.

TDatabase

L'oggetto Database permette di avere un controllo totale sulle proprietà della


connessione al database. Tramite questo oggetto è possibile gestire il login ai server
di database, controllo sulle transazioni. Delphi crea sempre, quando si utilizzano
componenti per l'accesso alle basi di dati un oggetto Database e un oggetto Session,
anche se non viene richiesto dall'utente.

Le proprietà fondamentali di questo oggetto sono: AliasName, Connected,


DatabaseName, DataSets, DriverName, Exclusive, KeepConnection, Params,
SessionName, ReadOnly, TransIsolation, LoginPrompt. La prima proprietà,
AliasName, specifica l'alias BDE da utilizzare per accedere al database. Connected
indica se la connessione al database è attiva. Impostare a false questa proprietà
determina la disconnessione del database, al contrario impostarla a true ne
determina la connessione. DatabaseName è il nome del database presente nel
server database oppure il percorso fisico dove sonovisualizzate le tabelle dBase o
Paradox. Questa proprietà può essere modificata solamente a connessione non
attivata, pena il sollevamento di una eccezione. DataSets è un array contenente
l'elenco dei dataset correntemente aperti. Attraverso questa proprietà è possibile
accedere alle proprietà e metodi dei datasets attivi per il database indicato.
DriverName, similmente alla stessa proprietà dell'oggetto Table, indica il nome del
driver utilizzato per l'accesso al database. Questa proprietà va utilizzata quando non
viene indicato un alias BDE per l'oggetto Database. Exclusive ha la stessa funzione
della proprietà Exclusive dell'oggetto Table: impedisce l'accesso al database ad altre
applicazioni. La proprietà KeepConnection serve a far si che la connessione al
database sia attiva anche se non vi sono dataset aperti. Se KeepConnection è true,
la connessione viene mantenuta attiva anche se non si sono dataset aperti, se ha
valore false, la connessione viene chiusa con la chiusura dell'ultimo dataset. Ciò
comporta tempi più lunghi di accesso poichè sarà necessario rieffettuare la
connessione al database. Params è una proprietà che permette di specificare dei
parametri relativi al database. Ad esempio è possibile indicare tra i parametri una
UserId e Password per l'accesso al database così da non doverli digitare tutte le
volte che si accede al database. I parametri da specificare variano da driver a driver

http://programmazione.html.it/delphi/delphi_36.htm (5 di 9) [21/05/2002 11.27.27]


DELPHI: Componenti per l'accesso ai databases

e i parametri specificati dall'utente vanno a sovrascrivere i parametri di default


assegnati dal BDE. SessioneName contiene il nome dell'oggetto Sessione a cui il
database è associato. Utilizzare le sessioni è utile quando è necessario avere
connessioni distinte dalla stessa applicazione verso lo stesso database. Potrebbe
essere il caso di una applicazione server che risponda alle richieste dei client per la
fornitura di informazioni da un database. Lasciando questa proprietà vuota, farà si
che Delphi associ questo database alla sessione di default. ReadOnly specifica se il
database sarà accessibile in sola lettura, similmente alla stessa proprietà degli
oggetti Table e Query. TransIsolation permette di indicare illivello di isolamento per
le transazioni controllate dal BDE. Ogni server di database ha dei caratteristici livelli
di isolamento. Per un elenco consultare la documentazione in linea riguardante la
proprietà TarnsIsolation dell'oggetto Database. La proprietà LoginPrompt permette
di specificare se prima dell'apertura della connessione al database venga
visualizzata o no una finestra di dialogo per la digitazione delle informazioni di login:
user name e password.

Metodi dell'oggetto Database: CloseDatasets, Commit, RollBack, StartTransaction,


Close, Open. Il metodo CloseDatasets permette di chiudere tutti i datasets attivi
relativi al database senza disconnettersi dal server. Commit viene utilizzato insieme
ai metodi StartTransaction e RollBack. Questi metodi permettono di implementare
il controllo delle transazioni. Per avviare una transazione, ovvero un insieme di
operazioni su tabelle, query, che devono necessariamente essere portati a termine
con successo perchè i dati siano validi, utilizzare il metodo StartTransaction. Se tutte
le operazioni intermedie sono state completate con successo, si può utilizzare il
metodo Commit per rendere effettive le modifiche oppure, in caso contrario, utilizzare
il metodo RollBack per annullarle e ripristinare automaticamente lo stato precedente
dei dati. I metodi Open ed Close sono utilizzati per aprire e chiudere la connessione
al database. Per connettersi al server di database è anche possibile utilizzare la
proprietà Connected impostandola su true per la connessione e a false per la
disconnessione.

TSession

L'oggetto Session permette all'applicazione di gestire gruppi di connessioni a


database. L'oggetto sessione viene utilizzato per accedere a tabelle paradox in rete
da più postazioni contemporaneamente e per gestire più connessioni simultanee allo
stesso database, (ad esempio una applicazione server web che recuperi dati da
database su richiesta dei clients deve gestire una sessione per ogni connessione
client richiesta). In pratica la sessione ha lo scopo di isolare le attività tra sessioni
differenti.

Le proprietà principali dell'oggetto Sessione sono: Active, AutoSessionName,


ConfigMode, DatabaseCount, Databases, KeepConnections, NetFileDir, PrivateDir,
SessionName, SQLHourGlass. La proprietà Active, di tipo boolean, indica quando
la sessione è attiva oppure no. Essa può anche essere utilizzata per attivare o
disattivare la sessione impostandone il valore. AutoSessionName è una proprietà
molto utile, non presente nella classe TSession contenuta nelle versioni precedenti di
Delphi. Essa permette di far si che venga generato automaticamente un nome
univoco per la sessione. Ciò è utile quando viene utilizzato l'oggetto Sesisone in
applicazioni multithread dove è richiesta l'univocità del nome della sessione. La
proprietà ConfigMode definisce il modo in cui vengono gestiti gli alias all'interno
della sessione. DatabaseCount contiene il numero di databases che si riferiscono
alla sessione. La proprietà Databases è un elenco di riferimenti ad oggetti Database
(TDatabase) che si riferiscono alla sessione. KeepConnections ha la stessa
funzione della stessa proprietà nell'oggetto Database. Si può, tramite questa
proprietà, decidere se un database che si riferisce alla sessione, possa mantenere la
connessione al server anche in assenza di dataset attivi. NetFileDir è proprietà che
viene utilizzata con tabelle di database Paradox. Essa indica il percorso completo del
file di condivisione in rete per le tabelle paradox (PDOXUSRS.NET). PrivateDir
indica invece il percorso completo, locale, per l'appoggio di file temporanei generati
dal BDE. Questa proprietà viene utilizzata per settare nella macchina client un
percorso (locale) per la creazione di tabelle temporanee per velocizzare l'accesso ai
dati nel server. La proprietà SQLHourGlass permette di specificare se il cursore
cambierà forma durante l'attesa di dati dal server SQL. L'impostazione di questa
proprietà va effettuata prima di attivare la sessione.

Molto più lunga è la lista dei metodi dell'oggetto Session. Questi permettono di
operare sugli alias di database, sui drivers di database, sui databases. Essi sono:

http://programmazione.html.it/delphi/delphi_36.htm (6 di 9) [21/05/2002 11.27.27]


DELPHI: Componenti per l'accesso ai databases

AddAlias, AddDriver, AddPassword, AddStandardAlias, Close, CloseDatabase,


DeleteAlias, DeleteDriver, DropConnections, FindDatabase, GetAliasDriverName,
GetAliasNames, GetAliasParams, GetConfigParams, GetDatabasesNames,
GetDriverNames, GetDriverParams, GetPassword, GetStoredProcNames,
GetTablesNames, IsAlias, ModifyAlias, ModifyDriver, Open, OpenDatabase,
RemoveAllPasswords, RemovePassword, SaveConfigFile. Attraverso AddAlias,
ModifyAlias, DeleteAlias è possibile rispettivamente aggiungere, modificare,
eliminare un alias di database nella sessione corrente. Il primo di questi metodi
accetta come parametri il nome dell'alias, il nome del driver e una lista di parametri di
tipo TStrings. Attraverso la proprietà ConfigMode dell'oggetto Sessione è possibile
indicare se gli alias creati all'interno della sessione saranno visibili da altre sessioni. Il
secondo accetta il nome dell'alias da modificare e la lista contenente i parametri da
moficiare ed i nuovi valori. L'ultimo accetta solamente il nome dell'alias da eliminare.
Stesse funzioni hanno i metodi AddDriver, ModifyDriver, DeleteDriver. Il primo
permette di aggiungere un driver di database ed accetta come parametri il nome del
driver ed una lista di parametri di configurazione. ModifyDriver accetta come
parametri il nome del driver da modificare ed una lista (TStrings) dei parametri di
configurazione da modificare. DeleteDriver accetta il solo nome del driver da
eliminare. AddPassword viene utilizzato per aggiungere una password di accesso a
tabelle paradox cifrate. Se si accede ad una tabella paradox cifrata e non è stata
definita una password a livello di sessione o non viene fornita una password
nell'evento OnPassword Delphi visualizza automaticamente una finestra di dialogo
che richiede l'inserimento di una password valida. Per eliminare una password
aggiunta utilizzare il metodo RemovePassword specificando la password da
eliminare. Per eliminare tutte le password specificate utilizzare il metodo
RemoveAllPassword. AddStandardAlias è simile al metodo AddAlias tranne per il
fatto che permette di agiungere solamente alias per tabelle paradox, dbase o ASCII. I
metodi Open e Close permettono di attivare o disattivare la sessione. La chiusura di
una sessione comporta la disconnessione di tutti i databases attivi. OpenDatabase
permette di attivare la connessione ad un database, appartenente alla sessione
corrente, specificandone il nome. Se il nome del database non esiste nell'elenco dei
databases appartenenti alla sessione viene creato un database temporaneo e
restituito un riferimento a questo come risultato. Per disconnettere ad un database
appartenente alla sessione corrente, si può utilizzare il metodo CloseDatabase
fornedogli un riferimento al database che si intende disconnettere. Il metodo
DropConnections ha lo scopo eliminare tutti i database temporanei creati dalla
sessione. Questo metodo è necessario perchè se la proprietà KeepConnections è
true, i database temporanei creati durante la sessione, non vengono eliminati
automaticamente quando vengono chiusi. FindDatabase ricerca nell'elenco dei
database appartenenti alla sessione corrente il database specificato e ne restituisce
un riferimento. Il metodo GetAliasDriverName restituisce il nome del driver utilizzato
da un particolare alias di database il nome viene passato come parametro al metodo
stesso. GetAliasNames fornisce un elenco (TStrings) di nomi di alias di database
disponibili nella configurazione del BDE. GetAliasParams, restituisce in una lista di
tipo TStrings un elenco dei parametri impostati per l'alias di database specificato. La
lista è in formato PARAMETRO=VALORE. Il metodo GetConfigParams ha lo scopo
di recuperare l'elenco dei parametri con i rispettivi valori dalla configurazione del
BDE. Il metodo prende in ingresso un percorso (corrispondente al ramo della
configurazione di cui si vogliono i parametri) un nome di sessione ed un riferimento
ad una lista di stringhe (TStrings) che verrà riempita con i parametri recuperati.
GetDatabaseNames restituisce un elenco di tutti gli alias di database definiti nella
cinfigurazione del BDE e tutti i database conosciuti dalla sessione corrente. In
maniera simile agisce il metodo GetDriverNames che restituisce un elenco di tutti i
driver di database disponibili nella sessione. GetDriverParams, in maniera simile al
metodo GetAliasParams, restituisce in una lista di stringhe di tipo TStrings, un elenco
dei parametri associati al driver di database specificato. Il metodo GetPassword
permette di recuperare una password per l'accesso ad un database. Effettuando una
chiamata al metodo, viene prima attivato l'evento OnPassword e, nel caso in cui non
fosse definito un gestore di evento, visualizzata la finsetra di dialogo standard per la
richiesta della password. Il metodo restituisce true se viene recuperata una
password, false altrimenti. GetStoredProcNames al pari di altri metodi già visti,
restituisce un elenco delle stored procedure presenti in database appartenente alla
sessione corrente. GetTableNames restituisce l'elenco delle tabelle appartenenti ad
un database, specificato, appartenete alla sessione. Alcuni parametri passati a
questo metodo, permettono di filtrare l'elenco delle tabelle in base al nome,
all'estenzione (tabelle paradox, dbase), di indicare se resituiture anche le tabelle di
sistema del database (SQL servers databases). Il metodo IsAlias permette di
determinare se un determinata stringa passatagli, corrisponde ad un alias di
database conosciuto dalla sessione. Il metodo SaveConfigFile è importante quando
si vogliono rendere persistenti le modifiche apportate alla configurazione del BDE,
ovvero agli alias di datbase o ai drivers. Alcune modifiche non verrano salvate se il

http://programmazione.html.it/delphi/delphi_36.htm (7 di 9) [21/05/2002 11.27.27]


DELPHI: Componenti per l'accesso ai databases

parametro ConfigMode dell'oggetto Session è impostato su cmSession.

TBatchMove

L'oggetto BatchMove ha lo scopo di permettere alle applicazioni di effettuare


operazioni batch (spostamenti, eliminazioni, trasformazioni, etc) su gruppi di records
o su intere tabelle. Questo oggetto opera su due tabelle, una sorgente ed una di
destinazione. Possiede una gestione dei problemi riguardanti violazioni di chiave od
altri prolemi durante l'esecuzione della procedura batch. Nel caso vi siano errori,
questi vengono riportati in due tabelle.

Ecco l'elenco delle sue proprietà: AbortOnKeyViol, AbortOnProblem, ChangedCount,


ChangedTableName, CommitCount, Destination, KeyViolCount, KeyViolTableName,
Mappings, Mode, MovedCount, ProblemCount, ProblemTableName, RecordCount,
Source, Transliterate. AbortOnKeyViol e AbortOnProblem sono due proprietà che
definiscono il comportamento dell'oggetto nel caso si verifichino errori. Impostando
una o entrambe queste proprietà a true l'operazione batch verrà annullata.
ChangedCount riporta il numero di record che sono stati modificati.
ChangedTableName specifica il nome della tabella paradox che conterrà una copia
dei records originali che sono stati modificati. CommitCount permette di specificare
quanti records devono essere modificati prima che le modifiche vengano applicate
effettivamente. Destination e Source specificano i nomi degli oggetti Table che
verranno utilizzati rispettivamente come tabella sorgente e come tabella di
destinazione. KeyViolCount riporta il numero di violazioni di chiave incontrati
durante l'esecuzione della procedura batch. La proprietà KeyViolTableName indica il
nomefile di una tabella paradox utilizzata per contenere i records che hanno generato
una violazione di chiave. Mappings è una proprietà di tipo TStrings che permette di
specificare la corrispondenza dei campi tra la tabella sorgente e quella di
destinazione. Il formato da utilizzare è del tipo
NOMECAMPODESTINAZIONE=NOMECAMPOSORGENTE. Per far si che il
contenuto di un campo nella tabella sorgente vada in un campo con lo stesso nome
nella tabella di destinazione, indicarne solamente il nome. La proprietà Mode
determina iltipo di operazione batch da effettuare. Valori validi per questa proprietà
sono: batAppend, batUpdate, batAppendUpdate, batCopy, batDelete. La modalità di
funzionamento predefinita è batAppend. Consultare la guida in linea per un
approfondimento sulle funzioni dei valori indicati. MovedCount riporta il numero di
records che sono stati processati e trasferiti nella tabella di destinazione.
ProblemCount indica il numero di records che non sono stati aggiunti alla tabella di
destinazione per il verificarsi di problemi. ProblemTableName specifica il nomefile di
una tabella parado che conterrà i records che non sono stati aggiunti alla tabella di
destinazione a causa di errori. RecordCount indica il numero massimo di record che
verranno spostati dalla tabella sorgente a quella di destinazione. La proprietà
Transliterate specifica se durante l'operazione batch deve essere effettuata una
conversione in base al linguaggio del driver utilizzato per la tabella di destinazione.

L'unico metodo rilevante per questo oggetto è il metodo Execute che avvia la
procedura batch.

TUpdateSQL

L'oggetto UpdateSQL permette di specificare degli scripts SQL per la modifica,


l'inserimento e l'eliminazione di records in dataset di sola lettura di tipo TQuery o
TStoredProc.

Le proprietà di questo oggetto sono: DataSet, DeleteSQL, InsertSQL, ModifySQL,


Query, SQL. La proprietà Dataset indica il componente TQuery o TStoredProc che
utilizzerà l'oggetto UpdateSQL. DeleteSQL, InsertSQL, ModifySQL, sono tre
proprietà di tipo TStrings che conterranno gli scripts SQL per, rispettivamente,
eliminare, inserire, modificare il contenuto del dataset collegato. La proprietà Query
restituisce, in base all'indice specificato, l'oggetto TQuery utilizzato per eliminare,
inserire o modificare i dati del dataset. Valori di indice validi sono: ukDelete, ukInsert,
ukModify. In maniera simile alla proprietà precedente, SQL resituisce la stringa SQL
utilizzata per eliminare, inserire, modificare il dataset.

http://programmazione.html.it/delphi/delphi_36.htm (8 di 9) [21/05/2002 11.27.27]


DELPHI: Componenti per l'accesso ai databases

I metodi dell'oggetto UpdateSQL sono: Apply, ExecSQL, SetParams. Il metodo


Apply permette di assegnare i parametri alla stringa SQL corrispondente all'indice
specificato e di eseguirla per aggiornare il record. ExecSQL in maniera simile ad
Apply esegue il codice SQL in base all'ndice specificato. Da notare però che se il
codice SQL specificato contiene dei parametri, occorre chiamare prima il metodo
SetParams.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_36.htm (9 di 9) [21/05/2002 11.27.27]


DELPHI: Componenti per la manipolazione dei dati (Data-Aware)

LEZIONE 36: Componenti per la manipolazione dei dati (Data-


Aware)

Dopo aver trattato i componenti per l'accesso alle sorgenti di dati, passiamo ora a
vedere i componenti che ci permettono di interagire in maniera visuale con i dati
provenienti da queste sorgenti dati. Come visibile nell'immagine qui sotto, questi
componenti sono raccolti nella pagina "Data Controls" della Component Palette.

Questa pagina ci mette a disposizione una griglia, una barra per la navigazione
all'interno dei nostri datasets, una etichetta collegata direttamente ad un campo di un
dataset, una casella di edit, un memo, un componente immagine da collegare
direttamente al campo del nostro dataset per visualizzare l'immagine in esso
contenuta, una casella combinata, una lista, una casella spuntabile per valori
booleani, un gruppo di componenti per selezioni esclusive, una casella di lookup, una
lista di lookup, un componente per formato RTF, una griglia di controllo, una
componente per la visualizzazione dei dati in forma grafica (Torte, Istogrammi, ...). Si
tratta di componenti Data-Aware, ovvero componenti che si collegano direttamente
alla sorgente di dati e che automaticamente gestiscono la visualizzazione, la
modifica. Questi componenti vengono collegati al dataset indirettamente, ovvero
vengono collegati ad un datasource che fa riferimento al dataset interessato.

Come per gli oggetti per l'accesso alle sorgenti di dati, daremo uno sguardo alle
proprietà e metodi principali e più utilizzati di questi componenti. Segue un elenco dei
nomi dei componenti e le loro rispettive classi: DBGrid (TDBGrid), DBNavigator
(TDBNavigator), DBLabel (TDBLabel), DBEdit (TDBEdit), DBMemo (TDBMemo),
DBListBox (TDBListBox), DBComboBox (TDBComboBox), DBCheckBox
(TDBCheckBox), DBRadioGroup (TDBRadioGroup), DBLookupListBox
(TDBLookupListBox), DBLookupComboBox (TDBLookupComboBox), DBRichEdit
(TDBRichEdit), DBCtrlGrid (TDBCtrlGrid), DBChart (TDBChart).

TDBGrid

L'oggetto DBGrid permette di visualizzare e manipolare i records di un dataset in una


griglia tipo quella utilizzata da programmi come MS Excel. Questo oggetto deriva da
TCustomDBGrid che a sua volta deriva da TCustomGrid. Tratteremo solamente le
proprietà ed i metodi relativi all'oggetto TCustomDBGrid poichè la classe TDBGrid
non aggiunge alcun metodo o proprietà, ma espone solamente i metodi della classe
da cui deriva, appunto TCustomDBGrid.

Ecco l'elenco delle proprietà dell'oggetto DBGrid: Columns, DataSource,


DefaultDrawing, FieldCount, Fields, Options, ReadOnly, SelectedRows, TitleFont,
BorderStyle, FixedColor. La proprietà Columns, di tipo TDBGridColumns, permette di
definire le colonne che verranno visualizzate nella griglia. Questa proprietà è un
elenco di oggetti TColumn che definiscono le proprietà delle varie colonne che
andranno a formare la tabella, a partire dal campo del dataset a cui si riferisce la
colonna, al colore del titolo, l'allineamento del testo etc... Per modificare la proprietà
Columns, è disponibile un editor di proprietà, avviabile, come sempre, attraverso il
bottone con i tre puntini nel Object Inspector. Se non vengono definite delle colonne
per la griglia, verranno automaticamente visualizzate tante colonne quanti sono i
campi visibili del dataset. La proprietà DataSource è quella che permette di
impostare il riferimento all'oggetto DataSource che a sua volta si riferisce al dataset
(Table, Query). DefaultDrawing indica se le celle della griglia verranno disegnate
automaticamente oppure il loro disegno verrà personalizzato attraverso i gestori di
eventi OnDrawColumnCell o OnDrawDataCell. Se impostato su True l'oggetto griglia
si occuperà direttamente del disegno dei dati nella cella, dal colore di sfondo, al font,

http://programmazione.html.it/delphi/delphi_37.htm (1 di 5) [21/05/2002 11.27.38]


DELPHI: Componenti per la manipolazione dei dati (Data-Aware)

al valore dei dati, se impostato su false, verrà gestito dall'oggetto griglia solamente il
disegno dello sfondo, il disegno dei valori dei dati è demandato ai gestori di eventi
OnDrawColumnCell o OnDrawDataCell. FieldCount è una proprietà che indica il
nome dei campi, ovvero delle colonne, visualizzate nella griglia. La proprietà Fields
permette di accedere in forma indicizzata i campi corrispondenti alle colonne
visualizzate, come per la proprietà Fields dell'oggetto Table. Attraverso la proprietà
Options è possibile definire alcune caratteristiche funzionali e grafiche della griglia.
Alcune di esse sono la possibilità di editare il contenuto della griglia, la
visualizzazione di un indicatore di riga, la visualizzazione dei titoli al disopra delle
colonne, la possibilità di selezionare un'intera riga della griglia, effettuare una
selezione su più righe, richiedere la conferma prima di eliminare un record dal
dataset. I valori disponibili per la proprietà sono: dgEditing, dgAlwaysShowEditor,
dgAlwaysShowEditor, dgRowSelect, dgTitles, dgIndicator, dgColumnResize,
dgColLines, dgRowLines, dgTabs, dgRowSelect, dgAlwaysShowSelection,
dgConfirmDelete, dgCancelOnExit, dgMultiSelect. La proprietà ReadOnly, è
autoesplicativa, ovvero permette di impostare in sola lettura il contenuto della griglia
così da impedirne la modifica. SelectedRows, riporta un elenco di Bookmark
(Segnalibri che vengono utilizzati per segnare una posizione come selezionata
all'interno della griglia) corrispondenti alle posizioni selezionate nella griglia. La
proprietà TitleFont permette di impostare il font per le celle contenenti i titoli delle
colonne. Attraverso la proprietà BorderStyle, si può definire il tipo di bordo disegnato
intorno alla griglia. In ultima, la proprietà FixedColor definisce il colore delle celle
della griglia che rimangono fisse durante lo scorrimento della stessa.

Dopo aver visto le proprietà passiamo ora ai metodi. Eccone l'elenco:


DefaultDrawColumnCell, DefaultDrawDataCell. DefaultDrawColumnCell viene
utilizzato all'interno di un gestore di evento OnDrawColumnCell per il disegno del
testo. Il risultato della chiamata a DeafulDrawColumnCell è lo stesso che impostare
la proprietà DefaultDrawing a True, tranne per il fatto che che non viene disegnato il
rettangolo di fuoco della cella. DefaultDrawDataCell è l'equivalente di
DefaultDrawColumnCell tranne per il fatto che in questo non vi è un riferimento alla
colonna a cui appartiene la cella.

TDBNavigator

Il componente DBNavigator, è un componente visuale che permette di facilitare lo


spostamento all'interno di dataset mettendo a disposizione una barra contenente dei
pulsanti corrispondenti alle tipiche funzioni di spostamento. Ci si può spostare in
avanti, in dietro, alla prima posizione, all'ultima, aggiungere una nuova posizione,
eliminare quella corrente, annullare le modifiche apportate ai dati.

Le proprietà di DBNavigator sono accessibili quasi completamente dall'Object


Inspector a design-time e sono: ConfirmDelete, DataSource, Flat, Hints,
VisibleButtons. ConfirmDelete permette di specificare se verrà richiesta la conferma
per l'eliminazione del record oppure no. Impostando questa proprietà a true, verrà
presentata una finestra di dialogo attraverso la quale sarà possibile decidere se
continuare con l'eliminazione oppure annullare l'operazione. DataSource è la
proprietà che serve ad indicare quale è il dataset a cui fa riferimento l'oggetto
DBNavigator. La proprietà Flat ha lo scopo di definire l'aspetto dei pulsanti della
barra di navigazione, ovvero se questi avranno uno stile piatto oppure tradizionale.
Attraverso la proprietà Hints si può definire il contenuto dei tooltip che vengono
visualizzati quando ci si ferma sopra i pulsanti della barra. Questa proprietà potrebbe
essere utilizzata per esempio per visualizzare in italiano la funzione dei vari pulsanti
della barra visto che di default il testo visualizzato è in inglese. L'ultima proprietà è
quella che definisce quali pulsanti visualizzare nella barra. Non sempre sono
necessari tutti i pulsanti presenti nella barra, ad esempio potrebbero servire
solamente i pulsanti di spostamento e non quelli di inserimento, modifica ed
eliminazione. Tramite VisibleButtons, di tipo TButtonSet, è possibile specificare
l'elenco dei pulsanti da visualizzare.

Per quanto riguarda i metodi dell'oggetto DBNavigator, interessante è il metodo


BtnClick che permette di eseguire la funzione associata al pulsante nella barra di
navigazione, come se il pulsante fosse stato premuto dall'utente. Questo metodo
accetta come parametro il tipo di pulsante che di cui si vuole simulare la pressione
(nbFirst, nbPrior, nbNext, nbLast, nbInsert, nbDelete, nbEdit, nbPost, nbCancel,
nbRefresh). L'esecuzione del metodo attiva comunque l'evento OnClick dell'oggetto
DBNavigator.

http://programmazione.html.it/delphi/delphi_37.htm (2 di 5) [21/05/2002 11.27.38]


DELPHI: Componenti per la manipolazione dei dati (Data-Aware)

TDBText

L'oggetto DBText è un oggetto visuale che permette di visualizzare il contenuto


testuale di un campo di un dataset. Attraverso questo oggetto l'utente nonpuò
modificare il contenuto del campo visualizzato.

Le proprietà dell'oggetto DBText sono: AutoSize, DataField, DataSource. La


proprietà AutoSize dell'oggetto DBText indica se la dimensione dell'oggetto viene
modificata automaticamente rispecchiando la dimensione del testo da visualizzare.
Impostare AutoSize a false a volte è utile per impedire che il testo da visualizzare
vada a sovrapporsi ad altri controlli nelle schermate del programma. DataField
contiene il nome del campo del dataset di cui verra visualizzato il contenuto.
DataSource conterrà il riferimento all'oggetto DataSource a cui è collegato il dataset
a cui collegare l'oggetto DBText.

TDBEdit

TDBEdit è un oggetto visuale rappresentante una casella di testo dove è possibile


visualizzare, inserire, modificare del testo contenuto in un campo di un dataset.
Questo oggetto deriva da altri oggetti non connessi direttamente a datasets come
CustomMaskEdit e CustomEdit. Per le proprietà ereditate da questi oggetti si
rimanda alla guida in linea.

Come per altri oggetti connessi ai dataset, le proprietà più importanti sono DataField,
DataSource e ReadOnly. Per le prime due il significato è lo stesso degli oggetti visti
in precedenza, ReadOnly è presente, come nel nostro caso, in quei componenti che
permettono la modifica dei dati. Attraverso questa proprietà, impostando il suo valore
a true, si impedisce la modifica dei dati visualizzati nel solo componente DBEdit
interessato.

TDBMemo

Il componente DBMemo è simile al componente DBEdit, ha la funzione di


visualizzare il testo contenuto in un campo di un dataset con la differenza che il testo
viene visualizzato su più righe. Le proprietà fondamentali sono le stesse dell'oggetto
DBEdit, mentre per le proprietà relative all'oggetto TCustomMemo, da cui DBMemo
deriva, consultare la guida in linea.

TDBImage

L'oggetto DBImage viene utilizzato per visualizzare ed inserire immagini in un campo


di un dataset. Solitamente questo oggetto viene collegato a campi di tipo blob.

Oltre alle proprietà comuni a tutti i componenti data-aware, l'oggetto DBImage


possiede le seguenti proprietà: AutoDisplay, BorderStyle, Center, Picture,
QuickDraw, Stretch. La proprietà AutoDisplay, indica se l'immagine contenuta nel
campo viene automaticamente caricata e visualizzata. Impostando questa proprietà a
false, per visualizzare l'immagine sarà necessario effettuare un doppio click
sull'oggetto oppure selezionare l'oggetto e premere enter. Attraverso BorderStyle è
possibile definire l'aspetto del bordo dell'immagine che può essere: bsNone,
bsSingle, bsSizeable, bsDialog, bsToolWindow, bsSizeToolWin. La proprietà Center
indica se l'immagine verrà visualizzata al centro dell'oggetto DBImage. Per ovvi
motivi, questa proprietà non avrà alcun effetto se la proprietà Stretch sarà impostata
a true. La proprietà Picture contiene un riferimento all'immagine contenuta nel
campo del dataset. Questa proprietà risulta molto utile per caricare una immagine nel
campo blob oppure per salvare su file il contenuto del campo blob stesso. Per
maggiori informazioni sui metodi e sulle proprietà dell'oggetto Picture consultare la
guida in linea riguardo alla classe TPicture. Attraverso QuickDraw, è possibile
velocizzare la visualizzazione dell'immagine, disabilitando l'utilizzo di una palette di

http://programmazione.html.it/delphi/delphi_37.htm (3 di 5) [21/05/2002 11.27.38]


DELPHI: Componenti per la manipolazione dei dati (Data-Aware)

colori, ottenendo però una qualità inferiore dell'immagine. Impostando questa


proprietà a false, verrà utilizzata una palette di colori per permettere una
visualizzazione migliore dell'immagine, introducendo dei ritardi di visualizzazione
causati dall'elaborazione più complessa. La proprietà Stretch permette di indicare se
l'immagine visualizzata verrà riadattata alle dimensioni dell'oggetto DBImage
effettuando appunto un stretching, con conseguente probabile deformazione o
perdita di definizione dell'immagine.

I metodi dell'oggetto DBImage sono: CopyToClipBoard, CutToClipBoard,


LoadPicture, PasteFromClipBoard. I metodi relativi alla clipboard (gli appunti di
Windows) sono abbastanza autoesplicativi e servono appunto a copiare ed a tagliare
il contenuto mettendolo nella clipboard o ad incollarlo prelevandolo da essa.
LoadPicture ha lo scopo di caricare l'immagine dal campo del dataset. Questo
metodo è necessario per visualizzare il contenuto dell'immagine se la proprietà
AutoDisplay è stata impostata false.

TDBListBox, TDBComboBox, TDBRichEdit, TDBCheckBox,


TDBRadioGroup

Per tutti questi componenti, non esistono particolari proprietà o metodi all'infuori di
quelli già visti comuni a tutti i componenti Data-Aware (DataSource, DataField, etc.).
Pertanto per le proprietà di questi controlli si rimanda alla guida in linea di Delphi.
L'unica differenza interessante tra la versione non Data-Aware e la versione standard
riguarda il controllo TDBRadioGroup che possiede una proprietà Values che ha lo
scopo di permettere l'assegnazione di valori personalizzati da salvare nel campo del
Dataset. Questa proprietà è di tipo TStrings e se non vengono definiti dei valori per
essa, nel campo dele dataset verrà salvato l'indice, in base 0, della voce selezionata.

TDBLookupListBox, TLookupComboBox

Questi due controlli, sono in tutto simili ai rispettivi TDBListBox e TDBComboBox


tranne per il fatto che i valori riportati nelle liste di scelta, provengono da un dataset.
Questi controlli sono collegati a due dataset: uno è il dataset di lavoro (dove vengono
inseriti i valori del controllo) e l'altro quello da dove vengono recuperati i valori che
appaiono nelle liste di scelta. Per quanto riguarda il collegamento al dataset
principale, non c'è nulla di nuovo da dire, il collegamento segue le regole già viste.
Per il dataset di origine dei valori delle liste, ci troviamo di fronte a delle nuove
proprietà che sono comuni a tutti quei componenti di tipo lookup che derivano da
TDBLookupControl.

Queste proprietà sono: KeyField, KeyValue, ListField, ListFieldIndex, ListSource.


Tutte queste proprietà, all'infuori di KeyValue, sono accessibili a design-time tramite
l'Object Inspector. KeyField è di tipo string e indica il campo del dataset sorgente per
la lista da cui verranno estratti i dati da inserire nel campo del dataset principale
indicato da DataField. KeyValue contiene il valore del campo indicato in KeyField.
Esso non corrisponde al valore visualizzato dal controllo. Assegnando a questa
proprietà un valore, il controllo effettuerà una ricerca all'interno del dataset sorgente
per la lista alla ricerca di un record il cui contenuto del campo riportato in KeyField
corrisponde al valore riportato in KeyValue. ListField, è di tipo string e contiene il
campo del dataset sorgente per la lista il cui valore sarà visualizzato dal controllo.
Nel caso di TDBLookupComboBox, sara il campo i cui valori appariranno nella lista a
discesa visualizzata dal controllo. ListFieldIndex indica l'indice che verrà utilizzato
per ordinare la lista dei valori visualizzati dal controllo. ListSource contiene il
riferimento al datasource che si riferisce al dataset sorgente per la lista di valori.

TDBCtrlGrid

Questo controllo è particolare ma molto utile per creare una visualizzazione


particolare del contenuto dei records di un dataset. Esso permette di raggruppare in
pannelli i contenuti dei records di un dataset, permettendo di disporre a piacere i
controlli che effettivamente visualizzano il contenuto dei campi dei records. A
differenza degli altri componenti data-aware che visualizzano o gestiscono

http://programmazione.html.it/delphi/delphi_37.htm (4 di 5) [21/05/2002 11.27.38]


DELPHI: Componenti per la manipolazione dei dati (Data-Aware)

direttamente il contenuto dei campi dei records, il controllo DBCtrlGrid non possiede,
ovviamente, la proprietà FieldName. Questo controllo lavora con i records piuttosto
che con i campi dei records. Infatti se viene posto in una form un controllo DbCtrlGrid
senza inserirvi degli altri controlli data-aware all'interno che gestiscano il contenuto
dei campi dei records non si vedrà apparire nulla all'interno di esso. Tra le
caratteristiche impostabili di questo controllo vi è la possibilità di scegliere il numero
di righe e colonne, l'orientamento, la dimensione dei pannelli ed altre caratteristiche.

Ecco le proprietà principali di questo controllo: AllowDelete, AllowInsert, Canvas,


ColCount, DataSource, EditMode, Orientation, PanelBorder, PanelCount,
PanelHeight, PanelIndex, PanelWidth, RowCount, SelectedColor, ShowFocus. Le
proprietà AllowDelete e AllowInsert indicano se il controllo può permettere
all'utente l'inserimento di un nuovo record oppure l'eliminazione di quello corrente.
Queste due operazioni possono essere effettuate attraverso la pressione
rispettivamente dei tasi Ctrl+Delete e Ctrl+Insert sulla tastiera quando il controllo
possiede il fuoco. La proprietà Canvas, disponibile solamente a run-time, permette di
accedere al controllo Canvas che gestisce la grafica dei pannelli della griglia.
ColCount e RowCount, permettono di specificare rispettivamente il numero di
colonne e di righe che verranno rappresentati contemporaneamente nel controllo.
Queste proprietà definiscono altresì il numero di pannelli visibili nella griglia. Questo
valore è reperibile nella proprietà PanelCount disponibile a run-time. DataSource
definisce il riferimento alla sorgente dati collegata al dataset a cui la griglia sarà
collegata. EditMode permette di stabilire la modalità di funzionamento della griglia,
ovvero se questa può permettere l'inserimento l'eliminazione e la modifica dei
records, un po' come la proprietà ReadOnly di altri controlli. PanelBorder definisce lo
stile del bordo dei pannelli nella griglia. I valori possibili sono gbNone o gbRaised.
Attraverso le proprietà PanelHeight e PanelWidth è possibile definire le dimensioni
dei pannelli, rispettivamente altezza e larghezza. SelectedColor definisce il colore
che verrà utilizzato per indicare il pannello corrispondente al record corrente nel
dataset. In ultimo ShowFocus permette di indicare se il controllo DBCtrlGrid deve
disegnare un rettangolo di fuoco attorno al pannello corrispondente al record corrente
quando il controllo ha il fuoco.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_37.htm (5 di 5) [21/05/2002 11.27.38]


DELPHI: I Reports

LEZIONE 36: I Reports

Nelle sue distribuzioni standard, Delphi fornisce dei componenti per creare, organizzare e
stampare i resoconti delle eleborazioni su database effettuate dalle nostre applicazioni.
Questo insieme di componenti costituiscono la suite QuickReport che permette di creare
visualmente lo schema da utilizzare per stampare i dati. Questa suite permette di
realizzare a design-time il report, di effettuarne l'anteprima sia in fase di design che di run-
time, effettuare calcoli l''interno del report stesso, inserire grafici recuperare
automaticamente i dati dai campi dei records, la realizzazione di report a più livelli o
collegati, effettuare un export del risultato dell'elaborazione in formato testuale, CSV o
HTML.

Su internet ed in particolare sui news group che trattano di Delphi molti non trovano bene
con questi componenti, che in effetti, almeno nelle versioni precedenti a quella presente
con Delphi 5, avevano qualche difetto. Esistono altre soluzioni per la creazione di reports,
sia commerciali che freeware, più o meno complesse e complete. Una delle suite che
ritengo meriti di essere mensionata è FuzzyReport (precedentemente FastReport) creata
da Fabio Dell'Aria che mette a disposizione un editor di reports a run-time permettedo
quindi all'utente finale di personalizzare i reports dell'applicazione. Tutto questo free, e
anche con prestazioni migliori rispetto a quelle di QuickReport. Per ulteriori informazioni
potete visitare il sito http://www.fuzzyreport.shorturl.com/. Comunque in questo corso
tratteremo in maniera molto breve gli strumenti standard forniti con Delphi.

Il componente fondamentale di QuickReport è il componente QuickRep, il cuore del


sistema di generazione dei reports. Esso si presenta come un foglio bianco da posizionare
in una form nel quale andranno posizionati gli altri componenti di QuickReport che
andranno a definire l'aspetto grafico e funzionale del report stesso. QuickReport è uno di
quegli strumenti per la costruzione di reports definiti a "bande". Infatti per definire la
struttura del report si utilizzano delle bande, ogniuna con uno specifico compito. Eistono
bande titolo, itestazione, piè di pagina, header, dettaglio, sottodettaglio e così via.
All'interno di cisacuna banda andranno inseriti altri componenti visuali, che serviranno a
rappresentare i dati del report: possiamo avere delle etichette sia statiche che collegate a
campi di database, come il componente Label standard, delle immagini, sia statiche che
ricavate da campi blob di database, campi per inserire formule, componenti per le forme,
per grafici statistici o per ricavare informazioni strettamente riguardanti il report come il
numero di pagine totali o il numero di pagina corrente, la data di stampa etc.

La costruzione di un report può avvenire in due modi, attraverso i wizards attraverso il


menu File->New oppure "a manina" partendo da una normalissima form. Per prima cosa
si posiziona un componente QuickRep nella form che assumerà l'aspetto di un foglio
bianco con una griglia quadrettata e l'indicazione dell'area stampabile nella stampante
predefinita. A questo punto è già possibile definire alcune delle caratteristiche del report;
attraverso la proprietà Bands del componente QuickRep, è possibile definire quali bande
sono necessarie alla realizzazione del report: Title, PageHeader, PageFooter, etc. La
proprietà Dataset serve a legare il report ad un dataset per il recupero dei dati. La
proprietà Description serve a definire una descrizione per il report; questa proprietà
potrebbe tornare utile quando si da la possibilità all'utente di scegliere un report, poiché
attraverso questa proprietà si può fornire una descrizione del report. Attraverso la
proprietà Frame è possibile specificare se il report verrà riquadrato segnando l'area
stampabile dalla stampante. Accedendo alla proprietà Page è possibile definire le
proprietà della pagina di stampa, larghezza, altezza, margini e se si vuole, la
visualizzazione dei righelli che ovviamente saranno visibili solamente in fase di disegno
del report. PrinterSettings permette di impostare le opzioni per la stampante, il numero di
copie, il binario della carta, etc. PrinterIfEmpty indica a QuickReport se stampare o no il
report se il dataset a cui è collegato è vuoto. ReportTitle serve ad impostare il titolo del
report e può essere utilizzato nel report stesso attraverso il componente QRSysData e la
stessa proprietà viene utilizzata nell'elenco dei job di stampa nel "Print Manager" di
windows. Vi sono ancora altre proprietà interessanti in questo componente come

http://programmazione.html.it/delphi/delphi_38.htm (1 di 2) [21/05/2002 11.27.46]


DELPHI: I Reports

ShowProgress che serve a visualizzare o no l'avanzamento dello stato di costruzione del


report, Unit che indica l'unità utilizzata nella rappresentazione della griglia e dei righelli ed
infine lo Zoom, che permette di regolare la visualizzazione del report in fase di disegno.
Completata la costruzione del report, per permetterne la visualizzazione è necessario
eseguire all'interno del codice del programma il metodo Preview del componente
QuickRep, per visualizzare l'anteprima del report, o il metodo Print per effettuarne la
stampa. QuickReport permette di personalizzare l'aspetto della form di anteprima,
mettendo a disposizione il componente QRPreview. Per maggiori informazioni a riguardo
consultare la guida in linea. Un'altra possibilità messa a disposizione da QuickReport è
quella di esportare il risultato dell'eleborazione del report in formato testuale formattato o
in HTML attraverso i componenti QRTextFilter, QRCVSFilter, QRHTMLFilter.
L'esportazione è molto semplice: basta collocare un componente di quelli
precedentemente mensionati in una form ed automaticamente, attraverso l'opzione salva
nella finestra di anteprima del report, verrà visualizzata l'opzione di esportazione scelta. Si
può efettuare la stessa operazione da codice attraverso il metodo ExportToFilter del
componente QuickRep.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_38.htm (2 di 2) [21/05/2002 11.27.46]


DELPHI: Modifica e creazione di componenti

LEZIONE 36: Modifica e creazione di componenti

Fino ad ora abbiamo visto più o meno come utilizzare i molti componenti che sono
disponibili in Delphi. Ma cosa fare tra tutti questi componenti non troviamo quello che
fa al caso nostro? Semplice, possiamo crearcelo da soli oppure modificare le
caratteristiche di uno già esistente che assomiglia molto al componente che stiamo
cercando. Questa non è difficile costruire nuovi componenti o modificare quelli già
esistenti, ma occorre avere una discreta conoscenza dell'Object Pascal poichè la
realizzazione di un componente non prevede l'impiego di tool di sviluppo visuale
(almeno direttamente forniti con Delphi) e quindi è necessario scrivere tutto il codice
a mano. Occorre altresì avere una buona conoscenza dei componenti già esistenti e
delle classi base messe a disposizione da Delphi per evitare di riscrivere del codice o
componenti già esistenti.

In questa sezione del corso vedremo prima di tutto come modificare un componente
già esistente e poi come costruire un semplicissimo componente "ex novo".

Quando si decide di realizzare un componente, è necessario analizzare attentamente


i componenti esistenti e tenere presente che non è possibile nascondere metodi o
proprietà di un componente. Per questo la scelta del componente (classe) da cui
ereditare le caratteristiche necessarie ha una grande importanza. Delphi come al
solito, ci viene incontro fornendoci delle classi base dei quasi tutti i componenti
standard della VCL (TEdit, TComboBox, ...). Queste classi hanno nomi del tipo
TCustomEdit, TCustomComboBox, etc. Esse implementano tutte le funzionalità e
proprietà dei componenti veri e propri ma non le rendono visibili all'esterno. Le classi
che implementano il componente vero e proprio, non fanno altro che ereditare dalle
classi base e rendere visibili all'esterno tutti i metodi e proprietà della relativa classe
base. Ciò permette di rendere visibili solo alcuni dei metodi e proprietà che ci
interessano.

Per creare un nuovo componente, possiamo seguire due strade: attraverso il menu
Component->New Component, oppure scrivendo da soli lo scheletro di base per il
nostro oggetto. Conviene comunque seguire la prima strada che risparmia, se non
altro, il tempo per la scrittura del codice. Cominciamo.

Apriamo la finestra di dialogo New Component come sopra descritto e digitiamo


quanto riportato nell'immagine seguente

Fig. 1 - Dialogo per creazione nuovo componente

Ancestor Type: indica la classe di base da cui far discendere il nostro nuovo
componente.

Class Name: è il nome che vogliamo dare alla nostra nuova classe; viene
automaticamente compilato quando si sceglie il tipo di classe da cui ereditare ma può
essere modificato a piacere. Il nome scelto per questo esempio è TMyEdit, ma

http://programmazione.html.it/delphi/delphi_39.htm (1 di 5) [21/05/2002 11.27.59]


DELPHI: Modifica e creazione di componenti

nessuno vieta di scegliere un altro nome. Occhio a non usare nomi di classi già
esistenti!

Palette Page: è la pagina della Componente Palette di Delphi dove verrà posizionato
il componente quando andremo a registrarlo nell'IDE di Delphi. In questo esempio ho
scelto di posizionare il nuovo componente nella pagina Samples della component
Palette, ma voi potrete scegliere la pagina che preferite.

Unit File Name: è il nome, completo di percorso, che vogliamo dare alla unit che
conterrà il codice del nostro nuovo componente. In questa casella potete inserire il
percorso che preferite che sicuramente differirà da quello da me scelto.

Search Path: è l'elenco di percorsi all'interno dei quali Delphi ricercherà le unit
necessarie per il componente.

Completata questa fase, premiamo il tasto OK e vedremo che Delphi creerà una unit
con il nome da noi fornito contenente l'impalcatura della nostra nuova classe.

Ecco il codice che comparirà nell'Code Editor al termine dell'operazione

unit MyEdit;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;

type
TMyEdit = class(TEdit)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents('Samples', [TMyEdit]);
end;

{ TMyEdit }

end.

Come potete vedere, sono rintracciabili tutte le informazioni da noi fornite all'interno
della finestra di dialogo, ma non abbiamo dovuto scrivere una riga di codice!

Ora passiamo a modificare le impostazioni di default della classe TEdit. Vogliamo


impostare come colore di sfondo di default del componente TEdit il colore giallino
utilizzato nei ToolTips e impostare la sua lunghezza di default a 200 pixels. Bene,
procediamo con il fare l'override della procedura di creazione del componente.
Aggiungiamo nella sezione Public della dichiarazione della classe la ridichiarazione
del costruttore Create come segue

Constructor Create(AOwner : TComponent); override;

http://programmazione.html.it/delphi/delphi_39.htm (2 di 5) [21/05/2002 11.27.59]


DELPHI: Modifica e creazione di componenti

una volta scritto il codice sopra riportato, possiamo utilizzare la combinazione di tasti
Ctrl+Shift+C per dire a Delphi di completare il codice automaticamente. Questa
operazione porterà alla creazione del codice di implementazione del costruttore
appena definito nella forma seguente

constructor TMyEdit.Create(AOwner: TComponent);


begin
inherited;
end;

a questo punto possiamo aggiungere il codice necessario per personalizzare i nostri


valori di default; modifichiamo l'implementazione del costruttore come segue

constructor TMyEdit.Create(AOwner: TComponent);


begin
inherited;

Width := 200;

Color := clInfoBk;
end;

Con queste righe di codice impostiamo a 200 pixels la lunghezza del controllo ed il
colore di sfondo al colore di sfondo correntemente attivo per i ToolTips.

La procedura Register serve a Delphi per registrare il componente in una pagina


della Components Palette; questa procedura chiama infattai la procedura
RegistreComponents passandogli come parametro il nome della pagina della
Components Palette: se la pagina esiste vi registra il componente altrimenti la crea e
quindi vi registra il componente.

Questo è un semplicissimo esempio di come è possibile personalizzare alcune


caratteristiche di un componente già esistente. Si possono anche aggiungere
funzionalità ad un componente già esistente; per esempio, potremmo voler
aggiungere al nostro componente MyEdit una proprietà che contenga il testo digitato
nel controllo con l'ordine dei caratteri invertito. Vediamo come implementare questa
caratteristica.

Aggiungiamo nella parte Private della nostra classe TMyEdit la funzione Reverse
dichiarandola come segue

Function Reverse : String;

ed implementandola come segue

function TMyEdit.Reverse: String;


Var I, L : Integer;
begin
L := Length(Text);

SetLength(Result, L);

For I := 1 to Length(Text) do
Result[(L+1) - I] := Text[I];
end;

http://programmazione.html.it/delphi/delphi_39.htm (3 di 5) [21/05/2002 11.27.59]


DELPHI: Modifica e creazione di componenti

Questa funzione legge la stringa contenuta nella proprietà Text del controllo
(ereditata dalla classe TEdit) e dispone i caratteri che la compongono in ordine
inverso. La proprietà Text contiene il testo digitato dall'utente. Ora occorre
implementare una proprietà che restituisca, in lettura, il testo invertito. Aggiungiamo
nella parte di dichiarazione Public della nostra classe TMyEdit la proprietà
ReversedTxt nel seguente modo:

Property ReversedTxt : String read GetReversedTxt;

Premendo Ctrl+C dopo aver digitato questa riga, otterremo il completamento


automatico della nostra proprietà con la creazione della funzione GetReversedTxt.
Ecco come apparirà la dichiarazione della nostra classe dopo queste modifiche

TMyEdit = class(TEdit)
private
{ Private declarations }
Function Reverse : String;
function GetReversedTxt: String;
protected
{ Protected declarations }
public
{ Public declarations }
Constructor Create(AOwner : TComponent); override;
Property ReversedTxt : String read GetReversedTxt;
published
{ Published declarations }
end;

Modifichiamo l'implementazione della funzione GetReversedTxt come segue

function TMyEdit.GetReversedTxt: String;


begin
Result := Reverse;
end;

Si può anche eliminare la funzione Reverse implementando il codice direttamente


nella funzione GetReversedTxt ma per questioni di chiarezza e modularità del
software, preferisco utilizzare questa struttura.

Per provare il nostro componente possiamo registrarlo in Delphi scegliendo dal menu
Component la voce "Install Component", il componente verrà installato nella libreria
dei componenti e la sua icona posta nella pagina della Components Palette da noi
scelta, oppure aggiungere la unit MyEdit nella clausola uses di una nuova form e
creare il componente a runtime come segue

procedure TForm1.FormCreate(Sender: TObject);


begin
MyEdit := TMyEdit.Create(Self);
MyEdit.Parent := Self; //
MyEdit.Top := 16; //Posizionamento del componente nella form
MyEdit.Left := 10; // " " " " "
end;

e nel evento OnDestroy della form aggiungiamo il seguente codice

http://programmazione.html.it/delphi/delphi_39.htm (4 di 5) [21/05/2002 11.27.59]


DELPHI: Modifica e creazione di componenti

procedure TForm1.FormDestroy(Sender: TObject);


begin
MyEdit.Free;
end;

per eliminare il componente da noi creato quando eliminiamo la form. Non


dimenticatevi di inserire una variabile MyEdit di tipo TMyEdit nella sezione Private
della classe TForm1

...

private
{ Private declarations }
MyEdit : TMyEdit;
public
{ Public declarations }
end;

Potete scaricare il codice completo dell'esempio riportato per vedere come funziona
ed avere il codice a disposizione.

Per quanto riguarda la creazione di componenti, è molto difficile che si creino


componenti partendo completamente da zero, salvo in casi molto particolari.
Generalmente si eredita dalla classe TComponent per i componenti non visibili e da
TControl o da TWinControl per i componenti visibili e da TGraphicControl per i
componenti grafici. Possiamo dire che queste sono le classi utili più "primitive" da cui
iniziare a creare nuovi componenti. La scrittura di codice per componenti richiede
una discreta conoscenza della programmazione ad oggetti in Object Pascal, delle
classi da cui ereditare e delle basi del sistema operativo, in questo caso Windows.
Pertanto si rimanda alla lettura dei manuali e alla lettura di testi specifici, come la
serie Mastering Delphi di Marco Cantù, in particolare "Delphi Developer's Handbook"
e "Laboratorio di Delphi" di Nando Dessena e Barbini, nonché frequentare i news
group che trattano di Delphi frequentati anche dai personaggi citati.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_39.htm (5 di 5) [21/05/2002 11.27.59]


DELPHI: DLL: cosa sono, a cosa servono, come crearle e/o utilizzarle

LEZIONE 36: DLL: cosa sono, a cosa servono, come crearle e/o
utilizzarle

DLL è l'abbrevizione di Dynamic Link Library (Libreria a Collegamento Dinamico).


Le DLL sono delle raccolte di routines che possono essere chiamate da più
programmi e solitamente contengono codice di utilità generale che viene messo a
disposizione di più applicazioni. Ciò permette di non inserire codice duplicato in
applicazioni differenti. Si possono assimilare le DLL alle unit di Delphi solamente che
le prime contengono codice eseguibile, collegabile in fase di esecuzione. In Delphi
esistono diversi modi di richiamare funzioni contenute in una DLL: è possibile
utilizzare la parola chiave External dopo la dichiarazione del nome della funzione da
importare, seguita dal nome del file della DLL che la contiene. Non è necessario che
la DLL sia presente in fase di compilazione, ma se questa non sarà presente durante
l'esecuzione dell'applicazione si otterrà un errore. Un esempio di procedura importata
da una DLL è il seguente

Procedure DLLProcedure1; external 'MYDLL.DLL';

Un'altro modo di richiamare una funzione o procedura contenuta in una DLL è


caricando in fase di esecuzione la libreria con l'API di windows LoadLibrary, di
recuperare l'indirizzo della procedura o funzione con l'API GetProcAddress, chiamare
la procedura attraverso l'indirizzo ottenuto ed infine, quando non occorre più,
scaricare la libreria con l'API FreeLibrary. In questo secondo caso è anche
necessario dichiarare una variabile procedura del tipo corrispondente alla procedura
o funzione da richiamare. Se nell'esempio precedente la nostra procedura avesse
avuto una dichiarazione del tipo

Procedure DLLProcedure1(Param1, Param2 : Integer);

la variabile procedura avrebbe dovuto essere del tipo

Type TDLLProcedure1 = procedure(Param1, Param2 : Integer);

Var DLLProcedure1 : TDLLProcedure1;

A questa variabile deve essere assegnato il valore restituito dalla funzione


GetProcAddress nella seguente maniera

...

@DLLProcedure1 := GetProcAddress(Handle, 'DLLProcedure1');

...

dove Handle è l'handle della libreria restituito dalla chiamata a LoadLibrary.

Si consiglia di fare riferimento all'SDK di Windows per le API sopracitate.

http://programmazione.html.it/delphi/delphi_40.htm (1 di 3) [21/05/2002 11.28.08]


DELPHI: DLL: cosa sono, a cosa servono, come crearle e/o utilizzarle

La scrittura di una DLL in Delphi non differisce molto dalla scrittura di una normale
applicazione. La differenza sostanziale sta nella parola chiave Library che sostituisce
Program nell'intestazione della unit. In una DLL le procedure e funzioni possono
essere contenute nel file di progetto oppure contenute in unit esterne e la libreria si
riduce ad una clausola uses contente le unit con le funzioni e procedure seguita da
una sezione Exports con l'elenco delle funzioni o procedure da esportare. Il blocco
principale Begin..End di una libreria rappresenta il blocco di codice che viene
eseguito come codice di inizializzazione della libreria e viene eseguito ogni qual volta
la libreria viene caricata in memoria. Se si vogliono rendere disponibili le routines
contenute in una DLL scritta in Delphi anche ad altri linguaggi di programmazione,
occorre specificare la direttiva stdcall poichè non tutti i linguaggi supportano la
modalità di chiamata ottimizzata "register" utilizzata dall'Object Pascal.

La sezione Exports di una DLL ha la seguente struttura

exports entry1, entry2, ..., entryn;

Ogni entry è costituita dal nome della routine da esportare (funzione o procedura)
seguita da due specificatori opzionali: index e name. Il primo serve a definire un
indice numerico per richiamare la routine esportata e può assumere valori tra 1 e
2.147.483.647. Se per una routine non viene specificato un valore per index gli viene
assegnato automaticamente un numero tratto dalla tabella di esportazione della DLL.
Lo specificatore name è seguito da una costante stringa e specifica il nome con cui
verrà esportata la routine. Se non viene assegnato ad una routine uno specificatore
name, questa verrà esportata con il nome con cui è stata dichiarata nella DLL,
rispettando anche le maiuscole/minuscole. Tornando alla nostra procedura di
esempio, potremmo avere una sezione exports di questo genere

exports DLLProcedure1 index 1 name 'DLLProcedure1';

che è equivalente a

exports DLLProcedure1;

se vogliamo esportare la routine con un altro nome

exports DLLProcedure1 index 1 name 'DLLProcedure';

Delphi ci mette a disposizione alcune variabili globali per semplificare la gestione


delle DLL. Abbiamo visto in precedenza che il codice contenuto nel blocco principale
della libreria viene utilizzato come codice di inizializzazione della libreria stessa, ma
esiste un codice di pulizia da eseguire al rilascio della libreria? Esiste. Si può fornire
l'indirizzo di una procedura che si occuperà di eseguire il codice di pulizia tramite la
variabile ExitProc. Questa viene solitamente impostata in fase di inizializzazione della
DLL. Quando la libreria viene rilasciata, viene eseguito automaticamente il codice
contenuto nella procedura all'indirizzo contenuto in ExitProc. Occorre però salvare il
vecchio puntatore alla procedura di pulizia prima di impostare una nuova procedura e
ripristinarlo prima che la nuova procedura termini l'esecuzione.

Nella unit System è presente una variabile che permette di determinare se il codice in
esecuzione appartiene ad una DLL oppure ad un'applicazione: IsLibrary. Questa
variabile assume valore false quando si è in presenza di un'applicazione e true
altrimenti.

Nella scrittura delle DLL bisogna fare attenzione ai parametri passati e restituiti

http://programmazione.html.it/delphi/delphi_40.htm (2 di 3) [21/05/2002 11.28.08]


DELPHI: DLL: cosa sono, a cosa servono, come crearle e/o utilizzarle

alle/dalle routines. Se si utilizzano stringhe di tipo long o array dinamici, record od


oggetti, occorre che tutti i moduli che fanno uso delle routines e le DLLs che
esportano le routines in oggetto utilizzino la unit ShareMem, dichiarandola come
prima unit nella clausola uses. Questa è una interfaccia per il gestore di memoria
BORLANDMM.DLL che permette ai moduli di condividere la memoria allocata in
modo dinamico.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_40.htm (3 di 3) [21/05/2002 11.28.08]


DELPHI: I Packages: cosa sono, a cosa servono, come crearli e/o utilizzarli

LEZIONE 36: I Packages: cosa sono, a cosa servono, come


crearli e/o utilizzarli

I Packages sono particolari tipi di librerie a caricamento dinamico che possono


essere utilizzate solamente da applicazioni Delphi e dall'IDE di Delphi stesso.

Questo nuovo tipo di libreria è stato introdotto con Delphi 3 e lo scopo principale è
quello di creare librerie di componenti riutilizzabili da più applicazioni. Essi possono
essere utilizzati per linkare il codice in essi contenuto sia a design-time che a run-
time. I Packages possono essere di tre differenti tipi: Design-Time, Run-Time,
Design-Time and Run-Time contemporaneamente. I primi sono disponibili solamente
in fase di progettazione ed installabili nell'IDE di Delphi, i secondi vengono distribuiti
con le applicazioni e non possono essere installati nell'IDE, gli ultimi sono un misto
dei primi due. L'estensione per il codice sorgente dei packages è .DPK, mentre per la
versione compilata sarà .BPL (Borland Package Library).

Esiste anche la possibilità di creare librerie di packages utili per passare o


condividere componenti con altri sviluppatori in uno stesso team. In questo caso
avremo un file di libreria con estensione .DPL. Questo tipo di libreria può essere
creato attraverso l'apposito editor; il Package collection Editor. Per creare un nuovo
package si può come sempre ricorrere al wizard "New" dal menu File dell'IDE. Come
per le librerie DLL, anche per i packages esiste una parola chiave che si trova
all'inizio del file sorgente principale e che ne identifica il tipo: Package.

Nel file sorgente principale di un package sono presenti due clausole aggiuntive, non
presenti nei files sorgente delle DLL; Requires e Contains. Queste due clausole
identificano rispettivamente altri packages esterni che sono richiesti dal package
corrente per il suo corretto funzionamento e le unit contenute all'interno del package
stesso.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_41.htm [21/05/2002 11.28.13]


DELPHI: Delphi & Web

LEZIONE 36: Delphi & Web

Il corso sta volgendo al termine. Prima di lasciarci, vorrei, a completamento, mostrare alcune
funzionalità di Delphi che permettono la realizzazione di applicazioni indirizzate al web, alla
produzione di pagine HTML e che permettono addirittura di creare componenti ASP da
inserire nei siti web oppure per creare siti completamente basati su Delphi. Con Delphi è
possibile realizzare applicazioni CGI oppure ISAPI. Il tutto è ovviamente semplicissimo come
del resto qualsiasi operazione in Delphi!?! Esistono anche qui dei template da utilizzare per la
creazione di CGI, ISAPI e componenti ASP; basta effettuare la scelta giusta nel wizard "New"
dal menu "File". Esistono anche dei componenti chiamati "HTML Producer Components" che
permettono di creare pagine HTML. Questi componenti utilizzano come schema per le pagine
da costruire dei template HTML e ricavano i dati per completarli o direttamente dal codice
oppure da tabelle di database o query. Questi componenti si trovano nella pagina Internet
della Component Palette e sono: PageProducer, DataSetPageProducer,
DataSetTableProducer, QueryTableProducer.

I Componenti per la produzione di pagine HTML

Nella pagina Internet della Component Palette oltre ad altri componenti che riguardano
internet (WebDispatcher, WebBrowser) compaiono i componenti per la produzione di pagine
Web: PageProducer, DataSetPageProducer, DataSetTableProducer, QueryTableProducer. La
tecnica utilizzata da questi componenti per creare pagine HTML è quella di utilizzare un
normale file HTML, creato con un qualsiasi editor HTML, contenente alcuni tag speciali che
hanno lo scopo di fare da segnaposto per l'output dei dati che l'applicazione dovrà inserire nel
documento. Il formato di questi tags è <#NomeTag>. Il componente pù semplice, è il
PageProducer. Questo analizza il documento HTML che gli viene fornito come template e
genera un evento OnTag per ogni tag speciale incontrato in esso. Nell'event handler del
evento OnTag deve essere inserito il codice che reagirà al particolare tag. Per esempio se nel
nostro documento HTML abbiamo inserito il tag speciale <#Data>, quando il componente
analizzerà il documento genererà l'evento OnTag. Il tipo dell'event handler per questo evento
è il seguente

Procedure (Sender : TObject; Tag : TTag; const


TagString : String; TagParams : TStrings; var
ReplaceText : String);

Supponiamo di aver creato il seguente event handler

procedure TForm1.PageProducer1HTMLTag(Sender: TObject; Tag: TTag;


const TagString: String; TagParams: TStrings;
var ReplaceText: String);
begin
If TagString = 'Data' then
ReplaceText := DateToStr(Now);
end;

Il codice dell'event handler sostituirà il il tag speciale con la data corrente. Supponendo che il

http://programmazione.html.it/delphi/delphi_42.htm (1 di 6) [21/05/2002 11.28.19]


DELPHI: Delphi & Web

nostro documento avesse avuto la seguente struttura

<HTML>
<HEAD>
<Title>Prova componente Page Producer</Title>
</HEAD>
<BODY>
<H2>Data corrente</H2>
La data corrente è <#Data>
</BODY>
</HTML>

il risultato sarebbe

Data Corrente

La data corrente è 17/08/2001

Il risultato precedente è prelevabile dalla proprietà Content del componente (di tipo TStrings).
Esso può essere copiato in un componente memo per la visualizzazione oppure salvato su
disco con la proprietà SaveToFile della classe TStrings. L'esempio riportato è semplicissimo,
ma è possibile realizzare pagine web complesse attingendo da databases o altre fonti.

DataSetTableProducer, QueryTableProducer

A proposito di databases, Delphi dispone già di un componente specializzato nella


composizione di pagine HTML attingendo da fonti dati quali databases. Questo componente è
DataSetPageProducer. L'utilizzo di questo componente è semplicissimo e molto simile al
PageProducer che abbiamo visto sopra. In questro caso abbiamo una proprietà in più che è
quella che definisce il collegamento con il dataset contenente i dati. Basta fornire il solito
documento HTML come template e disporre dove necessario i tags speciali corrispondenti ai
nomi dei campi del dataset collegato al componente stesso. In questo modo il componente
recupera automaticamente il contenuto dei campi e lo inserisce nel documento. Noi non
dobbiamo fare nulla se non gestire altri tags speciali, come abbiamo fatto nell'esempio
precedente, per inserire valori non provenienti dal dataset attraverso il gestore dell'evento
OnTag.

Un altro componente per la produzione di codice HTML è il DatasetTableProducer che ha lo


scopo di creare tabelle in standard HTML prelevando il contenuto da un dataset. Dispone
quindi della proprietà Dataset per il collegamento alla sorgente di dati. Questo componente è
dotato anche di un "Property Editor" per la proprietà Columns. Attraverso di esso è possibile
impostare a design-time le caratteristiche delle colonne ed i campi del dataset ad esse
associati, producendo anche una anteprima del risultato finale. Il DatasetTableProducer
prevede anche la possibilità di definire un header ed un footer (una intestazione tabella ed un
pie' tabella). Tramite le proprietà Hader ed Footer del componente è possibile inserire, ad
esempio, il titolo del documento. Il contenuto della proprietà Header e Footer deve essere in
formato HTML. Percui, se volessimo definire il titolo del documento, dovremmo scrivere nella
proprietà Header il seguente testo:

<H1>Titolo</H1>

http://programmazione.html.it/delphi/delphi_42.htm (2 di 6) [21/05/2002 11.28.19]


DELPHI: Delphi & Web

Lo stesso dicasi per la proprietà Footer. Nel componente sono previste anche proprietà per
impostare le caratteristiche della tabella quali il colore di sfondo, il cellpadding (la distanza del
contenuto della cella dal bordo cella), il cellspacing (lo spazio tra una cella e l'altra), la
presenza o no del bordo e la dimensione, e le caratteristiche delle righe che comporranno la
tabella. Tutte questa impostazioni sono raccolte in due proprietà, RowsAttributes e
TableAttributes. Il componente DatasetTableProducer espone anche degli eventi che
permettono un maggiore adattamento alle proprie necessità. In particolare l'evento
OnFormatCell che viene attivato al momento della formattazione della singola cella. Definire
un gestore per questo evento permette di modificare per ogni singola cella i valori di colore di
sfondo, l'allineamento verticale od orizzontale, e perfino il contenuto della cella stessa.

In maniera del tutto simile funziona il componente QueryTableProducer tranne per il fatto che
questo viene collegato ad un componente Query e che il suo scopo è quello di generare
pagine HTML basate su query parametrizzate i cui parametri vengono ottenuti da una richiesta
HTML. La modalità di richiesta può essere GET o POST.

Delphi e HTML dinamico

Quanto visto sopra è applicabile ad applicazioni che vogliono fornire una versione dei dati in
formato HTML (formato multipiattaforma) o per applicazioni che realizzano server web o
servizi che rispondono come server web. Ci sono altri sistemi che possono essere utilizzati per
fornire informazioni in formato HTML. Chi realizza siti web conosce i CGI (Common Gateway
Interface). Questi vengono utilizzati per fornire pagine HTML dinamiche ovvero il cui contenuto
vari in corrispondenza del variare dei potenziali dati da visualizzare. Esempi di questo tipo di
documenti potrebbero essere pagine che forniscono notizie dell'ultima ora, estrazione di dati
data database che vengono aggiornati frequentemente (annunci di compra/vendita). Quello
che il browser riceve è sempre un documento HTML che altro non è che il prodotto di una
elaborazione effettuata dal server web al momento della richiesta compilando la pagina con i
dati richiesti e disponibili in quel momento. Un CGI altro non è che una applicazione eseguibile
che risponde alle richieste HTML effettuate dai browser; esso analizza le richieste in arrivo e
restituisce dei documenti HTML compilati in base alle richieste ricevute. Esistono due tipi di
CGI; i CGI e i WINCGI. I primi ricevono i parametri di richiesta direttamente dalla riga di
comando mentre i secondi li estraggono da un file .INI che viene passato loro come parametro
nella riga di comando e utilizzano come input ed output speciali files. Questo secondo tipo di
CGI è stato realizzato per venire incontro agli sviluppatori Visual Basic permettendo di
scavalcare alcune limitazioni del linguaggio.

Neanche a farlo a posta Delphi ci permette di realizzare questo tipo di applicazioni partendo
dalla considerazione che essi non sono altro che applicazioni console che utilizzano lo
standard input e lo standard output per acquisire le richieste e fornire i risultati
dell'elaborazione. Tutto ciò si traduce in una serie di Writeln che restituiscono codice HTML
come ad esempio la seguente riga

Writeln('<H1>Titolo</H1>');

Se eseguiamo il CGI da una finestra di terminale vedremo esattamente ciò che è riportato
come parametro nei comandi Writeln.

Oltre ai CGI in Delphi è possibile creare delle DLL ISAPI (Intrenet Server API) o NSAPI
(Netscape Server API). Il primo tipo è stato introdotto da Microsoft, mentre il secondo dall
Netscape. Questa tecnologia può essere vista come un miglioramento del CGI. Essa si
differenzia dal CGI per le modalità di caricamento nella memoria del server e per le modalità di
risposta. Mentre il CGI viene caricato ad ogni richiesta, per questo tipo di DLL le cose
cambiano. La DLL viene carica al momento della richiesta, se essa non è già stata caricata in
precedenza, nello stesso spazio di indirizzamento del server web ed è capace di rispondere a
più richieste facendo uso di threads. Ciò permette di non dover caricare tanti eseguibili in
memoria quante sono le richieste fatte al server web come invece accade per i CGI. Ogni
thread attivato dal server web gestisce una richiesta e la relativa risposta. C'è differenza anche
nella velocità di risposta rispetto al CGI poichè nel caso delle DLL ISAPI/NSAPI lo scambio dei
dati avviene tutto in memoria. Non mi dilungherò oltre su questo argomento. Diamo uno
sguardo a cosa mette a disposizione Delphi per aiutarci nella realizzazione di questo tipo di
software. Delphi 5 include, nelle versioni Professional e Enterprise, la tecnologia WebBroker
che integra nella VCL delle classi che semplificano la realizzazione di applicazioni web lato
server.

http://programmazione.html.it/delphi/delphi_42.htm (3 di 6) [21/05/2002 11.28.19]


DELPHI: Delphi & Web

Per utilizzare il framework WebBroker, è necessario scegliere dal menu file la voce New e
nella prima pagina della finestra che compare scegliere "Web Server Application". Ciò
provocherà la visualizzazione di una finestra di scelta del tipo di applicazione che si ha
intenzione di realizzare. I tipi di applicazioni tra cui si può scegliere sono tre: DLL
ISAPI/NSAPI, eseguibile CGI , eseguibile WinCGI. Nel caso si scelga si realizzare DLL
ISAPI/NSAPI, Delphi produrrà un file progetto di libreria del tutto simile ad una normale libreria
ma con delle particolarità. Per prima cosa vediamo comparire nella clausola uses del file
progetto le unit WebBroker, ISAPIApp ed una nuova unit che contiene un oggetto WebModule.
Nello stesso tempo dalla libreria vengono esportate delle procedure standard per il
caricamento e scaricamento della libreria nella/dalla memoria del server web. Da notare che in
questo tipo di DLL, l'oggetto application che viene creato non è di tipo standard, non
corrisponde infatti agli altri oggetti application creati nelle normali applicazioni. Nelle
applicazioni ISAPI/NSAPI, l'oggetto application deriva da una nuova classe che può essere
TISAPIApplication o TCGIApplication. Queste ultime derivano entrambe da un'altra classe che
è TWebApplication. Sono delle classi specializzate per la realizzazione di applicazioni per il
web. Il tipo dell'oggetto applicazione può essere TISAPIApplication o TCGIApplication in base
alla scelta che abbiamo effettuato nel wizard del Object Repository. La caratteristica più
importante è che l'intera applicazione ruota attorno alla nuova unit contenente l'oggetto
WebModule. Un WebModule è molto simile ad un DataModule standard, con ovviamente delle
caratterizzazioni specifiche per le applicazioni web. Il WebModule ha lo scopo di raccogliere
tutti gli oggetti non visuali dell'applicazione e di permettere ad essa di rispondere ai messaggi
di richiesta HTTP passando gli oggetti Request e Response alle azioni appropriate. Tra le
proprietà dell'oggetto WebModule compare la proprietà Actions. Essa contiene l'elenco delle
azioni che devono essere eseguite dall'applicazione in risposta ad un determinato switch che
viene passato alla DLL. Lo switch che identifica l'azione è contenuto nella proprietà PathInfo
dell'oggetto WebAction stesso. Un'altra proprietà interessante dell'oggetto WebAction è
Producer che permette di collegare all'azione direttamente un oggetto di tipo TPageProducer,
TDataSetPageProducer, TDatasetTableProducer, TQueryTableProducer (che abbiamo già
visto) che verrà richiamato all'attivarsi dell'azione stessa.

Un'altra tecnologia che permette di realizzare pagine HTML dinamiche, è la tecnologia ASP
(Active Server Pages) introdotta da Microsoft. Essa si basa su codice script, inserito all'interno
di documenti HTML, che viene eseguito direttamente nel server e che produce pagine HTML
standard in risposta alle richieste dei clients. Questa tecnologia prevede anche un estensione
delle proprie funzioni attraverso la realizzazione di oggetti COM che possono essere richiamati
direttamente dal codice script. Anche in questo caso Delphi ci viene incontro fornendoci la
struttura base per la realizzazione di questo tipo di componenti. Nel wizard che porta alla
realizzazione del codice base per la realizzazione di oggetti ASP possiamo impostare tutti i
parametri di funzionamento dell'oggetto COM, come il nome della classe, il tipo di interfaccia
ed il tipo di threading dell'oggetto, il tipo di oggetto. Per creare la struttura base di un oggetto
ASP, possiamo scegliere dal menu File -> New, nella pagina ActiveX dell'Object Repository la
voce Active Server Object. ATTENZIONE! Per poter creare un oggetto ActiveX bisogna
disporre di un progetto attivo, nel nostro caso potremmo creare un progetto di tipo "ActiveX
Library", sempre dall'Object Repository, e quindi seguire la procedura indicata
precedentemente. Il wizard ActiveX Server Object presenterà una finestra per l'inserimento
delle caratteristiche principali dell'oggetto ActiveX come riportato nell'immagine seguente

Fig. 1

CoClass Name indica il nome dell'oggetto ActiveX che si sta creando a cui Delphi anteporrà
un T; per cui se inseriamo in questa casella "ProvaASP" il nome della classe che sarà
"TProvaASP".

http://programmazione.html.it/delphi/delphi_42.htm (4 di 6) [21/05/2002 11.28.19]


DELPHI: Delphi & Web

Instancing definisce la modalità di istanziazione dell'oggetto ovvero Internal, Single Instance,


Multiple Instance. Nel primo caso l'oggetto sarà creato come interno e nessun'altra
applicazione potrà creare un'altra istanza dello stesso. Single Instace permette di creare una
singola interfaccia per l'oggetto COM per ciascun eseguibile. Multiple Instance permette
invece a più applicazioni di collegarsi all'oggetto.

Threading Model specifica invece la modalità di gestione dei thread dell'oggetto COM.
L'impostazione Single indica che non vi è nessun supporto per i thread e che tutte le richieste
dei clients vengono gestite in maniera seriale. Apartment permette di accedere alle proprietà
ed ai metodi dell'oggetto solamente attraverso il thread in cui è stato creato l'oggetto stesso.
Free le proprietà ed i metodi possono essere chiamati in qualsiasi momento da qualsiasi
thread. La modalità Both indica che l'oggetto in questione può supportare il metodo Apartment
e Free.

Active Server Type definisce la "compatibilità" con il server web specifico: IIS3 o 4 per
l'opzione Page-level event methods e IIS 5 per Object Context. In pratica, nel primo caso
vengono generati all'interno dell'oggetto due eventi OnPageStart, OnPageEnd che vengono
richiamati dal server web rispettivamente all'inizializzazione della pagina e al termine
dell'elaborazione. Nel secondo caso vengono utilizzate le funzionalità di MTS per recuperare i
dati di istanza corretti dell'oggetto.

Nel riquadro Options, se viene spuntata la voce "Generate a template test script for this
object", verrà cerata una pagina asp che istanzierà e utilizzerà l'oggetto creato ed ha
solamente scopo di test.

Una volta creata la struttura base del nostro oggetto ASP, è possibile accedere direttamente
agli oggetti Application, Response, Request, Session di ASP. Non resta che implementare il
codice specifico. Vediamo un semplicissimo esempio.

A questo punto proviamo ad aggiungere un semplice metodo al nostro oggetto. Esso avrà
solamento lo scopo di restituire nel flusso HTML di risposta del codice HTML. Aggiungiamo
alla nostra alla classe appena creata una procedura così definita

procedure ShowHelloASP; safecall;

ed implementata nel seguente modo

procedure TTelpressNewsService.ShowNews;
begin
Response.Write('<h1>Prova oggetto ASP in Delphi</h1><br>');
Response.Write('<B>Hello ASP!!!</B>');
end;

Ora dovremo inserire una chiamata al metodo del nostro oggetto dalla pagina asp di test
(magari creata automaticamente da Delphi) ottenendo quanto segue

<HTML>
<BODY>
<TITLE> Testing Delphi ASP </TITLE>
<CENTER>
<H3> You should see the results of your Delphi
Active Server method below </H3>
</CENTER>
<HR>
<% Set DelphiASPObj = Server.CreateObject("TNSW.TelpressNewsService")
DelphiASPObj.ShowHelloASP
%>
<HR>
</BODY>
</HTML>

http://programmazione.html.it/delphi/delphi_42.htm (5 di 6) [21/05/2002 11.28.19]


DELPHI: Delphi & Web

Questo è solo un esempio, ma in programmazione (e soprattutto in Delphi) i limiti sono quelli


posti dalla nostra immaginazione!!!!!!!!!!!

P.S. Ovviamente per approfondimenti sull'argomento trattato, consultate il manuale del


linguaggio, la guida in linea e se ne avete la possibilità "Mastering Delphi 5" di Marco Cantù.

Lezione successiva
[Sommario]

http://programmazione.html.it/delphi/delphi_42.htm (6 di 6) [21/05/2002 11.28.19]


DELPHI: Risorse utili e bibliografia

LEZIONE 36: Risorse utili e bibliografia

Risorse Utili
Borland - sito ufficiale
Marco Cantù, guru italiano del Delphi - articoli, news groups, link, corsi
Le pagine di Torry - Articoli, tips, componenti
Project Jedi - The Delphi API library
Delphi Power Page - Componenti, Informazioni
Delphi Pages - Componenti, tips, articoli
Delphi Super Page - Componenti per Delphi
DelphiSeek - Motore di ricerca per risorse Delphi
Dr. Bob's Delphi Clinic
FreeVCS - Free Version Control System
GExperts - Utility aggiuntive per l'IDE di Delphi
FuzzyReport - Tool per la creazione di reports FREE!!!

Bibliografia
Guida al linguaggio Object Pascal, manuali Delphi 5 (Borland)
Guida alla programmazione, manuali Delphi 5 (Borland)
Programmare con Delphi 4 (1999 Apogeo, Marco Cantù)
Programmare con Delphi 5 (2000 Apogeo, Marco Cantù)
Delphi Developer's Handbook (1998 Sybex, Marco Cantù, Tim Gooch, John F. Lam)

[Sommario]

http://programmazione.html.it/delphi/delphi_43.htm [21/05/2002 11.28.27]

Potrebbero piacerti anche