Creazione di componenti in DELPHI. Introduzione alla creazione di componenti Delphi

Prima di creare il tuo componente, devi selezionarne un antenato. Chi può essere l'antenato del tuo componente?

Tipicamente utilizzato come predecessore di TComponent, TControl, TWinControl, TGraphicControl, TCustomXXXXXX, nonché di tutti i componenti della tavolozza dei componenti.
Prendiamo ad esempio il componente TOpenDialog, che si trova nella pagina Dialogs della palette dei componenti. Fa bene il suo lavoro, ma ha un piccolo inconveniente. Ogni volta che lo utilizzi, devi modificare ogni volta il valore della proprietà Options. E, di regola, queste sono le stesse azioni.
OpenDialog1.Options:= OpenDialog1.Options + ;

in modo che il file che stiamo tentando di aprire con questa finestra di dialogo esista effettivamente sul disco.
Abbiamo già scelto un compito per noi stessi, non resta che creare un componente. Creiamo uno spazio vuoto per il componente selezionando il comando Componente/Nuovo componente... dal menu e selezionando nella finestra di dialogo
Tipo antenato: TOpenDialog
Nome della classe: TOurOpenDialog
Pagina tavolozza: il nostro test
Abbiamo fatto clic su OK e ora abbiamo un modello per il nostro componente futuro.

Sovrascriviamo il costruttore per questo componente, ad es. nella sezione pubblica inserire la riga:

costruttore Crea(AProprietario: TComponent); oltrepassare;

Facendo clic su questa riga Ctrl + Shift + C si crea un modello per questo metodo, all'interno del quale inseriamo le seguenti righe:

Crea ereditato (proprietario); (Chiama il costruttore ereditato)
Opzioni:= Opzioni + ; (Effettuiamo le azioni di cui abbiamo bisogno)

Nota: le scorciatoie da tastiera Ctrl + Maiusc + frecce su/giù ti consentono di navigare tra la dichiarazione di un metodo e la sua implementazione.

Installazione del componente creato Componente/Installa componente...
Installa nel nuovo pacchetto
Nome del file del pacchetto: C:Program FilesBorlandDelphi4LibOurTest.dpk
Descrizione del pacchetto: Il nostro pacchetto testato

Non ti piace che il nostro componente abbia la stessa icona di quello standard? Allora creiamone uno nostro per lui.
Per fare questo dobbiamo chiamare Tools/Image Editor. Creare un nuovo file *.dcr.
Inserisci l'immagine Risorsa/Nuovo/Bitmap al suo interno. Imposta la dimensione dell'immagine su 24x24 pixel. E poi - la tua creatività...
Nota: il colore dei punti che corrisponde al colore del punto nell'angolo in basso a sinistra dell'immagine sarà considerato TRASPARENTE!
Una volta creato il disegno, rinominalo da Bitmap1 a TOurOpenDialog e salva il file come OurOpenDialog.dcr.
Rimuovere il componente dal pacchetto e installarlo nuovamente (solo in questo caso verrà aggiunto il collegamento al file *.dcr).
Compila, installa e buona fortuna!

unità La nostra finestra di dialogo aperta; interfaccia usi Finestre, Messaggi, SysUtils, Classi, Grafica, Controlli, Moduli, Finestre di dialogo; tipo TOurOpenDialog = classe(Apri finestra di dialogo) privato(Dichiarazioni private) protetto pubblico(Dichiarazioni pubbliche) costruttore Crea(Proprietario: TComponent); oltrepassare; pubblicato FINE; procedura Registrati; implementazione procedura Registrati; inizio RegisterComponents("Esempi", ); FINE; (La nostra finestra di dialogo aperta) costruttore TOurOpenDialog.Create(AProprietario: TComponent); inizio ereditato Crea(proprietario); (Chiama il costruttore ereditato) Opzioni:= Opzioni + ; (Effettuiamo le azioni di cui abbiamo bisogno) FINE; FINE.

Una dichiarazione di componente è composta da sezioni come privato, protetto, pubblico e pubblicato. Cosa vogliono dire?
Queste sono le direttive sulla visibilità.
Tutto ciò che è dichiarato nella sezione privato, disponibile solo all'interno del modulo in cui è dichiarata la classe (dichiarazioni private). Qui, di regola, vengono dichiarate variabili che memorizzano valori di proprietà, nonché metodi (procedure o funzioni) per accedervi.
Tutto ciò che è dichiarato nella sezione protetto, è disponibile sia nella sezione privata, sia ai discendenti di questa classe (interfaccia sviluppatore).
Qui puoi dichiarare metodi per accedere ai valori delle proprietà (se vuoi consentire ai figli del tuo componente di modificare questi metodi),
così come proprietà, metodi ed eventi (metodi per reagire agli eventi) in componenti di tipo TCustomXXX.
Tutto ciò che è dichiarato nella sezione pubblico, disponibile per qualsiasi utente del componente (interfaccia runtime).
I metodi vengono solitamente dichiarati qui. Nella sezione pubblicata è possibile dichiarare solo proprietà ed eventi (vengono dichiarati come proprietà).
Sono disponibili durante la progettazione dell'applicazione (interfaccia della fase di progettazione).

Proprietà

Digitare le proprietà massiccio- normali array Object Pascal, ma a differenza di questi ultimi possono essere indicizzati non solo da valori numerici ma anche da stringhe. Sfortunatamente, questo tipo di proprietà richiede un editor di proprietà personalizzato (nell'Ispettore oggetto, l'editor di proprietà ha un pulsante con tre punti [...]), quindi nell'esempio seguente la proprietà ArrayProp annunciato nella sez pubblico.

tipo TOurComponent = classe(Componente T) privato(Dichiarazioni private) FArrayProp: vettore Di numero intero; funzione GetArrayProp(aIndex: intero): intero; procedura SetArrayProp(aIndice: intero; cost Valore: intero); protetto(Dichiarazioni protette) pubblico(Dichiarazioni pubbliche) proprietà ArrayProp: intero Leggere OttieniArrayProp scrivere SetArrayProp; pubblicato(Dichiarazioni pubblicate) FINE;

Specificatori di proprietà

Specificatore predefinito specifica se salvare o meno il valore della proprietà nel file del modulo. Se il valore della proprietà corrisponde al valore predefinito- il valore nel file del modulo non viene salvato, se i valori non sono uguali - viene salvato. Puoi verificarlo posizionando il componente sul modulo e selezionando la voce di menu "Visualizza come testo" con il tasto destro del mouse. Predefinito non imposta il valore iniziale della proprietà su quello specificato. Questo deve essere fatto nel costruttore del componente.

unità Il nostro componente; interfaccia usi Windows, SysUtils, classi, grafica, moduli, controlli; tipo TOurComponent = classe(Componente T) privato(Dichiarazioni private) FMyInteger: intero; protetto(Dichiarazioni protette) pubblico(Dichiarazioni pubbliche) costruttore Crea(Proprietario: TComponent); oltrepassare; pubblicato(Dichiarazioni pubblicate) proprietà MioIntero: intero Leggere FMyInteger scrivere FMyInteger predefinito 10; FINE; implementazione costruttore TOurComponent.Create(AProprietario: TComponent); inizio ereditato Crea(proprietario); FInteger:= 10; FINE; FINE.

Specificatore nodefault sovrascrive il valore della proprietà predefinito. Questo specificatore viene in genere utilizzato per sovrascrivere il valore predefinito di una proprietà ereditata.
Per esempio: proprietà Dimensione dell'auto nodefault;

Specificatore immagazzinato specifica quando salvare il valore della proprietà nel file del modulo. Dopo immagazzinato posso sopportare VERO(salva sempre) falso(non salvare mai) o il nome di una funzione che restituisce un risultato booleano.

proprietà OneProp: numero intero Leggere FOneProp scrivere ImpostaUnoProp immagazzinato Falso; proprietà TwoProp: intero Leggere FDueProp scrivere SetDueProp immagazzinato VERO; proprietà ThreeProp: intero Leggere FTreProp scrivere SetTreProp immagazzinato Futto;

E l'ultima cosa:
Per aggiungere un'immagine ad un componente per la dimostrazione nel pannello dei componenti, è necessario: - crearla con una dimensione di 24*24 con il nome file.dcr (nella risorsa, il nome dell'immagine è uguale al nome di il componente, in maiuscolo)
- posizionare l'immagine accanto al componente.

Sviluppo Software per il sistema operativo Windows e altri popolari può essere eseguito utilizzando una varietà di tipi di strumenti. Tra quelli molto popolari tra i programmatori russi e stranieri c'è il programma Delphi. Quali sono le specifiche di questo strumento di sviluppo? Quali sono le sue caratteristiche più notevoli?

Informazioni generali su Delfi

Delphi è un ambiente di sviluppo per programmi applicativi progettati per essere eseguiti su Windows, MacOS e dispositivi mobili. sistemi operativi- iOS e Android. Si caratterizza per la semplicità del linguaggio e delle procedure di generazione del codice.

Se necessario, fornisce comunicazione di basso livello con il sistema operativo e le librerie scritte in C e C++. I programmi creati utilizzando Delphi non richiedono l'esecuzione di shell di terze parti, come Java Virtual Machine. Delphi è un ambiente di sviluppo che può essere utilizzato con successo sia da professionisti che per scopi didattici. Per padroneggiare le sue capacità di base, non è necessario possedere qualifiche elevate e conoscenza di linguaggi di programmazione complessi.

Principali vantaggi

Studiamo quali sono i principali vantaggi del prodotto software in questione. Quando una particolare azienda IT giustifica la scelta di un ambiente di sviluppo, Delphi diventa la scelta di molti programmatori e ne viene consigliato l'uso. Ciò è dovuto al fatto che questo ambiente consente di creare applicazioni nel minor tempo possibile, garantendone prestazioni elevate anche su quei computer che presentano caratteristiche hardware modeste. Un argomento significativo a favore della scelta dell'ambiente di sviluppo in questione è che esso può essere integrato con nuovi strumenti che non sono forniti dall'insieme standard di soluzioni presenti nell'interfaccia Delphi.

Studiamo ora le sfumature dell'uso pratico delle funzionalità Delphi.

Specifiche dell'interfaccia

Innanzitutto puoi prestare attenzione ad alcune funzionalità dell'interfaccia dell'ambiente di sviluppo software in questione. Pertanto, la struttura dell'area di lavoro del programma prevede il lavoro simultaneo con diverse finestre principali. Consideriamo questa proprietà in modo più dettagliato.

L'ambiente di sviluppo Delphi, in particolare la versione 7, prevede l'uso dei seguenti moduli chiave: progettista di moduli, editor, tavolozza, ispettore oggetti e libro di riferimento. In alcune modifiche Delphi, i componenti contrassegnati potrebbero avere nomi diversi. Ad esempio, un editor può corrispondere a una finestra di codice di programma e un designer può corrispondere a una finestra di modulo. Tuttavia scopo funzionale i loro saranno gli stessi. Il Delphi contrassegnato può integrare vari strumenti ausiliari. I primi due sono considerati i principali dal punto di vista delle procedure di sviluppo del programma. Ma anche il resto è importante. Diamo un'occhiata alle caratteristiche dell'utilizzo dei moduli Delphi contrassegnati.

