Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
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.
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.
I principi di sintassi del codice Delphi: operatori booleani, operatori relazionali, regole di precedenza
degli operatori.
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.
Delphi ed i Databases
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
DLL e Packages
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
Delphi ed il Web
Risorse
LEZIONE 1: La storia
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.
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".
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 "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.
Immagini delle confezioni delle varie releases della versione 5.0 (sito Borland)
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]
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.
Diamo un'occhiata alle estensioni utilizzate dal compilatore Delphi. Questo si aspetta
di trovare il codice sorgente in Pascal in di tre tipi:
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 .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.
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:
Si tratta di una semplice applicazione per console che può essere compilato ed
eseguito dalla riga di comando.
Program Hello_World;
{$APPTYPE CONSOLE}
Begin
Writeln(MessageStr);
End.
DCC32 Hworld.pas
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.
Lezione successiva
[Sommario]
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.
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.
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.
Il Code Editor è un editor ASCII completo, ricco di utili funzionalità, tramite il quale si
agisce sul codice sorgente dell'applicazione.
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.
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.
Lezione successiva
[Sommario]
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.
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.
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
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.
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.
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
begin
end;
type
TForm1 = class(TForm)
Button1: TButton;
private
{ Private declarations }
public
{ Public declarations }
Lezione successiva
[Sommario]
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.
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.
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
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]
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.
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.
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
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.
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.
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]
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.
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
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.
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.
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]
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. 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)
Lezione successiva
[Sommario]
Questo è il codice:
Program Editor;
Uses
Forms,
{$R *.RES}
Begin
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):
{$R *.RES}
...
End.
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:
Interface
Implementation
Initialization
Finalization
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
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.
Program Program1;
oppure
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;
...
Unit Unit1;
Interface
Uses Unit2;
...
Unit Unit2;
Interface
Const Paperino = 1;
...
Program Program2;
Uses Unit1;
...
Unit Unit1;
Interface
Uses Unit2;
...
Unit Unit2;
Interface
Uses Unit1;
Const Paperino = 1;
...
Program Program2;
Uses Unit1;
...
Unit Unit1;
Interface
Uses Unit2;
...
Unit Unit2;
Interface
Const Paperino = 1;
Implementation
Uses Unit1;
...
Se abbiamo due unit che dichiarano una variabile, costante, tipo, routine con lo
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;
...
...
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]
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;
Pippo := 1; Pluto : = 2;
#$&'()*+,-./:;<>=@[]^{}
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 (_).
Numerali
In Object Pascal i numeri reali ed interi possono essere rappresentati sia in forma
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
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 }
...
{$APPTYPE CONSOLE}
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.
Operatori aritmetici
Segue un elenco degli operatori aritmetici definiti in Object Pascal:
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:
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
perché equivale a
Lezione successiva
[Sommario]
Poco c'è da dire su questo tipo di operatori, essi seguono infatti le regole della logica
booleana.
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:
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.
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
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.
(2 + 3) * 4
2 + (3 * 4)
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]
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.
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 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.
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.
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.
var I : Byte;
...
I := High(Byte);
I := I + 1;
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.
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
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.
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
...
Snd := Click;
...
End;
codice
Snd := SoundFuncs.Click;
Come anche
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
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
Non valgono per questo tipo gli sconfinamenti visti per i tipi precedenti.
Lezione successiva
[Sommario]
I tipi real
I tipi real definiscono insiemi di numeri che possono essere rappresentati con la
notazione in virgola mobile.
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 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
AnsiString è il tipo preferito per la maggior parte della applicazioni. A volte viene anche
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.
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
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
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 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
Per assegnare dei valori a varibili di tipo Set, facendo riferimento all'esempio precedente,
si procede come segue:
...
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.
Array statici
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
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.
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.
Quando si dichara un array dinamico questo non occupa memoria fino a quando non si
effettua una chiamata alla funzione SetLength.
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
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.
Nome : String[40];
Cognome : String[40];
Eta : Integer;
End;
NomeVariabile.Nomecampo
Dall'esempio precedente
...
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;
Campo1 : Tipo1;
...
Campon : Tipon;
ListaCostanti1 : (Variante1);
...
ListaCostantin : (Varianten);
End;
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.
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
Tipi file
I tipi file rappresentano un insieme ordinato di elementi tutti dello stesso tipo. Un tipo file
viene dichiarato come segue
Con riferimento all'esempio per i tipi record possiamo dichiarare un tipo file come segue
...
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]
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 esempio
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
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.
Var B : Boolean;
F : TBisestileFunc;
...
F := Bisestile;
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
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.
Var I : Integer;
A, B : Real;
Nome : String;
Lezione successiva
[Sommario]
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.
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.
Lezione successiva
[Sommario]
L'Object Pascal distingue tra funzioni, che restituiscono dei valori, e le procedure
che non restituiscono alcun valore.
Procedure
Procedure NomeProcedura(ListaParametri);
DichiarazioniLocali;
Begin
CodiceProcedura;
End;
I parametri passati ad una procedura possono essere di due tipi: per riferimento o
per valore.
Funzioni
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
oppure
il risultato è lo stesso.
Lezione successiva
[Sommario]
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
If Espressione then
BloccoEspressioneVera
else
BloccoEspressioneFalsa
N.B.
All'interno del blocco di codice del ramo THEN, tutte le istruzioni devono terminare
con il punto e virgola!
If A < B then
Begin
A := A + 1;
B := B - 1;
Else
Begin
B := B + 1;
A := A - 1;
End;
If A < B then
Else
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
Else
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
oppure
If A > B then
Begin
If A < 4 then
Else
End;
nel secondo
If A > B then
Begin
If A < 4 then
End
Else
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.
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.
1 : ...;
2 : ...;
...
End;
4 : ..;
Else
End;
Come valori alla sinistra dei due punti possono essere specificati anche elenchi di
valori
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]
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.
BloccoIstruzioniDaRipetere;
oppure
BloccoIstruzioniDaRipetere;
For I := 1 to 10 do
...
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
For I := 1 to 10 do
Begin
A := 11 - I;
End;
For I := 10 downto 1 do
Begin
A := 11 - I;
End;
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 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.
Nel caso in cui A sia maggiore od uguale a B il ciclo non viene eseguito neanche una
volta.
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.
Repeat
Inc(A);
Until A = B;
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]
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 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.
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.
ElencoMembri
End;
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]
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.
Begin
Inherited;
End;
I metodi possono essere di tre tipi: statici, virtuali, dinamici. Quando un metodo viene
definito, per impostazione predefinita è statico.
Procedure Metodo1;
End;
TClasse2 = Class(TClasse1)
Procedure Metodo1;
End;
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;
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
Procedure Metodo1;
End;
TClasse2 = Class(TClasse1)
End;
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;
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
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;
Gestori di messaggi
Private
...
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
Begin
ProcessEnter
Else
Inherited;
End;
Lezione successiva
[Sommario]
Property NomeProprieta[Indici]:
Tipo index CostanteIntera Specificatori;
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à.
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
Begin
Result := FTimer.Interval;
End;
Begin
FTimer.Interval := Value;
End;
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.
Public
...
End;
StringArray[10] := 'Carlo';
al posto di
StringArray.Strings[10] := 'Carlo';
Per ovvi motivi, una classe può avere solamente una proprietà predefinita.
Lezione successiva
[Sommario]
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à.
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.
Oggetto is Classe
Oggetto as Classe
Lezione successiva
[Sommario]
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.
ErrorCode : Integer;
End;
oppure
Try...Except
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.
Facoltativamente può essere definito dopo l'elenco dei gestori di eccezione un blocco
else che gestica tutte le eccezioni non gestite dai gestori definiti.
Try
...
Except
On EZeroDivide do GestisciZeroDivideException;
On EOverFlow do GestisciOverFlowException;
On EMathError do GestisciMathErrorException;
Else
GestisciTutteLeAltreEccezioni;
End;
Try
...
Except
GestisciLeEccezioni;
End;
Try...Finally
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]
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.
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
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
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 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
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]
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);
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.
Nella finestra che compare , nel gruppo "Orizontal" e "Vertical" scegliamo "Center in
Window"
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
In questo modo alla pressione del bottone verrà visualizzata una finestra di dialogo
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.
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.
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.
Modifichiamo il codice del gestrore dell'evento OnClick del bottone nel modo
seguente:
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)
Lezione successiva
[Sommario]
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.
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.
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
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.
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 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 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
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]
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.
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
Fig. 10
Dialogs
Fig. 11
Win 3.1
Fig. 12
Samples
Fig. 13
ActiveX
Fig. 14
Servers
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.
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,
Lezione successiva
[Sommario]
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
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);
Close;
End
Else
Caption := FFileName;
End;
end;
Seguendo lo stesso metodo digitiamo il seguente codice per il gestore di evento OnClose
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.
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
begin
Inherited Create(AOwner);
FDocCount := Count;
FNewDoc := NewDoc;
FFileName := FileName;
end;
procedure TEditorWin.ResetModified;
begin
Doc.Modified := False;
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]
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.
DefaultExt = '.txt'
Filter = 'Text files (*.txt)|*.txt|INI files (*.INI)|*.INI|
NFO files (*.NFO)|*.NFO|Any file (*.*)|*.*'
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.
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
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 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.
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]
Create(Suspended : Boolean);
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
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
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.
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.
L'oggetto evento, o meglio la classe TEvent, non è altro che una struttura wrapper
per gli oggetti event di Windows.
Lezione successiva
[Sommario]
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.
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]
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.
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]
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.
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
TTable
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.
TStoredProc
TDatabase
TSession
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:
TBatchMove
L'unico metodo rilevante per questo oggetto è il metodo Execute che avvia la
procedura batch.
TUpdateSQL
Lezione successiva
[Sommario]
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
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.
TDBNavigator
TDBText
TDBEdit
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
TDBImage
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
TDBCtrlGrid
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.
Lezione successiva
[Sommario]
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.
Lezione successiva
[Sommario]
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".
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.
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
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.
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!
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
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.
Aggiungiamo nella parte Private della nostra classe TMyEdit la funzione Reverse
dichiarandola come segue
SetLength(Result, L);
For I := 1 to Length(Text) do
Result[(L+1) - I] := Text[I];
end;
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:
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;
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
...
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.
Lezione successiva
[Sommario]
LEZIONE 36: 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.
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
che è equivalente a
exports DLLProcedure1;
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
Lezione successiva
[Sommario]
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).
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]
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.
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
Il codice dell'event handler sostituirà il il tag speciale con la data corrente. Supponendo che il
<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
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
<H1>Titolo</H1>
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.
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.
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".
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 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>
Lezione successiva
[Sommario]
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]