Progettista di moduli, editor e tavolozza

Con l'aiuto del progettista del modulo, lo sviluppatore crea l'interfaccia del suo programma. A sua volta, il suo codice è scritto nell'editor. Molti programmatori che consigliano di scegliere l'ambiente di sviluppo Delphi come soluzione ottimale citano come argomento la facilità d'uso del progettista di moduli. Alcuni esperti ritengono che questo processo sia più simile a un gioco.

Non appena l'utente inizia a creare un programma e avvia il progettista del modulo, inizialmente non ci sono elementi al suo interno, è vuoto. Ma puoi compilarlo immediatamente utilizzando gli strumenti situati su un altro modulo Delphi: la tavolozza. Gli elementi dell'interfaccia del programma configurati nel progettista del modulo devono essere controllati da comandi che, a loro volta, vengono scritti nell'editor.

Ma torniamo per ora alla tavolozza. Usandolo, puoi posizionare gli oggetti necessari nell'area di progettazione del modulo. Per utilizzare un particolare strumento, è necessario fare clic una volta su di esso, mentre si trova nell'area della tavolozza, e una seconda volta, nella finestra di progettazione del modulo. Successivamente, l'oggetto corrispondente verrà spostato nell'area di sviluppo e potrai scriverne il codice nell'editor.

Ispettore oggetti

Un altro elemento significativo contenuto in Delphi, un ambiente di sviluppo di applicazioni per Windows e altre piattaforme comuni, è l'ispettore degli oggetti. Potresti notare che le informazioni visualizzate in esso cambiano: questo è influenzato dallo stato dell'oggetto selezionato nell'area di progettazione del modulo.

La struttura dell'ispettore oggetto è la seguente. Si compone di due finestre. Ognuno contiene algoritmi che determinano il comportamento dei componenti corrispondenti. Il primo visualizza le proprietà, il secondo visualizza gli eventi. Se il programmatore desidera apportare modifiche agli algoritmi che influiscono su un componente specifico, vengono utilizzate le funzionalità dell'ispettore oggetto. Ad esempio, puoi modificare il posizionamento di alcuni elementi dell'interfaccia del programma, la loro altezza e larghezza.

La finestra di ispezione oggetto dispone di schede che ti consentono di passare da una pagina all'altra che visualizzano proprietà o eventi direttamente correlati all'editor. Quindi, se fai doppio clic sul lato destro di uno qualsiasi degli elementi visualizzati sullo schermo, il codice che corrisponde a un particolare evento verrà registrato nell'editor.

Lo sviluppo del software in Delphi prevede l'utilizzo di Object Inspector per risolvere una varietà di problemi. Ciò è predeterminato dal fatto che con l'aiuto di questo strumento è possibile modificare le proprietà praticamente di qualsiasi oggetto situato sul modulo, nonché del modulo stesso. Diamo uno sguardo più da vicino ad alcune delle funzionalità del lavoro con l'ispettore oggetto.

Ispettore oggetto: utilizzo delle funzionalità

Per comprendere come funziona l'IDE Delphi in termini di interazione tra Object Inspector e Form Inspector, puoi provare ad apportare modifiche alle proprietà di alcuni elementi comuni dell'interfaccia software in Windows, ad esempio Memo, Button e Listbox (lo faremo esploreremo la loro essenza più in dettaglio un po’ più avanti). Innanzitutto, devi inserirli nel modulo utilizzando gli strumenti Delphi disponibili.

Puoi provare a sperimentare con la proprietà Ctl3D. Per fare ciò, è necessario fare clic sul modulo, quindi andare nell'ispettore dell'oggetto e modificare il valore della proprietà in questione. Successivamente la forma cambierà in modo significativo e allo stesso tempo verrà modificata la proprietà Ctl3D su ciascuno degli elementi posizionati nella finestra di disegno.

Dopo aver effettuato gli esperimenti possiamo tornare alla videata ed attivare il valore Ctl3D. Successivamente, diamo un'occhiata agli elementi Memo e Listbox. Ora puoi modificarne le proprietà, la posizione nel modulo e l'aspetto. Ad esempio, selezionando l'opzione Modifica nella voce di menu e poi Dimensione, il programmatore può modificare la larghezza e l'altezza degli oggetti. C'è un'opzione per centrarli selezionando Modifica e Allinea. Le azioni corrispondenti influenzeranno gli elementi visualizzati nell'Ispettore oggetto.

Utilizzando il modulo Delphi in questione, è possibile modificare le proprietà dei componenti. Quindi, ad esempio, se l'attività è determinare un colore specifico per loro, ci sono opzioni per utilizzare più strumenti contemporaneamente. Per prima cosa è possibile inserire un comando corrispondente a un colore, ad esempio rosso - clRed, nell'area. In secondo luogo, l'utente può selezionare il colore desiderato da un elenco. In terzo luogo, c'è la possibilità di fare doppio clic sulle proprietà Colore: verrà visualizzata una finestra di selezione del colore. Allo stesso modo, lo sviluppatore può modificare altri attributi degli oggetti, ad esempio il tipo di carattere, il colore o la dimensione.

Direttorio

Delphi è un ambiente di sviluppo completato da un sistema di aiuto abbastanza dettagliato. Per accedervi, seleziona Aiuto dal menu. Successivamente, nella finestra verrà visualizzato uno di quelli che abbiamo notato sopra. moduli software ambiente di sviluppo in questione - libro di consultazione. La particolarità del suo utilizzo è che quando si preme F1, l'utente riceverà un suggerimento specifico che riflette le specificità dell'utilizzo dello strumento corrente. Ad esempio, se un programmatore sta lavorando con l'Ispettore Oggetto, può selezionare una delle proprietà, quindi premere F1 e ottenere informazioni di aiuto sull'opzione corrispondente. Lo stesso può essere fatto quando si lavora con qualsiasi altro elemento dell'interfaccia che includa l'ambiente di sviluppo Delphi 7 e altre versioni del tipo di software corrispondente.

Altri elementi dell'interfaccia

Tra gli altri componenti significativi dell'interfaccia in questione soluzione software- menu, pannello accesso veloce, così come un editor di immagini. Per quanto riguarda il menu, consente al programmatore di accedere rapidamente ai componenti necessari presenti nella struttura dell'ambiente di sviluppo. Puoi usarlo sia usando il mouse che usando i tasti di scelta rapida. Appena sotto il menu c'è il pannello di accesso rapido. Alcune delle sue funzioni duplicano quelle del menu, ma sono più veloci da accedere. Delphi è in qualche modo simile al programma Paint su Windows. Cioè, con l'aiuto di esso puoi apportare semplici modifiche alle immagini, inserire iscrizioni e altri elementi su di esse.

Strumenti di programmazione

Delphi è un ambiente di sviluppo che include un gran numero di strumenti progettati per migliorare la produttività dei programmatori. Pertanto, i moduli chiave di cui abbiamo discusso sopra sono integrati da una serie di strumenti speciali. Questi includono un debugger, un compilatore e i componenti WinSight e WinSpector. Si noti che in alcune versioni di Delphi gli elementi contrassegnati devono essere installati separatamente. Studiamo le loro specifiche.

Debug Delphi

Per quanto riguarda il debugger, questo strumento integra l'editor di codice in termini di esecuzione dei necessari controlli di correttezza degli algoritmi software corrispondenti. Con esso, uno sviluppatore può effettivamente esaminare il suo codice sorgente riga per riga. In alcuni casi, quando si risolve un compito come lo sviluppo di componenti, Delphi come prodotto indipendente può essere integrato con un debugger esterno, che offre al programmatore funzionalità avanzate per controllare il codice del software creato.

Compilatore Delphi

Studiamo ora le specifiche del compilatore dell'ambiente di sviluppo in questione. Si noti che potrebbero esserci diversi elementi corrispondenti nella struttura Delphi. Quindi, esiste un'opzione per utilizzare il compilatore DCC, che è utile nei casi in cui l'attività è lavorare con l'applicazione in un debugger esterno.

Winsight e WinSpector

I moduli specificati sono quelli che devono essere installati in aggiunta su Delphi. Sono caratterizzati da una relativa difficoltà di padronanza. Tuttavia, molti programmatori che hanno scelto l'ambiente di sviluppo Delphi credono che questi componenti debbano essere imparati ad usare. Pertanto, il modulo Winsight viene utilizzato per monitorare i messaggi di Windows. È necessario un componente come WinSpector per registrare lo stato del computer in un file speciale. Se riscontri problemi durante lo sviluppo del software, puoi sempre aprire questo file e vedere cosa potrebbe causare il problema.

Componenti standard

L'ambiente di sviluppo Delphi, di cui stiamo parlando in generale, include una serie di componenti standard che è utile conoscere. Gli esperti li classificano come: MainMenu, PopupMenu, Label, Edit, Memo, Button, Checkbox, Radiobutton, Listbox, Combobox, Scrollbar, Groupbox, Panel e Scrollbox. Studiamo le loro specifiche in modo più dettagliato.

Il componente MainMenu è progettato per posizionare il menu principale nell'interfaccia del programma creato. Per fare ciò, è necessario posizionare l'elemento corrispondente nel modulo, quindi chiamare la proprietà Items tramite la finestra di ispezione oggetto e quindi determinare le voci di menu necessarie.

Il componente PopupMenu è progettato per posizionare menu a comparsa nell'interfaccia del programma creato, ovvero quelli che si aprono facendo clic con il tasto destro del mouse.

Il componente Label viene utilizzato per visualizzare il testo nella finestra del programma. Può essere personalizzato, ad esempio, impostando il carattere desiderato nell'ispettore dell'oggetto.

Il componente Modifica viene utilizzato per visualizzare una porzione di testo sullo schermo che l'utente può modificare mentre il programma è in esecuzione. È completato dal componente Memo che, a sua volta, può essere utilizzato per lavorare con testi più grandi. Questo elemento include, ad esempio, opzioni come la copia del testo.

Il componente Button è progettato per eseguire determinate azioni premendo un pulsante mentre il programma è in esecuzione. È necessario posizionare l'elemento corrispondente sul modulo, quindi inserire il codice del programma richiesto.

Il componente Checkbox consente di visualizzare delle righe sullo schermo con una piccola finestra in cui è possibile posizionare una casella di controllo utilizzando il mouse. Un elemento simile è Radiobutton. Differiscono, in primo luogo, aspetto- il secondo componente ha la forma di un cerchio e, in secondo luogo, il primo elemento consente la selezione simultanea di più opzioni, Radiobutton - solo una.

Il componente Listbox viene utilizzato per visualizzare un elenco sullo schermo che l'utente può scorrere utilizzando il mouse. Un altro elemento, Combobox, è in qualche modo simile ad esso, ma è completato dalla possibilità di inserire testo in un campo speciale.

Il componente Barra di scorrimento è una barra di scorrimento in Windows. In genere appare automaticamente non appena lo spazio del testo o il modulo con gli oggetti diventa più grande della finestra.

Il componente Groupbox viene utilizzato per registrare l'ordine in cui ti sposti tra le finestre quando premi il tasto TAB. Può essere integrato con un elemento Panel, che può essere utilizzato per spostare diversi oggetti sul modulo.

Il componente Scrollbox permette di fissare un'area su un form che può essere fatta scorrere sia orizzontalmente che verticalmente. Questa proprietà caratterizza di default le principali finestre di sviluppo di Delphi. Ma se è necessario abilitare tale opzione su una sezione specifica del modulo, puoi utilizzare il componente Scrollbox.

Riepilogo

Delphi è un potente ambiente di sviluppo di applicazioni che allo stesso tempo è caratterizzato dalla facilità d'uso delle sue funzioni principali. Con l'aiuto degli strumenti inclusi nella sua struttura, puoi creare il massimo tipi diversi programmi per Windows e altri sistemi operativi popolari.

La scelta degli strumenti di sviluppo Delphi da parte di molti programmatori è determinata dalla facilità d'uso delle interfacce del software corrispondente, nonché da un'ampia gamma di strumenti utili per lavorare in qualsiasi fase della creazione del programma - nella fase di progettazione, programmazione di algoritmi o debug.


Sviluppare i propri componenti

Se non sei soddisfatto dei componenti standard forniti con Delphi, allora è il momento di provare a crearne di tuoi. Inizieremo prima con quelli semplici e gradualmente passeremo a quelli più complessi. Quindi, cominciamo.

Prima di creare il tuo componente, è importante scegliere il giusto antenato. Chi può essere l'antenato del tuo componente? Tipicamente utilizzato come predecessore di TComponent, TControl, TWinControl, TGraphicControl, TCustomXXXXXX, nonché di tutti i componenti della tavolozza dei componenti. Prendiamo ad esempio il componente TOpenDialog, che si trova nella pagina Dialogs della palette dei componenti. Fa bene il suo lavoro, ma ha un piccolo inconveniente. Ogni volta che lo utilizzi, devi modificare ogni volta il valore della proprietà Options. E, di regola, queste sono le stesse azioni.


Facendo clic su questa riga Ctrl + Shift + C si crea un modello per questo metodo, all'interno del quale inseriamo le seguenti righe:


Installazione del componente creato Componente/Installa componente...

  • Installa nel nuovo pacchetto
  • Nome del file del pacchetto: C:\Programmi\Borland\Delphi4\Lib\OurTest.dpk
  • Descrizione del pacchetto: Il nostro pacchetto testato

Non ti piace che il nostro componente abbia la stessa icona di quello standard? Allora creiamone uno nostro per lui. Per fare questo dobbiamo chiamare Tools/Image Editor. Creare un nuovo file *.dcr. Inserisci l'immagine Risorsa/Nuovo/Bitmap al suo interno. Imposta la dimensione dell'immagine su 24x24 pixel. E poi - la tua creatività... Nota: il colore dei punti che corrisponde al colore del punto nell'angolo in basso a sinistra dell'immagine sarà considerato TRASPARENTE!

Una volta creato il disegno, rinominalo da Bitmap1 a TOurOpenDialog e salva il file come OurOpenDialog.dcr. Rimuovere il componente dal pacchetto e installarlo nuovamente (solo in questo caso verrà aggiunto il collegamento al file *.dcr).

Compila, installa e buona fortuna!

Sviluppo di applicazioni professionali con Delphi 5 | Strumenti di sviluppo | ComputerPress 2"2001

Creazione di componenti Delphi

Introduzione alla creazione di componenti Delphi

Quando si sviluppano applicazioni utilizzando Borland Delphi, è conveniente creare componenti per i seguenti motivi:

  1. Facilità d'uso. Il componente viene inserito nel modulo ed è necessario impostare i valori delle proprietà e scrivere il codice del gestore eventi. Pertanto, se in un progetto qualsiasi combinazione di controlli e gestori di eventi ad essi associati si verifica in due posti, ha senso pensare alla creazione di un componente corrispondente. Se la combinazione di controlli e gestori eventi ad essi associati si verifica più di due volte, la creazione di un componente garantisce un risparmio di fatica durante lo sviluppo dell'applicazione.
  2. Organizzazione semplice dello sviluppo di progetti di gruppo. Nello sviluppo di gruppo è possibile definire singole parti del progetto come componenti e assegnare il lavoro a diversi programmatori. È possibile eseguire il debug dei componenti separatamente dall'applicazione, il che è abbastanza semplice.
  3. Semplice e metodo efficace condividere il codice con altri programmatori. Esistono molti siti, ad esempio http://www.torry.net/, dove è possibile trovare componenti distribuiti gratuitamente o acquistarli per una tariffa simbolica.

Pacchetti di funzionalità

In Delphi, i componenti sono archiviati in pacchetti. L'elenco dei pacchetti di componenti utilizzati può essere richiamato utilizzando la voce di menu Componente/Installa pacchetti (tuttavia, per qualche motivo questa finestra di dialogo ha il titolo Opzioni progetto).

Utilizzando questa finestra di dialogo è possibile aggiungere un nuovo pacchetto (Aggiungi) o rimuoverne uno esistente (Rimuovi). Eliminare non significa eliminare fisicamente un file dal disco, ma piuttosto rimuovere il collegamento dall'ambiente di sviluppo a questo pacchetto. Quando aggiungi un nuovo pacchetto, i componenti in esso memorizzati appaiono nella tavolozza, ma quando lo elimini, al contrario, scompaiono. Invece di eliminare un pacchetto, puoi "nasconderne" il contenuto in fase di sviluppo deselezionando il nome del pacchetto nell'elenco. È inoltre possibile visualizzare i componenti e le relative icone (Componenti). Infine, puoi modificare i pacchetti aggiunti dall'utente (Modifica): i pacchetti forniti con Delphi non possono essere modificati (il pulsante Modifica è disattivato).

In questa finestra di dialogo è possibile specificare come creare il progetto: utilizzando i pacchetti runtime o senza di essi. Da ciò risulta chiaro che esistono due tipi di pacchetti di componenti: pacchetto runtime (pacchetto che funziona in fase di esecuzione) e pacchetto design-time (pacchetto utilizzato durante lo sviluppo). Sono tutte DLL (librerie caricate dinamicamente).

I pacchetti runtime (estensione *.bpl) vengono consegnati all'utente finale insieme al progetto se il progetto è stato compilato con l'opzione Crea con pacchetti runtime abilitata. L'applicazione stessa (*.exe o *.dll) in questo caso risulta essere piccola, ma con essa devono essere trasferiti file *.bpl piuttosto grandi. Secondo gli esperti, consegnare un progetto con pacchetti runtime offre un vantaggio in termini di volume di file forniti, solo se contiene cinque o più moduli (*.exe o *.dll) scritti in Delphi. Quando questi moduli lavorano insieme, le risorse del sistema operativo vengono risparmiate, poiché un pacchetto caricato nella RAM serve diversi moduli.

I pacchetti in fase di progettazione (estensione *.dcp) vengono utilizzati solo nella fase di sviluppo. Durante lo sviluppo, supportano la creazione di componenti su un modulo. Il progetto Delphi compilato include codice non dal pacchetto dei componenti, ma dai file *.dcu. Sebbene il file *.dcp venga generato dal file *.dcu, il suo contenuto potrebbe non essere lo stesso se sono state apportate modifiche al file *.pas e il pacchetto non è stato ricompilato. La compilazione è possibile solo per i pacchetti creati dai programmatori. Ciò si ottiene facendo clic sul pulsante Modifica nella finestra di dialogo sopra. Successivamente, viene visualizzato un modulo che consente di manipolare il pacchetto.

Il pacchetto contiene due sezioni. La sezione Contiene contiene un elenco di moduli che costituiscono i componenti di questo pacchetto (file *.pas e *.dcu) e le relative icone (file *.dcr). La sezione Richiesto contiene collegamenti ad altri pacchetti necessari per il funzionamento di questi componenti. L'aggiunta di un nuovo componente al pacchetto viene eseguita con il pulsante Aggiungi, mentre la rimozione di uno esistente viene eseguita con il pulsante Rimuovi. Finché il pacchetto non viene compilato facendo clic sul pulsante Compila, eventuali modifiche apportate al pacchetto non verranno visualizzate nell'ambiente di sviluppo. Infine, il comando Installa è disponibile quando il contenuto del pacchetto è stato rimosso dall'ambiente di sviluppo deselezionando il nome del pacchetto nella finestra di dialogo precedente.

Il comando Opzione consente di selezionare opzioni per la compilazione di un pacchetto simili alle opzioni del progetto. Questi possono essere utilizzati per determinare quale tipo di pacchetto è un determinato pacchetto: in fase di esecuzione, in fase di progettazione o entrambi (tipo di pacchetto predefinito). Le opzioni definiscono le directory in cui cercare i moduli necessari e salvare i risultati della compilazione. Definiscono inoltre le azioni richieste per il debug: se controllare o meno l'intervallo di valori accettabili, come eseguire le ottimizzazioni, come gestire gli errori di I/O. Infine, le opzioni possono includere informazioni sulla versione del pacchetto. Questo è molto importante se l'applicazione viene distribuita insieme ai pacchetti runtime: durante l'esecuzione del programma di installazione, le informazioni sulla versione consentiranno di sostituire correttamente le versioni obsolete dei pacchetti e viceversa, se si tenta di installare un pacchetto di una versione precedente a quella uno già disponibile su un determinato computer, quest'ultimo non verrà sovrascritto.

Modelli di componenti

Delphi consente di creare semplici componenti compositi da alcuni componenti comuni selezionati su un modulo in fase di progettazione. L'esperto corrispondente viene richiamato tramite la voce di menu Componenti/Crea modello componente. Questa voce di menu è disponibile se nel modulo è selezionato almeno un componente. Dopo averlo selezionato, viene visualizzata la finestra di dialogo Informazioni sul modello componente.

In questa finestra di dialogo, dovresti specificare il nome della classe e il nome della pagina sulla tavolozza dei componenti in cui posizionare il nuovo componente. Se una pagina con questo nome non è presente nella tavolozza dei componenti, verrà creata. È inoltre possibile modificare l'icona proposta per il nuovo componente caricando un apposito file *.bmp.

Quando viene creato un modello, vengono ricordate sia le proprietà modificate dal programmatore nell'Object Inspector sia i gestori di eventi associati ai controlli selezionati. In questo caso, i gestori di eventi vengono ricordati completamente, senza filtrare le chiamate ad altri componenti (non selezionati nel modulo), variabili globali, metodi, ecc. Di conseguenza, se tali componenti (variabili, metodi) mancano in un altro progetto, quando si tenta di compilare un progetto di questo tipo, verrà ricevuto il messaggio diagnostico Identificatore sconosciuto.

Quando dovresti utilizzare i modelli? Innanzitutto nei casi in cui è necessario modificare eventuali proprietà disponibili di default nella classe base. Ad esempio, un'applicazione utilizza un controllo per modificare una riga di testo giallo. È possibile posizionare il componente TEdit nel modulo, modificare la proprietà Colore in giallo, contrassegnare questo componente e salvarlo come modello. Successivamente, puoi accedere a questo modello e il componente inserito nel modulo avrà un colore giallo. Tuttavia non bisogna abusare di questa opportunità, perché verrà creata una nuova classe per il controllo con un colore modificato e tutti i metodi virtuali verranno duplicati in memoria. Ciò avrà un impatto negativo sulle risorse del sistema operativo.

È inoltre utile utilizzare i modelli di componente quando è necessario trasferire un numero di componenti insieme ai gestori di eventi da un modulo a un altro. Per fare ciò, vengono tutti selezionati, viene creato un modello di componente, che viene inserito in un nuovo modulo. In questo caso verranno trasferiti non solo i componenti stessi, ma anche i gestori di eventi, cosa che non è possibile ottenere richiamando i comandi Copia/Incolla: in quest'ultimo caso i gestori di eventi andranno perduti.

I componenti creati utilizzando il comando Crea modello componente sono molto diversi dai normali componenti creati utilizzando il metodo standard (descritto di seguito). Visivamente, la differenza principale è questa: se un modello include più controlli, dopo aver inserito tale componente nel modulo, è possibile selezionare un singolo controllo ed eliminarlo, mentre il resto rimarrà nel modulo. Per i componenti standard, se includono più controlli, non è possibile selezionarne uno ed eliminarlo: il componente viene selezionato ed eliminato completamente.

Creazione di un componente semplice

Quando si scrive un nuovo componente, è necessario che sia chiaro che il componente è stato scritto per i programmatori, non per gli utenti finali. In questo caso, è auspicabile che il programmatore non approfondisca i dettagli dell'implementazione del componente, ma utilizzi semplicemente le proprietà e gli eventi da esso esposti. Ciò si ottiene attraverso test molto approfonditi. Un nuovo componente deve essere testato anche in situazioni per le quali non è chiaramente progettato.

Poniamo il problema come segue. È necessario creare un pulsante che emetta un segnale acustico quando viene premuto e implementarlo come componente in modo che il programmatore possa posizionarlo su un modulo e utilizzarlo. In generale, quando consideriamo i componenti, utilizzeremo molto spesso gli effetti esterni più semplici: segnale acustico, visualizzazione di un messaggio, ecc. Ciò implica che nei luoghi in cui vengono utilizzati effetti esterni, è possibile posizionare qualsiasi codice abbastanza complesso. Semplicemente non siamo interessati a lui al momento.

La creazione di un componente inizia selezionando la voce di menu Componente/Nuovi componenti. Successivamente viene visualizzata immediatamente la finestra di dialogo Nuovo componente.

In questa finestra di dialogo è necessario definire la classe antenata, il nome della classe appena creata, la pagina nella palette in cui verrà posizionato il nuovo componente, il nome del modulo contenente l'implementazione del nuovo componente e il percorso per Esso. Se il nuovo componente utilizza altri moduli il cui percorso non è descritto, è necessario definirli nel campo Percorso di ricerca.

Quindi, il primo (e forse il principale) compito è scegliere una classe antenata. Nell'elenco a discesa tutti i componenti disponibili nella tavolozza vengono offerti come classe antenata, compresi quelli che non sono inclusi nella distribuzione standard di Delphi. È necessario selezionare come classe antenata una classe che sia il più vicino possibile nelle proprietà alla classe che si sta creando. Per il nostro compito possiamo, ad esempio, selezionare TWinControl come antenato, ma in questo caso dovremo implementare tutti gli effetti visivi dei clic sui pulsanti, ecc. Pertanto, scegliamo TButton come antenato.

Il nome della classe appena creata deve riflettere il contenuto del componente e in nessun caso coincidere con il nome di un componente già registrato! Nella fase di compilazione di questo dialogo, non viene verificata la corrispondenza dei nomi: le avventure associate a tale errore inizieranno più tardi...

Quando selezioni una pagina devi sapere che se specifichi il nome di una pagina inesistente ne verrà creata una nuova.

Infine, quando si fa clic su entrambi i pulsanti Installa e OK, verrà creato un modello per implementare il nuovo componente. Tuttavia, quando si fa clic sul pulsante Installa, il modello verrà inserito nella tavolozza dei componenti e quando si fa clic su OK, verrà semplicemente creato. Si consiglia di utilizzare il pulsante Installa. Una volta installato, il componente può essere inserito nel modulo. Ora tutte le modifiche apportate al codice di implementazione del componente verranno compilate insieme al progetto e il programmatore riceverà immediatamente messaggi di errore. Se il componente non è installato, per trovare errori è necessario compilarlo tramite l'editor dei pacchetti (vedi sopra) facendo clic sul pulsante Compila, che è meno conveniente.

Quindi, dopo aver fatto clic sul pulsante Installa, viene visualizzata un'altra finestra di dialogo che consente di determinare il pacchetto in cui verrà posizionato questo componente.

Questa finestra di dialogo ha due pagine, nella prima delle quali puoi selezionare uno dei pacchetti esistenti, e nella seconda puoi crearne uno nuovo. È altamente consigliabile fornire una breve descrizione testuale del pacchetto; questo è ciò che verrà mostrato nella finestra di dialogo richiamata dal comando Componente/Installa pacchetti (vedi sopra). Dopo aver selezionato un pacchetto e premuto il pulsante OK, viene richiamato l'editor del pacchetto, dove viene automaticamente posizionato il modulo appena creato per l'implementazione del nuovo componente. È utile non chiuderlo, ma spostarlo in uno degli angoli dello schermo in modo che possa essere attivato premendo il pulsante del mouse.

Allo stesso tempo, nell'editor del codice verrà creato uno “spazio vuoto” per descrivere il nuovo componente:

Pulsante unitàBeep; l'interfaccia utilizza Windows, Messaggi, SysUtils, Classi, Grafica, Controlli, Moduli, Finestre di dialogo, StdCtrl; tipo TButtonBeep = class(TButton) privato (Dichiarazioni private) protetto (Dichiarazioni protette) pubblico (Dichiarazioni pubbliche) pubblicato (Dichiarazioni pubblicate) end; Registro delle procedure; procedura di attuazione Registro; iniziare RegisterComponents("Esempi", ); FINE; FINE.

La nuova classe stessa dichiara quattro sezioni, il cui significato è descritto in dettaglio nella sezione “Visibilità di variabili e metodi” dell'articolo precedente di questa serie (ComputerPress n. 1 "2001). Inoltre, la nuova classe definisce il Procedura di registrazione, che viene chiamata dall'ambiente di sviluppo Delphi durante l'installazione di questo modulo come componente. Contiene il nome della pagina sulla tavolozza in cui è posizionato questo componente e tra parentesi quadre il nome della classe. In generale, il metodo Register accetta come parametro un array di tipi di classe, poiché in un modulo possono essere implementati più componenti, pertanto vengono separati da una virgola, ad esempio:

Registro delle procedure; iniziare RegisterComponents("Esempi", ); FINE;

Continuiamo a risolvere il compito da svolgere: creare un pulsante che emetta un cigolio. Per prima cosa, facciamo qualcosa di banale (ma, come si scoprirà in seguito, in modo errato): assegniamo un gestore di eventi OnClick nel costruttore del pulsante. Per fare ciò, nella sezione privata, definiamo l'intestazione del nuovo metodo BtClick(Sender:TObject) e lo implementiamo nella sezione implementazione:

Procedura TButtonBeep.BtClick(Sender:TObject); inizioBeep; FINE;

costruttore Crea(AOwner:TComponent); oltrepassare;

con una direttiva di override obbligatoria! Implementiamolo nella sezione implementazione:

Costruttore TButtonBeep.Create(AOwner:TComponent); inizio ereditato Create(AOwner); SuClic:=BtClic; FINE;

Dopodiché compiliamo il componente. Inseriamo un pulsante nel modulo dalla pagina Esempi ed eseguiamo il progetto per l'esecuzione. Puoi assicurarti che il pulsante emetta un segnale acustico quando viene premuto!

Ora torniamo all'ambiente di sviluppo e assegniamo un gestore eventi OnClick nell'ispezione oggetto. Nel gestore eventi, visualizzeremo il testo nel titolo del modulo:

Procedura TForm1.ButtonBeep1Click(Sender:TObject); inizio Didascalia:="Test"; FINE;

Eseguiamo il progetto e proviamo a premere il pulsante. Il titolo del modulo cambia, ma il pulsante smette di emettere segnali acustici! L'errore è che abbiamo provato a definire due gestori per un evento del pulsante OnClick: uno all'interno del componente BtClick e l'altro assegnato utilizzando l'ispettore oggetto. Dopo aver elaborato il costruttore TButtonBeep, avevamo un collegamento al primo gestore BtClick. Quindi le risorse vengono caricate e il metodo ButtonBeep1Click viene assegnato al gestore eventi OnClick. In questo caso, il collegamento al primo gestore - BtClick - viene irrimediabilmente perso.

Pertanto, quando scrivi nuovi componenti, dovresti sempre considerare la modifica delle proprietà e dei gestori eventi utilizzando la finestra di ispezione oggetto. Se una proprietà (evento) non dovesse cambiare, non dovrebbe essere mostrata nell'Ispettore Oggetto. E se è già visibile, dovresti nasconderlo (ne parleremo più avanti). Il programmatore ha tutto il diritto di modificare qualsiasi proprietà nell'ispettore oggetto e, se in seguito il componente smette di funzionare, la colpa è dello sviluppatore del componente, ma in nessun caso del programmatore che lo utilizza.

Come possiamo risolvere correttamente questo problema? Un modo per creare componenti è riscrivere i metodi esistenti. Osservando il file StdCtrls.pas, dove è implementato il codice sorgente per il componente TButton, puoi notare che contiene un metodo Click dinamico che può essere riscritto. Pertanto, torniamo nuovamente al codice sorgente creato dall'esperto Delphi durante la creazione del componente (rimuoviamo il costruttore e il metodo BtClick). Quindi nella sezione public definiamo l'intestazione del metodo:

Procedura Fare clic; oltrepassare;

ed ecco l'implementazione del metodo:

Procedura TButtonBeep.Click; iniziare il clic ereditato; segnale acustico; FINE;

Puoi verificare che il pulsante emette un cigolio quando viene premuto. Inoltre, quando si assegna un gestore eventi nell'ispettore oggetto, questo gestore viene eseguito e il segnale acustico non scompare! Il componente è implementato correttamente.

Utilizzando questo esempio, è utile analizzare i possibili errori durante la scrittura del codice:

  1. Direttiva di override dimenticata durante la definizione dell'intestazione del metodo Click. Il pulsante smette di emettere segnali acustici, pertanto il metodo Click non viene chiamato.
  2. Una chiamata dimenticata al metodo antenato (Click ereditato) nell'implementazione della procedura Click. Il pulsante continua a emettere un segnale acustico quando viene premuto, ma il codice nel gestore eventi assegnato nell'ispezione oggetto non viene eseguito. Pertanto, il metodo Click della classe TButton genera l'evento OnClick.

Ora cambiamo l'icona del componente TButtonBeep sulla palette. Per impostazione predefinita, l'icona del componente antenato viene utilizzata per un nuovo componente. Per fare ciò, richiamare l'editor di immagini utilizzando il comando Strumenti/Editor di immagini. Nell'editor, richiamare il comando File/Nuovo/File di risorse componente (*.dcr). Dopo il comando Risorsa/Nuovo/Bitmap, verrà visualizzata una finestra di dialogo che suggerisce una dimensione dell'icona di 32x32. Queste dimensioni predefinite dovrebbero essere modificate in 24x24: questa è la dimensione che devono avere le icone dei componenti! Dopo aver fatto clic su OK, dovresti disegnare un'immagine utilizzando strumenti standard simili a Editore di vernici. Ricorda che il colore del pixel in basso a sinistra è il colore della maschera: questo colore sarà “trasparente”.

Successivamente è necessario ridefinire il nome della risorsa con l'icona; per impostazione predefinita, il suo nome è Bitmap1. Il nuovo nome della risorsa deve corrispondere al nome della classe, nel nostro caso TButtonBeep.

Ora è necessario salvare il file icona nella stessa directory del modulo contenente la procedura Register per questo componente e con lo stesso nome del modulo. Solo che l'estensione del file non sarà *.pas, ma *.dcr. Il file con l'icona del componente è pronto. Tuttavia, se guardiamo la palette Componenti, vedremo che la vecchia icona è ancora lì. Se riavvii Delphi o anche il sistema operativo, la vecchia icona rimarrà ancora sulla tavolozza. Per modificare l'icona è necessario registrare nuovamente il componente. Per fare questo è necessario:

Questo esempio dovrebbe essere considerato un esercizio di prova. Prima di scrivere un nuovo componente, è necessario vedere se ne esistono di simili tra i componenti distribuiti liberamente. Esistono quasi tutti i pulsanti: trasparenti, che scappano, rotondi, colorati, ecc. La situazione è più o meno la stessa con gli altri componenti: discendenti della stessa classe. Pertanto, molto spesso è necessario implementare componenti costituiti da più controlli.

Pertanto, in questo esempio abbiamo esplorato l'uso della riscrittura del metodo per creare nuovi componenti.

Creazione di un componente complesso

Supponiamo che tu debba inserire un elenco di nomi di clienti in un'applicazione. Nella stessa applicazione dovrai inserire anche una lista di numeri telefonici. L'inserimento di un elenco è un'operazione abbastanza comune, quindi dovresti considerare di implementarlo come componente.

Per inserire un nuovo elemento nell'elenco, avrai bisogno di un editor: il componente TEdit. Successivamente, l'utente dovrebbe essere in grado di visualizzare l'elenco: sarà necessario il componente TListBox. Inoltre, avrai bisogno di comandi per scrivere il valore corrente da TEdit nell'elenco, modificare l'elemento dell'elenco selezionato ed eliminarlo. Il modo più semplice per implementare questi comandi è utilizzare i pulsanti. Per semplificare l'attività, posizioneremo un pulsante sul modulo, quando cliccato aggiungeremo il contenuto del componente TEdit all'elenco.

Quindi dobbiamo creare un nuovo componente che includa un TEdit, un TListBox e un TButton. Come sempre, iniziamo a crearlo con il comando Componente/Nuovo componente. Successivamente, viene visualizzata una finestra di dialogo in cui è necessario definire la classe antenata, il nome della classe e il nome del modulo. Non ci sono difficoltà con il nome della classe e del modulo, ma il nome della classe antenata non è chiaro. Abbiamo tre controlli. La classe antenato comune per loro è TWinControl. Ma se la scegliamo come classe antenata, ci troviamo di fronte ad un'implementazione molto lunga e noiosa del codice TButton, TEdit e TListBox. In questi casi è necessario selezionare un componente come classe antenata che può essere un “padre” rispetto ad altri componenti. Tra i componenti standard distribuiti con Delphi ce ne sono tre: TPanel, TGroupBox, TScrollBox. Selezioniamo il pannello come classe antenata, ma non il componente TPanel stesso, bensì la classe TCustomPanel. Discuteremo i vantaggi di scegliere TCustomPanel rispetto a TPanel di seguito.

Diamo un nome alla nuova classe TListAdd e facciamo clic sul pulsante Installa. Dopo aver selezionato il pacchetto, il componente verrà installato nella palette, da dove potrà essere inserito nel modulo dell'applicazione appena creata. Questo è comodo perché quando si compila il progetto verrà compilato anche il modulo componente e se ci sono errori il compilatore visualizzerà un messaggio.

Sarebbe conveniente posizionare i nostri controlli su qualche modulo e quindi creare un componente da essi. Non esiste un esperto di questo tipo nel pacchetto Delphi standard. Pertanto, dovrai creare tu stesso i componenti e posizionarli sul pannello. È ragionevole creare controlli - TButton, TEdit e TListBox - nel costruttore TCustomPanel, che ovviamente richiede una riscrittura. Per ora posizioniamo i controlli in un quadrato 100x100. Anche le loro coordinate devono essere determinate nel costruttore. Va tenuto presente che dopo che il costruttore di qualsiasi elemento di controllo è stato elaborato, non ha ancora un genitore, cioè non sa rispetto a quale finestra dovrebbe misurare le coordinate dell'angolo in alto a sinistra. Il tentativo di modificare le coordinate di una finestra figlia che non ha una finestra genitore genererà immediatamente un'eccezione. Pertanto, il primo operatore dopo aver chiamato il costruttore del controllo sarà quello di assegnargli un genitore, per il quale selezioneremo TCustomPanel. Ne faremo anche il proprietario, in questo caso non sarà necessario riscrivere il distruttore.

Quindi, nella sezione uses aggiungiamo il modulo StdCtrls, dove si trovano le descrizioni delle classi TEdit, TButton e TListBox, e nella sezione private definiamo tre variabili:

Modifica F privata: Modifica T; FListBox:TListBox; Tasto F: Tasto T;

Nella sezione public dichiariamo l'intestazione del costruttore con la direttiva override obbligatoria:

Creazione costruttore (Proprietario:TComponent); oltrepassare;

Implementiamo il costruttore nella sezione implementazione:

Costruttore TListAdd.Create(AOwner:TComponent); inizio ereditato Create(AOwner); FButton:=TButton.Create(Self); FButton.Parent:=Se stesso; TastoF.Sinistra:=5; TastoF.In alto:=5; Larghezza FButton:=40; Altezza.FButton:=25; FEdit:=TEdit.Create(Self); FEdit.Parent:=Se stesso; FModifica.Sinistra:=50; FEdit.Top:=5; FModifica.Larghezza:=45; FModifica.Altezza:=25; FListBox:=TListBox.Create(Self); FListBox.Parent:=Se stesso; FListBox.Left:=5; FListBox.Top:=35; FListBox.Width:=90; FListBox.Altezza:=60; FINE;

Va sottolineato ancora una volta che il distruttore in questo caso non ha bisogno di essere riscritto: il pannello è il proprietario di tutti i controlli e quando viene chiamato il suo distruttore, i distruttori dei controlli verranno chiamati automaticamente.

Dopo aver ricompilato il componente utilizzando l'editor dei pacchetti, le modifiche apportate al componente possono già essere viste visivamente, nella fase di sviluppo.

Il primo inconveniente che salta all'occhio è il comportamento inadeguato dei controlli durante il ridimensionamento del componente. Quando si modificano le sue dimensioni, le dimensioni e la posizione degli elementi non cambiano. Inoltre, il componente può essere ridotto in modo che non possa contenere tre controlli. Infine, quando si installa un componente su un modulo dalla tavolozza dei componenti con un semplice clic del mouse, anche le sue dimensioni lasciano molto a desiderare.

Per prima cosa correggiamo le dimensioni predefinite dei componenti, cioè quelle che gli vengono assegnate automaticamente quando si fa clic sulla tavolozza dei componenti e quindi si fa clic sul modulo. Per fare ciò, devi semplicemente specificare le nuove dimensioni del pannello nel costruttore:

Larghezza:=100; Altezza:=100;

Quindi è necessario migliorare il comportamento del componente durante il ridimensionamento. Per fare ciò, è necessario ricevere un messaggio che le dimensioni sono cambiate. Quando la dimensione di un controllo cambia, il sistema gli invia un messaggio WM_SIZE. Questo messaggio deve essere intercettato. Per fare ciò, nella sezione privata descriviamo l'intestazione del messaggio interceptor:

Procedura WMSize(var Messaggio:Tmessage); messaggio WM_SIZE;

e nella sezione di implementazione implementiamo il suo gestore:

Procedura TListAdd.WMSize(var Message:TMessage); iniziare ereditato; se Larghezza<100 then Width:=100; if Height<100 then Height:=100; FEdit.Width:=Width-55; FListBox.Width:=Width-10; FListBox.Height:=Height-40; end;

La prima istruzione è una chiamata al gestore WM_SIZE predefinito (ereditato). Dopo averlo chiamato, le proprietà Larghezza e Altezza conterranno la nuova larghezza e altezza del pannello. Successivamente vengono determinate le dimensioni minime del componente, in questo caso 100x100. Se la dimensione orizzontale o verticale è inferiore al minimo, gli viene assegnato il valore minimo. I controlli vengono quindi ridimensionati in modo da riempire l'intero pannello con una certa rientranza. Compilando il componente tramite l'editor dei pacchetti, è possibile notare già in fase di sviluppo che i controlli sul pannello si comportano correttamente quando ridimensionati e anche che la dimensione del componente non può essere inferiore a 100x100.

Ora sarà utile eseguire l'intero progetto, provare a inserire i dati in un editor di testo di una riga e fare clic sul pulsante. In questo caso non viene aggiunto nulla all'elenco. E non a caso, da nessuna parte nel nostro componente è specificato cosa si dovrebbe fare quando si preme il pulsante. Per creare un gestore eventi associato al clic di un pulsante, puoi procedere come quando scrivi il componente TbuttonBeep, ovvero definire una nuova classe, un discendente di TButton e riscrivere il metodo Click. Tuttavia, la definizione di una nuova classe richiede risorse di sistema (i metodi virtuali vengono moltiplicati). Se contrassegniamo il componente sul modulo e osserviamo l'ispettore dell'oggetto, scopriremo che il componente TlistAdd espone poche proprietà e nessun evento, incluso nessun gestore di eventi per il pulsante OnClick. Pertanto, ciò che abbiamo rifiutato nell'ultimo capitolo come metodo errato, ridefinendo il gestore del pulsante OnClick, è applicabile in questo caso, poiché il programmatore non può assegnare un nuovo gestore nell'ispettore dell'oggetto. Quindi, nella sezione privata descriviamo l'intestazione del nuovo metodo:

Procedura BtClick(Sender:TObject);

Nell'implementazione del costruttore TListAdd, assegniamo questo gestore al gestore di eventi FButton.OnClick:

FButton.OnClick:=BtClick;

Infine, implementiamo il metodo BtClick:

Procedura TListAdd.BtClick(Sender:TObject); inizia se length(FEdit.Text)>0 quindi inizia FListBox.Items.Add(FEdit.Text); FEdit.Text:=""; FEdit.SetFocus; FINE; FINE;

Per prima cosa controlliamo se l'editor a riga singola è vuoto: non aggiungeremo righe vuote all'elenco. Quindi trasferiamo il contenuto dell'editor nell'elenco (FListBox.Items.Add(FEdit.Text);) e prepariamo l'editor per l'immissione del valore successivo, ovvero lo cancelliamo dal testo (che è già stato trasferito nell'elenco ) e trasferisci su di esso il focus di input. Ora, dopo aver compilato ed eseguito l'applicazione, puoi assicurarti che funzioni correttamente: quando premi il pulsante, i contenuti dell'editor vengono trasferiti nell'elenco.

Aggiunta di proprietà e metodi

Se posizioni il componente TPanel accanto al componente TListAdd e confronti ciò che viene mostrato nell'ispezione oggetto, noterai che un numero abbastanza elevato di proprietà ed eventi sono esposti per il pannello, mentre solo poche proprietà sono esposte per TListAdd. Nel frattempo, la classe TCustomPanel è l'antenata di entrambi i componenti. Per capirne il motivo, apriamo il modulo ExtCtrls.pas e osserviamo la differenza tra le classi TCustomPanel e TPanel. Si può notare che tutti i metodi e le variabili che forniscono le funzionalità del pannello sono definiti a livello di classe TCustomPanel. Definisce inoltre le proprietà che vengono poi visualizzate nell'ispettore oggetto per TPanel, solo queste proprietà sono definite nella sezione Protetto. L'implementazione della classe TPanel è estremamente semplice: TCustomPanel è definito come antenato e le proprietà di questa classe sono dichiarate, ma nella sezione pubblicata. Diventa chiaro cosa è necessario fare nella classe TListAdd affinché le proprietà e i metodi della classe TcustomPanel appaiano nell'ispettore dell'oggetto, vale a dire dichiarare le proprietà. Nella sezione pubblicata della classe TListAdd scriviamo:

Allineamento proprietà; proprietà OnMouseDown;

Quando si dichiara una proprietà, non è necessario specificarne il tipo o le variabili di riferimento o i metodi per leggere o scrivere la proprietà. Dopo aver compilato il componente tramite l'editor del pacchetto nell'ispezione oggetti, puoi osservare l'aspetto della proprietà Align e dell'evento OnMouseDown. Pertanto, per i discendenti delle classi TCustom..., il programmatore ha l'opportunità di scegliere quali proprietà ed eventi devono essere visualizzati nell'ispettore oggetto e quali no. È per questo motivo che si consiglia di utilizzare le classi TCustom... come antenati per la creazione di componenti.

Ora diamo un'occhiata a come introdurre una nuova proprietà (quello che abbiamo fatto sopra è la nuova dichiarazione delle proprietà esistenti). Il testo sul pulsante può essere utilizzato come proprietà adatta da visualizzare nell'ispettore dell'oggetto: lasciare che il programmatore che utilizza il componente TListAdd modifichi il testo in fase di progettazione. Cercando di introdurre una nuova proprietà (chiamiamola BtCaption) usando una dichiarazione:

Proprietà BtCaption:string leggi FButton.Caption scrivi FButton.Caption;

genera un errore durante il tentativo di compilare il componente. Pertanto, definiamo le intestazioni di due metodi nella sezione privata:

Funzione GetBtCaption:string; procedura SetBtCaption(const Valore:stringa);

Nella sezione pubblicata dichiariamo la proprietà BtCaption:

Proprietà BtCaption:stringa lettura GetBtCaption scrittura SetBtCaption;

Infine, implementiamo i due metodi dichiarati nella sezione implementazione:

Funzione TListAdd.GetBtCaption:string; inizio Risultato:=FButton.Caption; FINE; procedura TListAdd.SetBtCaption(const Value:string); inizia FButton.Caption:=Valore; FINE;

Dopo aver compilato un componente utilizzando l'editor del pacchetto, viene visualizzata una nuova proprietà nell'Ispettore oggetto. La modifica del valore di questa proprietà si riflette direttamente nella fase di sviluppo.

Ora definiamo un nuovo evento. In questo caso, sarebbe ragionevole creare un evento che consenta al programmatore che utilizza questo componente di analizzare il testo prima di aggiungere il contenuto dell'editor all'elenco e consentire o disabilitare l'aggiunta di testo all'elenco. Pertanto questo metodo deve contenere come parametro il valore corrente del testo nell'editor e dipendere da una variabile booleana, alla quale il programmatore può assegnare il valore True o False. Inoltre, qualsiasi gestore eventi in un componente deve dipendere dal parametro Sender, in cui il componente che lo chiama passa un riferimento a se stesso. Ciò è necessario perché nell'ambiente di sviluppo Delphi lo stesso gestore di eventi può essere chiamato da diversi componenti e il programmatore deve essere in grado di analizzare quale componente ha chiamato il gestore. Quindi, dopo la parola tipo nella sezione interfaccia, prima di definire TListAdd, definiamo un nuovo tipo di metodo:

Digitare TFilterEvent=procedure(Sender:TObject; const EditText:string; var CanAdd:boolean) di object;

FOnFilter:evento TFilter;

E nella sezione pubblicata definiamo una proprietà di questo tipo:

Proprietà OnFilter:TFilterEvent leggi FOnFilter scrivi FOnFilter;

Quando definiamo una nuova proprietà, facciamo riferimento alla variabile FOnFilter e non ai metodi: qui non sono richiesti. Ora, se compili il componente utilizzando l'editor del pacchetto, puoi vedere l'evento OnFilter apparire nella finestra di ispezione oggetto. Tuttavia, se gli assegniamo un gestore ed eseguiamo il progetto per l'esecuzione, potrebbe non essere chiamato. Questo perché non l'abbiamo chiamato da nessuna parte nel nostro componente. Un buon posto per chiamare l'evento OnFilter è nel gestore eventi OnClick per FButton, che è già implementato. Modificheremo quindi il codice di implementazione del metodo BtClick precedentemente definito:

Procedura TListAdd.BtClick(Sender:TObject); var CanAdd:boolean; inizia se length(FEdit.Text)>0 allora inizia CanAdd:=True; se Assegnato(FOnFilter) allora FOnFilter(Self,FEdit.Text,CanAdd); se CanAdd allora inizia FListBox.Items.Add(FEdit.Text); FEdit.Text:=""; FEdit.SetFocus; fine altrimenti bip; FINE; FINE;

Pertanto, nel frammento di codice sopra, è definita la variabile booleana CanAdd. Quando si scrive il codice, tenere presente che il programmatore potrebbe non creare un gestore eventi OnFilter. Pertanto, impostiamo il valore predefinito della variabile CanAdd su True: aggiungi tutte le righe all'elenco. Successivamente, prima di chiamare FonFilter, dovresti verificare se il programmatore ha creato un gestore di eventi. Ciò si ottiene chiamando il metodo Assigned, che restituisce un valore booleano. Per un puntatore, chiamare il metodo Assigned equivale a controllare P<>zero. Per un metodo oggetto non possiamo utilizzare il controllo FOnFilter<>nil, poiché il metodo dell'oggetto è caratterizzato da due indirizzi e tale controllo non sarà consentito dal compilatore. Ma chiamando il metodo Assigned si controlla perfettamente se è stato creato un gestore eventi. Il codice sopra è assolutamente modo standard chiamando un gestore eventi da un componente.

Tutto ciò che resta è testare il gestore di eventi. Inseriamo due componenti TListAdd nel modulo, per uno consentiremo di aggiungere solo numeri interi e per l'altro solo parole che iniziano con lettere inglesi maiuscole. Di conseguenza, il codice per i gestori di eventi OnFilter sarà simile al seguente:

Procedura TForm1.ListAdd1Filter(Sender: TObject; const EditText: String; var CanAdd: Boolean); var I,N:intero; inizio Val(ModificaTesto,N,I); CanAdd:=I=0; FINE; procedura TForm1.ListAdd2Filter(Sender: TObject; const EditText: String; var CanAdd: Boolean); inizio CanAdd:=False; se length(EditText)>0 allora CanAdd:=(EditText>="A") e (EditText<="Z"); end;

Il codice è facile da capire, l'unico avvertimento è che controlla che il testo non sia una stringa vuota prima di controllare la prima lettera del testo nel gestore dell'evento ListAdd2Filter. Effettuare tale verifica è obbligatorio: le stringhe in Object Pascal sono oggetti, e una stringa vuota corrisponde a un puntatore nil. Se provi a controllare la prima lettera di una stringa vuota, l'applicazione tenterà di dereferenziare nil, generando un'eccezione. In questo caso, questo non è un problema: prima di chiamare il gestore di eventi FOnFilter dal componente TListAdd, viene verificata la lunghezza diversa da zero della stringa. Tuttavia, per i componenti il ​​cui codice sorgente non è disponibile per te, tale verifica è obbligatoria!

Nascondere le proprietà nell'Ispettore oggetto

Supponiamo che tu stia creando un componente di accesso ai dati, ad esempio, un discendente della classe TTable. Diciamo che questo componente analizza l'elenco delle tabelle disponibili nel database e in base ad alcuni criteri (ad esempio la presenza di un campo di un certo tipo e con un certo nome), ne viene selezionato uno per il lavoro. Per il normale funzionamento del componente è necessario inserire il nome di questa tabella nella proprietà TableName. Ma questa proprietà è visibile nell'ispettore oggetto! Un programmatore che utilizza questo componente potrebbe modificarne il valore durante lo sviluppo, il che renderebbe presumibilmente il componente inutilizzabile. E avrà ragione! Se una qualsiasi delle proprietà o degli eventi non può essere modificata, deve essere nascosta.

Continueremo a lavorare sul componente TListAdd e rimuoveremo la proprietà Cursor dall'ispettore oggetto come attività del modello. Questa proprietà è definita nella sezione pubblicata della classe TControl e viene visualizzata nell'ispettore dell'oggetto per TListAdd fin dall'inizio dello sviluppo del componente. Sulla base di ciò, puoi provare a sovrascrivere questa proprietà nella sezione protetta. Il compilatore consentirà tale override, ma non porterà al risultato desiderato: la proprietà Cursor rimarrà nell'ispettore dell'oggetto com'era... Qualsiasi proprietà, una volta definita nella sezione pubblicata, sarà sempre visualizzata nell'oggetto ispettore per tutti i discendenti di questa classe.

Per nascondere una proprietà dall'ispettore oggetto, utilizziamo due funzionalità del compilatore Delphi, vale a dire:

  1. Quando si dichiara una nuova proprietà con lo stesso nome di una proprietà esistente, la proprietà precedentemente definita viene “ombreggiata”.
  2. Le proprietà che hanno accesso di sola lettura o di sola scrittura non vengono visualizzate nell'ispezione oggetto, anche se sono dichiarate nella sezione pubblicata.

Prima di iniziare a lavorare per nascondere la proprietà Cursor, è una buona idea rimuovere i componenti TListAdd dal modulo, altrimenti potrebbe verificarsi un'eccezione durante la lettura della risorsa del modulo. Quindi, nella sezione privata dichiariamo la variabile FDummy:integer (il nome e il tipo della variabile può essere qualsiasi cosa) e nella sezione pubblicata definiamo una nuova proprietà:

Proprietà Cursore:intero leggi FDummy;

La nuova proprietà deve chiamarsi Cursore, il suo tipo deve corrispondere al tipo della variabile definita sopra, la proprietà deve essere di sola lettura o di sola scrittura. Dopo aver compilato il componente utilizzando l'editor del pacchetto, dovresti inserire nuovamente il componente TListAdd nel modulo. Potresti scoprire che la proprietà Cursor non è più visibile nella finestra di ispezione oggetto.

Ora complichiamo un po' il compito. Supponiamo di voler visualizzare il cursore non come una freccia, ma come una clessidra (crHourGlass). Per modificare il valore predefinito delle proprietà, il nuovo valore deve essere assegnato a una variabile nel costruttore. Quando si tenta di assegnare un nuovo valore a Cursor nel costruttore

Cursore:=crHourGlass;

Il compilatore Delphi emetterà un messaggio diagnostico indicando che un nuovo valore non può essere assegnato a una variabile di sola lettura. Se crei una nuova proprietà di "sola scrittura", il compilatore emetterà un messaggio diagnostico diverso, relativo ai tipi di dati incompatibili. Se dichiari la variabile FDummy:TCursor e la rendi di sola scrittura, il compilatore consentirà questa assegnazione, ma l'aspetto del cursore non cambierà: sarà ancora una freccia.

Una soluzione banale a questo problema è dichiarare una classe discendente di TCustomPanel, nel cui costruttore è necessario assegnare un nuovo valore alla variabile Cursor, e da essa produrre il nostro componente TListAdd. Questa soluzione presenta due svantaggi:

  1. È ad alta intensità di risorse: i metodi virtuali si moltiplicano.
  2. Abbiamo nascosto la proprietà nell'ispettore oggetto al programmatore che utilizzerà questo componente. Vogliamo lavorare con questa proprietà.

Pertanto, la soluzione a questo problema è simile alla seguente: nel costruttore TListAdd dichiariamo l'operatore:

Cursore ereditato:=crHourGlass;

e basta! Questo è sufficiente per cambiare il cursore.

In precedenza, utilizzavamo la parola funzione ereditata solo per chiamare un metodo antenato. Questa costruzione permette di comprendere meglio il significato di ereditato come riferimento ad una classe antenata. È possibile accedere sia alle proprietà che ai metodi. Quando si accede ad una proprietà è possibile leggerla oppure assegnarle un nuovo valore; in questo caso la parola ausiliare ereditato appare a sinistra del segno di assegnazione. Allo stesso modo, puoi chiamare metodi antenati nascosti. Sono vietate le chiamate gerarchiche superiori alla classe antenata: costruzione

Cursore ereditato ereditato:=crHourGlass;

non verrà compilato.

A questo punto considereremo questo progetto completato. Nel nuovo componente abbiamo intercettato il messaggio, dichiarato le proprietà, aggiunto nuove proprietà ed eventi e nascosto la proprietà precedentemente dichiarata. Tutti questi metodi vengono utilizzati per creare componenti. Di seguito vedremo un altro metodo interessante.

Utilizzo delle procedure Hook per creare componenti

È stato menzionato in precedenza che ogni figlio TWinControl ha una procedura che riceve ed elabora i messaggi. Se è presente un riferimento all'handle della finestra (HWND), è possibile determinare l'indirizzo di questa procedura e, soprattutto, sovrascrivere questo indirizzo ed elaborare così i messaggi ricevuti a modo proprio. Di norma, nessuno scrive gestori completi per tutti i messaggi; il vecchio metodo predefinito viene chiamato più spesso. In questo caso la nuova procedura viene utilizzata come filtro: quando arriva un evento il codice viene eseguito. In realtà, questa è una “spia” in TwinControl: veniamo avvisati quando arriva un messaggio e qualche codice può essere eseguito. Se la procedura Hook è implementata correttamente, TWinControl continua a funzionare normalmente, senza rendersi conto di condividere i propri messaggi con qualcun altro.

La procedura di hook è definita come segue:

Procedura(var Message:TMessage) dell'oggetto;

Dipende da una variabile di tipo TMessage, che contiene tutte le informazioni sul messaggio. Ma definire questa procedura non è sufficiente. Deve essere copiato per ogni TWinControl a cui verrà allegato. Ciò si ottiene chiamando il metodo WinAPI MakeObjectInstance. Questo metodo accetta un metodo oggetto come parametro, ne fa una copia in memoria e restituisce l'indirizzo del nuovo metodo. È chiaro che questo riserva risorse di sistema che devono essere restituite al sistema. Ciò si ottiene chiamando il metodo FreeObjectInstance.

Altra condizione importante: prima di distruggere TWinControl è necessario ripristinare la comunicazione con la vecchia procedura di elaborazione dei messaggi, altrimenti le risorse non verranno restituite al sistema. Ciò significa che dovrai ricordare il puntatore alla vecchia procedura, che puoi trovare chiamando il metodo GetWindowLong della Win API con il parametro GWL_WNDPROC. Questo puntatore verrà utilizzato anche per chiamare i gestori eventi predefiniti di TWinControl. Il metodo inverso, SetWindowLong, viene utilizzato per impostare una procedura Hook.

Formuliamo quindi il problema per il prossimo esercizio. Diciamo che vogliamo creare un componente che farà suonare gli altri componenti, discendenti di TWinControl, quando viene premuto il pulsante del mouse. È chiaro che questo componente non dovrebbe essere mostrato durante l'esecuzione dell'applicazione, quindi selezioneremo TComponent come classe antenata. Definiamo il nome della classe come TBeepWnd. Nella sezione privata definiamo tre variabili:

FOldProc,FNewProc:puntatore; Controllo F:TWinControl;

Dai nomi è chiaro che ricorderemo il collegamento alla vecchia procedura nella variabile FOldProc, il collegamento alla nuova procedura (dopo aver eseguito il metodo MakeObjectInstance) verrà memorizzato nella variabile FNewProc. E nella variabile FControl memorizzeremo un collegamento all'elemento di controllo su cui è attualmente “appesa” la procedura Hook. Definiamo tre metodi nella stessa sezione:

Procedura HookProc(var Messaggio:TMessage); procedura HookWindow(W:TWinControl); procedura UnhookWindow;

e nella sezione implementazione li implementiamo:

Procedura TBeepWnd.HookProc(var Messaggio:TMessage); inizio caso Messaggio.Msg di WM_LBUTTONDOWN:inizio (Il nostro compito) Beep; Messaggio.Risultato:=CallWindowProc(FOldProc, FControl.Handle, Messaggio.Msg, Messaggio.WParam, Messaggio.lParam); FINE; WM_DESTROY:begin (Quando la finestra sta per essere distrutta, rimuovi il hook) Message.Result:=CallWindowProc(FOldProc, FControl.Handle, Message.Msg, Message.WParam, Message.lParam); Sgancia Finestra; FINE; (Gestore predefinito della chiamata) else Message.Result:=CallWindowProc(FOldProc, FControl.Handle, Message.Msg, Message.WParam, Message.lParam); FINE; FINE;

Nella procedura Hook stessa, viene intercettato un messaggio al quale si verifica una reazione: WM_LBUTTONDOWN. Inoltre, qualsiasi procedura Hook deve gestire il messaggio WM_DESTROY. Questo è l'ultimo messaggio inviato alla finestra prima che venga distrutta. La nostra risposta è ripristinare il metodo precedente chiamando il metodo UnhookWindow descritto di seguito. Infine, i gestori di messaggi predefiniti vengono chiamati utilizzando il metodo CallWindowProc. Dimenticare il gestore eventi predefinito equivale a dimenticare quello ereditato nel gestore eventi; nell'80% dei casi ciò porterà a un comportamento non corretto dell'applicazione. In nessun caso dovresti dimenticare di assegnare il risultato della chiamata del metodo CallWindowProc al campo Risultato della variabile Messaggio! Il codice non funzionerà in questo caso!

Procedura TBeepWnd.HookWindow(W:TWinControl); iniziare se csDesigning in ComponentState quindi iniziare (controllare se il componente in fase di progettazione o in fase di esecuzione) FControl:=W; Uscita; FINE; se FControl<>nil quindi UnhookWindow; (Rimuovere il gancio se era installato in precedenza) se W<>nil quindi iniziare FOldProc:=pointer(GetWindowLong(W.Handle,GWL_WNDPROC)); (Determina l'indirizzo della vecchia procedura) FNewProc:=MakeObjectInstance(HookProc); (Crea una copia in memoria) SetWindowLong(W.Handle,GWL_WNDPROC,integer(FNewProc)); (Imposta nuova procedura) fine; Controllo F:=W; (Memorizza riferimento al controllo) fine;

Questo metodo viene utilizzato per impostare una nuova routine di gestione dei messaggi. Innanzitutto controlla in quale fase si trova il componente: in fase di sviluppo o in fase di esecuzione. Se il componente è in fase di sviluppo, cioè nella proprietà ComponentState è impostato il flag csDesigning, viene semplicemente salvato un collegamento al componente senza installare una procedura Hook. Questo viene fatto per evitare di installare una procedura Hook sull'ambiente di sviluppo Delphi. Se questa procedura era precedentemente impostata su un altro controllo, viene rimossa chiamando il metodo UnhookWindow. Successivamente viene ricordato l'indirizzo della vecchia procedura (GetWindowLong), viene fatta una copia in memoria della nuova procedura (MakeObjectInstance) e viene impostato l'indirizzo della nuova procedura (SetWindowLong). Viene utilizzato il cast del tipo da intero a puntatore e viceversa: i metodi chiamati richiedono (o restituiscono) variabili di tipi non del tutto adatti. Infine, il riferimento al controllo viene memorizzato nella variabile FControl, che abbiamo definito nella sezione private.

Procedura TBeepWnd.UnhookWindow; iniziare se (FControl=nil) o (FOldProc=nil) o (FNewProc=nil) quindi Esci; (Non è stato installato alcun hook) SetWindowLong(FControl.Handle,GWL_WNDPROC,integer(FOldProc)); (Imposta la vecchia procedura della finestra) FreeObjectInstance(FNewProc); (Risorse gratuite) FControl:=nil; (Variabili di avvio) FOldProc:=nil; FNewProc:=zero; FINE;

Questo metodo ripristina il vecchio gestore eventi. Viene chiamato dal metodo HookProc e deve essere chiamato anche dal distruttore del componente: l'Hook deve essere rimosso sia quando viene distrutta la finestra sia quando viene distrutto questo componente. Il metodo SetWindowLong con l'indirizzo del vecchio metodo ripristina il vecchio gestore dei messaggi. Successivamente, dovresti restituire le risorse al sistema chiamando il metodo FreeObjectInstance.

Sono quindi state definite le modalità di base per lavorare con la procedura Hook. Ora devi riscrivere il distruttore in modo che la procedura Hook venga rimossa quando questo componente viene distrutto:

Distruttore TBeepWnd.Destroy; inizia UnhookWindow; ereditato Distruggere; FINE;

E infine, nella sezione pubblicata definiamo una proprietà che verrà visualizzata nell'ispezione oggetto:

proprietà Control:TWinControl leggi FControl scrivi HookWindow;

Per installare un nuovo componente si fa riferimento ad un metodo precedentemente definito, che, mentre l'applicazione è in esecuzione, “bloccherà” immediatamente una procedura di Hook sul componente, che emetterà un segnale acustico alla pressione del pulsante. Ricordiamo che al posto dell'operatore Beep è possibile scrivere qualsiasi codice eseguibile.

Il componente viene testato in modo molto semplice: viene posizionato su un modulo in cui sono posizionati diversi componenti discendenti di TWinControl. Dopo aver selezionato il componente TBeepWnd in background, facendo clic con il mouse nel campo Controllo nell'ispettore oggetto si espande un elenco contenente tutti i TWinControl definiti nel modulo. Dovresti selezionarne uno e avviare l'applicazione. Quando si fa clic con il pulsante sinistro del mouse sul componente selezionato, viene emesso un cigolio.

Editor di proprietà ed editor di componenti

Tutto quanto trattato nelle sezioni precedenti riguarda la creazione del codice dell'applicazione che verrà distribuito agli utenti. Tuttavia, l'ambiente di sviluppo Delphi consente di modificarsi. Ciò non richiede la conoscenza di un linguaggio speciale, poiché tutti i metodi per modificare l'ambiente di sviluppo sono scritti in Delphi. Qui questi metodi, vale a dire gli editor di proprietà e gli editor di componenti, sono considerati parzialmente, in termini di creazione di strumenti per lavorare con i componenti. Leggendo i materiali in questa sezione, dovresti capire chiaramente che l'utente finale che lavora con la tua applicazione non vedrà mai né l'editor delle proprietà né l'editor dei componenti: sono creati per i programmatori e funzionano solo nell'ambiente di sviluppo Delphi.

Redattori di proprietà

Durante lo sviluppo dell'applicazione, le proprietà vengono visualizzate nell'Ispettore oggetto. Tieni presente che le proprietà vengono modificate in modo diverso nell'Ispettore oggetto. Ad alcune proprietà (Larghezza, Didascalia) può essere assegnato solo un nuovo valore di testo. Una proprietà di tipo Cursore fornisce un elenco a discesa su cui è possibile fare clic per selezionare un valore. Una proprietà di tipo TFont ha un segno "+" a sinistra; Quando fai clic su di esso, si espande consentendoti di modificare i singoli campi. Inoltre, sulla destra c'è un pulsante con tre punti (pulsante ellittico), quando si fa clic viene visualizzata la finestra di dialogo dell'editor delle proprietà.

Ognuna delle proprietà di cui sopra ha il proprio editor e un grande vantaggio dell'ambiente di sviluppo Delphi è la possibilità di creare i propri editor di proprietà. I nuovi editor di proprietà sono abbastanza comuni tra i componenti distribuiti. Ma vanno trattati con cautela: eseguire inizialmente i test su un computer dove è possibile reinstallare Delphi se necessario. Di norma, vengono creati da programmatori qualificati e non ci sono lamentele sul codice, ma spesso dimenticano di includere qualsiasi DLL nell'editor delle proprietà distribuite. Dopo aver installato un editor di questo tipo, otteniamo una serie di proprietà che non possono essere modificate: il vecchio editor è bloccato e quello nuovo non funziona...

Prima di creare un nuovo editor di proprietà, è opportuno pensare se valga la pena farlo: probabilmente ne troverai uno adatto tra gli editor standard. Se devi creare un editor di proprietà, devi seguire la regola: dovresti evitare di creare editor per tipi di dati standard (intero, stringa, ecc.). Altri programmatori sono abituati agli editor standard e potrebbero non apprezzare i tuoi. Pertanto, dovrai essere modesto e registrare l'editor per la tua classe e non per la classe TComponent. Se ai programmatori piace il tuo editor di proprietà, la maggior parte di loro sarà in grado di modificare da soli la registrazione in modo che l'editor funzioni per tutti i componenti. Discuteremo la questione della registrazione di un editore di seguito.

Poniamo quindi un problema modello, per la cui implementazione sarà necessario implementare un editor di proprietà. Supponiamo che qualche componente abbia la proprietà: giorno della settimana. In linea di principio, per inserire il giorno della settimana è possibile utilizzare un editor standard con un elenco a discesa. Vogliamo però che il programmatore in fase di sviluppo possa inserire il giorno della settimana, specificandone il numero di serie (1 - Lunedì, 2 - Martedì, ecc.), oppure un testo in lingua nazionale o inglese. Quando si inserisce il testo, è consentito mescolare lettere maiuscole e minuscole.

Prima di tutto, devi creare un componente che memorizzerà il giorno della settimana. Creiamo un nuovo componente richiamando il comando Componente/Nuovo componente. Selezioniamo TComponent come classe antenata e diamo alla nuova classe il nome TDayStore. Successivamente, installa il componente nella tavolozza. Ora dobbiamo decidere in quale forma memorizzare il giorno della settimana. È chiaro che per un'identificazione inequivocabile e per risparmiare risorse dovrebbe essere memorizzato come numero intero con intervalli validi da 1 a 7. Tuttavia, se creeremo un editor di proprietà, dovremmo ricordare la regola di non creare nuovi editor per i tipi esistenti. Pertanto, definiremo un nuovo tipo: TDayWeek e tutte le operazioni con esso verranno eseguite come con i numeri interi. Definiamo la variabile FDay nella sezione privata del componente. Poiché questa variabile verrà inizializzata su 0 quando viene eseguito il costruttore predefinito e questo numero non rientra nei valori consentiti, il costruttore deve essere riscritto. Infine, definiamo la proprietà DayWeek nella sezione pubblicata per visualizzarla nell'ispezione oggetto. Il componente finale è simile a questo:

Digitare TDayWeek=tipo intero; TDayStore = class(TComponent) private ( Dichiarazioni private ) FDay:TDayWeek; protetto (Dichiarazioni protette) pubblico (Dichiarazioni pubbliche) costruttore Create(AOwner:TComponent); oltrepassare; Published ( Dichiarazioni pubblicate ) proprietà DayWeek:TDayWeek read FDay write FDay; FINE; ... costruttore di implementazione TDayStore.Create(AOwner:TComponent); inizio ereditato Create(Aowner); FGiorno:=1; FINE;

Vale la pena prestare attenzione alla rara costruzione della definizione di un nuovo tipo

TDayWeek=tipo intero;

Viene quindi introdotto un nuovo tipo di dati, che ha la stessa dimensione del tipo intero; tutte le operazioni su questo tipo di dati vengono eseguite come con gli interi. Lo scopo di questa operazione è dichiarare un nuovo tipo di dati in modo che il nostro editor di proprietà possa essere applicato specificatamente ad esso e non influenzi altri tipi di dati.

Ora creiamo un editor per la proprietà TDayWeek. Per fare ciò, aggiungi un nuovo modulo al progetto esistente, ricordalo con un nome adatto (DayPropE.pas) ed escludilo dal progetto. Successivamente, apriremo il modulo come file separato e implementeremo al suo interno l'editor delle proprietà. Nella prima fase non avremo bisogno del modulo, ma in seguito implementeremo un dialogo su di esso.

Il modulo per la creazione degli editor di proprietà si chiama DsgnIntf.pas (Design Interface), definisce la classe base TPropertyEditor e le classi discendenti destinate alla modifica delle proprietà standard - TIntegerProperty, TFloatProperty, TStringProperty, ecc. Il meccanismo di funzionamento degli editor di proprietà è il seguente:

  1. Viene registrato nell'ambiente di sviluppo Delphi chiamando il metodo RegisterPropertyEditor. Questo metodo accetta come parametri i seguenti valori:

    a) informazioni sul tipo di proprietà per la modifica a cui è destinato questo editor. A causa di queste informazioni, abbiamo dovuto definire un nuovo tipo TDayWeek;

    b) informazioni sul componente in cui è applicabile questo editor. L'editor verrà chiamato non solo per il componente specificato, ma anche per tutti i suoi discendenti. Se lo imposti su TComponent, l'editor verrà richiamato per qualsiasi componente;

    c) il nome dell'immobile per il quale viene utilizzato questo editor. Se nome è una stringa vuota, vengono utilizzati i due filtri precedenti;

  2. Il metodo GetValue viene chiamato quando è necessario leggere il valore corrente di una proprietà dal componente. Per qualsiasi proprietà, questo metodo restituisce una stringa inserita nell'ispezione oggetto.
  3. Il metodo SetValue viene chiamato quando il programmatore ha inserito un nuovo valore di proprietà nell'Ispettore oggetto. Una nuova stringa viene passata come parametro. Nel metodo, deve essere analizzato e convertito nel tipo di immobile da modificare.

I metodi GetValue e SetValue sono virtuali; quando vengono riscritti, vengono creati nuovi editor di proprietà. Quindi ora possiamo iniziare a creare un nuovo editor di proprietà.

Facciamo riferimento al modulo DsgnIntf ​​nella sezione uses del modulo DayPropE.pas e definiamo una nuova classe nella sezione Interface:

Digitare TDWPropED=class(TPropertyEditor) funzione pubblica GetValue:string; oltrepassare; procedura SetValue(const Valore:stringa); oltrepassare; FINE;

La sezione di implementazione dovrebbe implementare entrambi questi metodi. In questo caso, abbiamo inoltre bisogno di un elenco dei nomi dei giorni della settimana - nella formulazione iniziale del problema è richiesto che il programmatore possa inserire il giorno della settimana:

Const DayWeek:array di string = ("Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato", "Domenica"); DayWeekEn:array of string = ("Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato", "Domenica");

Pubblicazioni sull'argomento