' Rapid-Q by William Yu (c)1999-2000 . ' ================================================================================ ' Upload_il_tuo_script_su_Rapidq.it ' Capitolo_10__Creazione_di_componenti_personalizzati ****** 10. Creazione di componenti personalizzati ****** La creazione di componenti personalizzati può essere di aiuto se si desidera che i propri form abbiano un aspetto particolare (e per altre cose). Per esempio, invece delle checkbox standard di Windows, potreste preferire creare checkbox a forma di diamante (vedi fine del capitolo). Dopo aver letto questo capitolo, sarete in grado di creare i vostri componenti totalmente riutilizzabili, perfino form personalizzati! Cominciamo da un semplice esempio di una checkbox a diamante. [http://www.rapidq.it/public/hell_editor/homepages/images/chap10d1.gif] [http://www.cajino.50megs.com/rapidq/chap10d2.gif] 10.1 Introduzione alla creazione di oggetti Nel creare oggetti personalizzati non reinventerete la ruota. Per creare i vostri oggetti usate il comando EXTEND su un componente (come QCanvas). Ciò significa che tutte le sue proprietà , i suoi metodi ed eventi saranno ereditati. Successivamente, potrete scegliere di ignorare le sue proprietà e/o metodi, o crearne di nuovi. Il concetto è veramente facile da capire, una volta presa un po' di confidenza. Il metodo di creazione dei componenti di Rapid-Q non ricorda alcun altro linguaggio, ma non è nemmeno troppo strano. Dal momento che la creazione di oggetti non è materia per principianti, vi consiglio di familiarizzare con i concetti di proprietà , metodi ed eventi. Avrete anche bisogno di una buona comprensione dei Tipi, dal momento che eventi e metodi compresi i tipi sono concetti nuovi. Non confondetevi con altri linguaggi OOP, Rapid-Q non adotta membri sovraccaricanti, protetti o virtuali, ma deriva diverse idee da altri linguaggi OOP come Delphi, C++ e Java. 10.2 Estendere QCanvas QCanvas è un utile componente 'foglio bianco'. Permette di disegnarvi qualunque cosa desideriate, e ciò lo rende il componente più adatto per essere esteso. Prima di tutto, come si fa esattamente ad estenderlo? TYPE QDiamondBox EXTENDS QCanvas END TYPE Wow, è stato facile. Il codice sopraindicato è corretto, per quanto non faccia niente di utile. L'unica cosa che ha fatto è stato creare un nuovo oggetto chiamato QDiamondBox che ha ereditato tutte le proprietà , metodi, ed eventi di QCanvas. Adesso possiamo dichiararlo con DIM ed assegnarvi dei valori: DIM DBox AS QDiamondBox DBox.Top = 10 Okay, questo non è molto utile. Qualche volta vorremo aggiungere delle proprietà ad un componente esistente, e nell'esempio precedente aveva probabilmente bisogno di una proprietà Caption (Titolo), e forse anche Checked (Selezionato), dal momento che stiamo implementando un componente di tipo checkbox. L'aggiunta di proprietà non presenta niente di nuovo: TYPE QDiamondBox EXTENDS QCanvas Caption AS STRING Checked AS INTEGER END TYPE Come vedete, sembra un qualsiasi UDT (tipo definito dall'utente), ben conosciuto dalla maggior parte dei programmatori Basic. A questo punto abbiamo aggiunto due proprietà addizionali al nostro nuovo componente, e possiamo accedervi come a qualunque altra proprietà . Bene, e se volete che i valori siano inizializzati quando viene creato? Non è un problema, non serve un costruttore come in C++, è sufficiente inserire questa informazione in questo modo: TYPE QDiamondBox EXTENDS QCanvas Caption AS STRING Checked AS INTEGER CONSTRUCTOR Caption = 'DiamondBox' Checked = 0 END CONSTRUCTOR END TYPE Dopo ogni DIM (cioè quando viene creato l'oggetto), vengono inizializzati i valori di default. Rapid-Q non richiede alcun DESTRUCTOR, in quanto tutto viene automaticamente ripulito alla fine del programma. Se non siete convinti che questo funzioni, provate: DIM DBox AS QDiamondBox ShowMessage(DBox.Caption) Se non restituisce 'DiamondBox' qualcosa non ha funzionato, controllate il codice e provate con $TYPECHECK ON. 10.3 Aggiungere/Superare Metodi In Rapid-Q, tutti i metodi, proprietà , ed eventi possono essere superati. Infatti, è possibile superare un metodo e trattarlo come se fosse una proprietà o un evento personalizzato. Una volta superato un metodo/proprietà non sarà più possibile accedere a quelli ereditati (dal momento che sono nascosti). Per accedere a questa proprietà o metodo, è necessario chiamare esplicitamente la classe SUPER come sotto indicato. Fate attenzione all'utilizzo di WITH Super ... END WITH, dal momento che tutte le proprietà e i metodi saranno riferiti alla classe superiore. Il concetto di superare le proprietà o i metodi è abbastanza semplice, invece di utilizzare le proprietà ereditate è possibile semplicemente ridefinirle. TYPE QDiamondBox EXTENDS QCanvas Left AS STRING Top AS BYTE SUB Pset PRINT Super.Left END SUB END TYPE In questo esempio stiamo superando due proprietà di QCanvas, ed un metodo. Come vedrete, QDiamondBox ha una proprietà denominata Left che supera la proprietà Left di QCanvas. Per utilizzare le proprietà Left della classe superiore, utilizziamo semplicemente la chiamata Super.Left anziché QDiamondBox.Left. Il superamento di metodi e proprietà può essere utile in molti casi. Considerate una listbox filtrata. È possibile superare il metodo AddItems e creando uno ad hoc filtrando determinate parole chiave che non volete che vengano incluse. È utile anche quando trasferite vostro codice Windows in Linux e viceversa. In questo modo, potete avere il vostro componente 'standard' per entrambe le versioni. Comunque, non approfondiremo ulteriormente questo argomento. Passiamo alla creazione di nuovi metodi per i nostri componenti: TYPE QDiamondBox EXTENDS QCanvas Caption AS STRING Checked AS INTEGER FUNCTION TextSize AS INTEGER Result = LEN(QDiamondBox.Caption) 'QDiamondBox.TextSize = LEN(QDiamondBox.Caption) END FUNCTION END TYPE Questo esempio è un buon punto di partenza. Abbiamo appena definito un nuovo metodo per il nostro componente QDiamondBox. Come avrete notato, dobbiamo comprendere le nostre funzioni o sub nella nostra dichiarazione di TYPE. Questo è chiamato codice INLINE, e dovrebbe essere familiare a chi ha utilizzato C++ o altro linguaggio OOP. Rapid- Q richiede il codice INLINE, non è possibile definire la vostra SUB e puoi avere la SUB stessa al di fuori della definizione del TYPE. Bene, l'unica cosa da notare è QDiamondBox.Caption; che cosa fa esattamente? Bene, vi starere probabilmente chiedendo perché non abbiamo semplicemente scritto Result = LEN(Caption) Bene, questo non funziona, in quanto nella nostra funzione avremmo potuto facilmente aggiungere DIM Caption AS INTEGER A questo punto Rapid-Q sarebbe piuttosto confuso...Quale devo utilizzare, la variabile Caption locale o la nuova proprietà ? Per semplificare le cose, quando volete utilizzare una proprietà o un metodo dovete anche farvi riferimento come fareste normalmente. Il nostro oggetto in questo caso è QDiamondBox (potete anche utilizzare la parola riservata this), e vogliamo fare riferimento alla proprietà Caption. Dal momento che il metodo sopraindicato è praticamente inutile nel nostro componente, lo ignoriamo semplicemente. Ciò di cui abbiamo veramente bisogno è un metodo per disegnare il nostro componente, in questo modo: SUB DrawComponent IF QDiamondBox.Checked THEN QDiamondBox.Line(QDiamondBox.Height/2,0,0,QDiamondBox.Height/2,0) QDiamondBox.Line(0,QDiamondBox.Height/2,QDiamondBox.Height/2,QDiamondBox.Height,0) QDiamondBox.Line(QDiamondBox.Height/2,0,QDiamondBox.Height,QDiamondBox.Height/2,0) QDiamondBox.Line(QDiamondBox.Height,QDiamondBox.Height/2,QDiamondBox.Height/ 2,QDiamondBox.Height,0) QDiamondBox.Paint(QDiamondBox.Height/2, QDiamondBox.Height/2, QDiamondBox.HiLightColor, 0) QDiamondBox.Line(QDiamondBox.Height/2,0,QDiamondBox.Height,QDiamondBox.Height/ 2,&HFFFFFF) QDiamondBox.Line(QDiamondBox.Height,QDiamondBox.Height/2,QDiamondBox.Height/ 2,QDiamondBox.Height,&HFFFFFF) ELSE QDiamondBox.Line(QDiamondBox.Height/2,0,0,QDiamondBox.Height/2,0) QDiamondBox.Line(0,QDiamondBox.Height/2,QDiamondBox.Height/2,QDiamondBox.Height,0) QDiamondBox.Line(QDiamondBox.Height/2,0,QDiamondBox.Height,QDiamondBox.Height/2,0) QDiamondBox.Line(QDiamondBox.Height,QDiamondBox.Height/2,QDiamondBox.Height/ 2,QDiamondBox.Height,0) QDiamondBox.Paint(QDiamondBox.Height/2, QDiamondBox.Height/2, &HBBBBBB, 0) QDiamondBox.Line(QDiamondBox.Height/2,0,0,QDiamondBox.Height/2,&HFFFFFF) QDiamondBox.Line(0,QDiamondBox.Height/2,QDiamondBox.Height/ 2,QDiamondBox.Height,&HFFFFFF) END IF QDiamondBox.TextOut(QDiamondBox.Height + 5, QDiamondBox.Height/2-QDiamondBox.Height/ 4, QDiamondBox.Caption, 0, -1) END SUB Può sembrare orribile, ma un esame attento rivela che appare complicato a causa delle righe lunghe. Questo codice disegna un diamante e dimensiona il componente in modo appropriato cosicchè appaia correttamente quando viene ridimensionato. Ciò è molto importante, per evitare che il vostro componente sembri una miniatura quando l'utente specifica Height = 100, Width = 100, ecc... È sufficiente inserire il codice sopra indicato all'interno della dichiarazione del TYPE. È necessario inoltre definire un'altra proprietà , HiLightColor. Ogni qualvolta viene selezionata la nostra check box, probabilmente vorremmo che il nostro componente cambi colore per avvisare l'utente che è stato selezionato. 10.4 Definizione di eventi È possibile definire nuovi eventi per i nostri componenti, ma di questo parleremo più avanti. Per il momento abbiamo tutto quello che serve per il nostro componente QDiamondBox, OnPaint, e OnClick. Che bisogno abbiamo di definire eventi? Perché non lasciamo che lo faccia l'utente? Certo, ma dal momento che questo è il nostro componente personalizzato, vogliamo che succedano alcune cose. Per esempio, il componente QCheckBox diventa automaticamente selezionato e non selezionato quando viene cliccato; non è stato necessario prevedere un evento per fare ciò. Questo è ciò che vogliamo che accada nel nostro componente personalizzato. Per cominciare, avremo bisogno di un evento OnPaint. Questo evento è chiamato ogni volta che il form o il componente vengono aggiornati. TYPE QDiamondBox EXTENDS QCanvas 'Inserire qui le proprietà 'Inserire qui il metodo DrawComponent EVENT OnClick IF QDiamondBox.Checked THEN QDiamondBox.Checked = 0 ELSE QDiamondBox.Checked = 1 END IF QDiamondBox.DrawComponent END EVENT EVENT OnPaint QDiamondBox.DrawComponent END EVENT END TYPE Praticamente, abbiamo già spiegato perché bisogna fare riferimento a DrawComponent, ma è l'identificatore EVENT è nuovo. Dice semplicemente che stiamo creando un gestore EVENT per il nostro componente. Scrivete il vostro codice come fate normalmente. Ogni qualvolta viene creato (DIM) il nuovo componente, gli eventi vengono registrati automaticamente per noi. Vengono chiamati quando si verifica lo specifico evento. Fin qui tutto bene, e se ho bisogno di scrivere un nuovo gestore di evento? Nel nostro esempio QDiamondBox, dovremo sicuramente ridefinire OnClick, in quanto vogliamo sapere se l'utente ha selezionato o deselezionato la DiamondBox. A questo punto le cose si complicano. Siccome non vogliamo scartare la nostra precedente dichiarazione di OnClick, dovremo ereditarla. DIM DBox AS QDiamondBox SUB NewDBoxOnClick DBox.InheritOnClick ' inserire codice qui END SUB DBox.OnClick = NewDBoxOnClick Come avrete notato, per evitare l'evento OnClick vi facciamo riferimento utilizzando QObject.Inherit, dove EventName è l'evento che vogliamo ereditare. In realtà è possibile ereditare OnPaint in questo esempio, ma ciò non avrebbe alcuno scopo. Se l'evento ereditato non viene trovato (ad esempio perché non vi era alcuna dichiarazione precedente), riceverete un messaggio di errore in compilazione. 10.5 Aggiunta di componenti al vostro componente (composizione) Nel nostro esempio, non è necessario includere componenti nel nostro nuovo componente QDiamondBox, ma dal momento che questo è spesso utile, ne parliamo in questa sezione. È possibile aggiungere solo componenti Rapid-Q, e non quelli personalizzati. Anche in questo caso, non c'è niente di nuovo nel metodo per aggiungere componenti o proprietà : TYPE QGenericForm AS QForm Panel AS QPanel END TYPE DIM GF AS QGenericForm GF.Panel.Left = 123 Notate come facciamo riferimento ad un oggetto all'interno di un oggetto. Il primo punto serve per fare riferimento all'oggetto Panel, ed il secondo per fare riferimento alle proprietà e metodi di Panel. Se desiderate che vostro Panel abbia dei valori di default, utilizzate il metodo CONSTRUCTOR come prima: TYPE QGenericForm AS QForm Panel AS QPanel CONSTRUCTOR Panel.Parent = QGenericForm Panel.Width = QGenericForm.ClientWidth Panel.Height = QGenericForm.ClientHeight END CONSTRUCTOR END TYPE Avete notato probabilmente che anche se abbiamo in qualche modo sottointeso che il Panel sarà parte del form, è comunque necessario assegnargli la proprietà Parent, altrimenti non sarà visibile. Ciò è abbastanza semplice, e per quanto riguarda la definizione dei suoi eventi? Non c'è problema: TYPE QGenericForm AS QForm Panel AS QPanel EVENT Panel.OnClick ShowMessage('L'utente ha cliccato sul pannello') END EVENT EVENT OnClick ShowMessage('L'utente ha cliccato sul form') END EVENT END TYPE Notate che c'è un riferimento (punto) in più. Se questo viene omesso, l'evento OnClick sarà associato al vostro QGenericForm. Come prima, per ereditare eventi dovrete fare così: GF.Panel.OnClick = GF.Panel.InheritOnClick Normalmente, non ci sarà quasi mai bisogno di accedere ad un componente all'interno di un altro componente. Per esempio, potete creare il vostro form personalizzato, con i bottoni per chiuderlo e ridimensionarlo senza riscrivere i gestori degli eventi. 10.6 Estensione di un componente vuoto In alcuni casi non avrete bisogno di estendere alcun componente, e dovrete solo scrivere il vostro oggetto interfaccia. Ad esempio si può incrementare un oggetto interfaccia QIniFile. Magari prevedere proprietà e metodi per accedere e scrivere facilmente nel vostro file .INI nel modo che preferite. Per fare ciò Rapid-Q fornisce un oggetto vuoto: TYPE QIniFile EXTENDS QObject Size AS INTEGER FileName AS STRING SUB SaveINI END SUB SUB LoadINI END SUB END TYPE Questo genere di oggetto si comporta come QSocket. Non è un componente visibile, è essenzialmente un oggetto interfaccia. Gli oggetti forniscono un'interfaccia più semplice e più elegante (non che questo mi interessi molto in realtà ). Concludendo, questo è praticamente tutto quello che dovete sapere per aggiungere i vostri componenti a Rapid-Q, è piuttosto semplice da capire. 10.7 Proprietà e metodi pubblici e privati Tutte le proprietà e metodi sono PUBLIC per default. Ecco come definire l'area di validità delle vostre proprietà e metodi: TYPE QText EXTENDS QObject PUBLIC: I AS INTEGER X AS INTEGER PRIVATE: Y AS INTEGER PUBLIC: SUB Test QText.X = QText.Y + 1 END SUB END TYPE Le proprietà I e X sono PUBLIC, cioè possono essere utilizzate al di fuori della loro area di validità (per esempio al di fuori di TYPE). Lo stesso vale per il metodo Test. Tuttavia, la proprietà Y è PRIVATE, e non può essere utilizzata al di fuori della sua area di validità . Dal momento che SUB Test è all'interno dell'area di Y (cioè all'interno di TYPE), è possibile utilizzarla lì. È possibile definire proprietà e metodi PROTECTED, ma funzionano esattamente come proprietà e metodi PUBLIC in quanto (per il momento) non è possibile estendere un tipo personalizzato. Per chi non ha una cultura OOP, la ragione per cui esistono membri PUBLIC e PRIVATE è essenzialmente a beneficio dell'utente finale. Saprete certamente quali membri non debbano essere utilizzati al di fuori della loro area di validità , ma definendo membri PRIVATE istruite i vostri utenti a non toccarli nei vostri programmi in quanto vengono utilizzati 'internamente'. 10.8 Schemi e gruppi di proprietà Non abbiamo ancora parlato di come vengono creati gli eventi personalizzati, ci arriveremo presto. Tuttavia, ci sono alcune altre tecniche interessanti, una detta schema che potete usare nelle vostre definizioni di TYPE, ed un'altra detta guppo di proprietà . Arriveremo ai gruppi di proprietà più avanti questa sezione, cominciamo da gli schemi. Uno schema di definizione del tipo può sembrare strano, ma sono modellati più o meno in base a C++. TYPE NewClass EXTENDS QOBJECT N AS DataType END TYPE Subito dopo aver dichiarato il nuovo tipo, si può definire lo schema . Nell'esempio precedente, il nostro solo parametro è DataType, ma se possono utilizzare un numero infinito. A differenza di C++, non si specifica il tipo di parametro, ma solo il nome. Quando si effettua la DIM del nuovo tipo, è necessario passargli il parametro extra previsto dallo schema: DIM MyClass1 AS NewClass DIM MyClass2 AS NewClass Notate che la proprietà N di MyClass1 è legata ad un dato di tipo INTEGER, mentre MyClass2 ha una proprietà N legata ad un dato di tipo STRING. Ecco alcuni altri esempi: TYPE Arrays Size> EXTENDS QOBJECT Item(Size) AS DataType SUB Clear DIM N AS DataType DIM I AS LONG DIM V AS VARIANT WITH Arrays V = .Item(0) IF VARTYPE(V) = 2 THEN '-- String data type FOR I = 0 TO Size .Item(I) = '' NEXT ELSE '-- Integer/Float FOR I = 0 TO Size .Item(I) = 0 NEXT END IF END WITH END SUB END TYPE DIM IntArray AS ARRAYS 100> DIM StrArray AS ARRAYS 50> IntArray.Clear IntArray.Item(1) = 99 StrArray.Clear StrArray.Item(1) = 'Ciao mondo' PRINT IntArray.Item(1) PRINT StrArray.Item(1) L'utilizzo di schemi può aiutare a ridurre la quantità di codice riutilizzando lo stesso TYPE per le nostre matrici INTEGER e STRING. Ora che abbiamo parlato di schemi, passiamo ai gruppi di proprietà . Un gruppo di proprietà non è altro che una raccolta di proprietà con la differenza che è possibile fare delle elaborazioni particolari quando un utente assegna la proprietà . Cominciamo con un esempio abbastanza inutile: TYPE TForm EXTENDS QFORM Focus AS LONG PROPERTY SET Set_Focus PROPERTY SET Set_Focus (Handle AS LONG) WITH TForm .Focus = Handle IF .Focus THEN SetFocus(Handle) END IF END WITH END PROPERTY END TYPE CREATE Form AS TForm CREATE Edit AS QEDIT END CREATE Focus = Edit.Handle END CREATE Non considerate la funzione SetFocus,si tratta solo di una chiamata API. Nell'esempio precedente, Focus è un gruppo di proprietà , ogni qualvolta viene assegnato un valore a questa proprietà viene automaticamente chiamata la routine Set_Focus come specificato nel parametro SET dove il parametro e il valore che viene assegnato. Naturalmente, potete chiedervi perché si possa aver bisogno di questa in quanto è equivalente a: TYPE TForm EXTENDS QFORM SUB Focus (Handle AS LONG) IF Handle THEN SetFocus(Handle) END IF END SUB END TYPE CREATE Form AS TForm CREATE Edit AS QEDIT END CREATE Focus(Edit.Handle) END CREATE Come avrete notato che quest'esempio, dal momento che Focus è una SUB, non è possibile recuperare il valore (come nel nostro esempio precedente). In questo caso, dovrete usare un altro nome come GetFocus per verificare quale maniglia sia attiva. Nell'esempio presente è possibile leggere e scrivere la proprietà ed eseguire elaborazioni particolari quando la proprietà viene assegnata, che è ciò che volevamo. Prendete ad esempio come è cambiato BorderStyle in un QFORM: quando assegnamo un nuovo valore a BorderStyle, inizia una sequenza di eventi, ma dal momento che BorderStyle è un gruppo di proprietà non abbiamo bisogno di definire una SUB per determinare lo stile ed una FUNCTION per recuperare il valore. TYPE TForm EXTENDS QFORM GetStyle AS LONG SUB BorderStyle (Style AS LONG) TForm.GetStyle = Style SendMessage(TForm.Handle, etc...) END SUB END TYPE CREATE Form AS TFORM BorderStyle(bsNone) Caption = STR$(Form.GetStyle) END CREATE Come vedete è un vero spreco, in questo caso è opportuno utilizzare gruppi di proprietà . Ovviamente questi non serviranno se non avete bisogno di alcun processo particolare quando assegnate un valore ad una proprietà . Se decidete di utilizzare gruppi di proprietà ci sono alcune regole da seguire: 1. La proprietà ed il parametro devono essere dello stesso tipo; 2. Solo tipi semplici e QObject possono essere gruppi di proprietà , sono esclusi matrici e UDT. Il compilatore di avvertirà se violerete queste regole, ma a volte i messaggi di errore non sono molto chiari. 10.9 Eventi personalizzati Gli eventi personalizzati sono stabiliti dal programmatore, cioè voi. Proviamo a immaginare una situazione in cui è necessario inventare un evento. Prendiamo ad esempio il componente QSOCKET. È completamente privo di qualsiasi evento; e se vogliamo sapere quando il server è pronto? Ovviamente possiamo continuare ad interrogarlo per vedere quando ciò accade, ma non sarebbe meglio se fosse rilevato automaticamente, per poi scrivere un gestore di evento per fare quello che vi serve? Non scriverò in realtà l'esempio completo, ma solo la parte necessaria per dimostrare come creare eventi personalizzati: '-- Definire, ma non implementare, una funzione schema DECLARE SUB ServerReady_EventTemplate (Socket AS LONG) TYPE TSocket EXTENDS QSOCKET OnServerReady AS EVENT(ServerReady_EventTemplate) Timer1 AS QTIMER Sock AS LONG EVENT Timer1.OnTimer WITH TSocket IF .IsServerReady(.Sock) AND .OnServerReady > 0 THEN '-- OnServerReady > 0 serve per controllare se il puntatore è nullo '-- per controllare se c'è un gestore di evento assegnato '-- se assegnato, realizza l'evento CALLFUNC(.OnServerReady, .Sock) END IF END WITH END EVENT CONSTRUCTOR OnServerReady = 0 Timer1.Enabled = 1 Timer1.Interval = 500 END CONSTRUCTOR END TYPE SUB ServerReady (Sock AS LONG) PRINT Sock;' is ready.' END SUB CREATE Socket AS QSOCKET OnServerReady = ServerReady END CREATE Può essere utile leggere il prossimo capitolo sui puntatori alle funzioni, dal momento che fondamentalmente è così che funzionano gli eventi personalizzati. OnServerReady è in pratica un puntatore ad una funzione, che punta ad una SUB (se assegnato, altrimenti è equivalente a zero). È richiesta una dichiarazione dello schema SUB, allo scopo di fornire a CALLFUNC i parametri necessari. Può sembrare un po' complicato all'inizio, e serve un po' di tempo per abituarsi alla sintassi e all'idea di realizzare un evento. Tuttavia, la creazione di eventi personalizzati presenta parecchi vantaggi ed utilizzi, ed apprendere come creare i vostri eventi potrà semplificare considerevolmente l'utilizzo dei vostri componenti o perfino estendere le funzionalità di altri (come nell'esempio sopra). 10.10 Codice sorgente di QDiamondBox $APPTYPE GUI $TYPECHECK ON TYPE QDiamondBox EXTENDS QCanvas '' È possibile estendere qualsiasi QObject '-- Nuove proprietà , si possono anche aggiungere componenti Caption AS STRING Checked AS INTEGER HiLightColor AS INTEGER '-- Non esistono metodi protetti, ma è meglio informare l'utente '-- PROTECTED (nel senso che è meglio non chiamarlo direttamente nel vostro programma). SUB DrawComponent IF QDiamondBox.Checked THEN QDiamondBox.Line(QDiamondBox.Height/2,0,0,QDiamondBox.Height/2,0) QDiamondBox.Line(0,QDiamondBox.Height/2,QDiamondBox.Height/2,QDiamondBox.Height,0) QDiamondBox.Line(QDiamondBox.Height/2,0,QDiamondBox.Height,QDiamondBox.Height/2,0) QDiamondBox.Line(QDiamondBox.Height,QDiamondBox.Height/2,QDiamondBox.Height/ 2,QDiamondBox.Height,0) QDiamondBox.Paint(QDiamondBox.Height/2, QDiamondBox.Height/2, QDiamondBox.HiLightColor, 0) QDiamondBox.Line(QDiamondBox.Height/2,0,QDiamondBox.Height,QDiamondBox.Height/ 2,&HFFFFFF) QDiamondBox.Line(QDiamondBox.Height,QDiamondBox.Height/2,QDiamondBox.Height/ 2,QDiamondBox.Height,&HFFFFFF) ELSE QDiamondBox.Line(QDiamondBox.Height/2,0,0,QDiamondBox.Height/2,0) QDiamondBox.Line(0,QDiamondBox.Height/2,QDiamondBox.Height/2,QDiamondBox.Height,0) QDiamondBox.Line(QDiamondBox.Height/2,0,QDiamondBox.Height,QDiamondBox.Height/2,0) QDiamondBox.Line(QDiamondBox.Height,QDiamondBox.Height/2,QDiamondBox.Height/ 2,QDiamondBox.Height,0) QDiamondBox.Paint(QDiamondBox.Height/2, QDiamondBox.Height/2, &HBBBBBB, 0) QDiamondBox.Line(QDiamondBox.Height/2,0,0,QDiamondBox.Height/2,&HFFFFFF) QDiamondBox.Line(0,QDiamondBox.Height/2,QDiamondBox.Height/ 2,QDiamondBox.Height,&HFFFFFF) END IF QDiamondBox.TextOut(QDiamondBox.Height + 5, QDiamondBox.Height/2-QDiamondBox.Height/ 4, QDiamondBox.Caption, 0, -1) END SUB '-- Eventi ereditati (spiacente, non è possibile creare nuovi eventi) '-- L'utente può comunque superare questi eventi, ma non è una buona idea. EVENT OnClick IF QDiamondBox.Checked THEN QDiamondBox.Checked = 0 ELSE QDiamondBox.Checked = 1 END IF QDiamondBox.DrawComponent END EVENT EVENT OnPaint QDiamondBox.DrawComponent END EVENT '-- Valori di default CONSTRUCTOR Height = 30 Width = 100 HiLightColor = &H00FF00 Caption = 'DiamondBox' Checked = 0 END CONSTRUCTOR END TYPE '----- Collaudiamo il nostro nuovo componente DECLARE SUB DBox2Click DIM Font AS QFont Font.Name = 'Arial' Font.Size = 10 CREATE Form AS QForm Center Height = 120 Caption = 'Custom Check Boxes' CREATE DBox1 AS QDiamondBox Caption = 'Diamond Box 1' Left = 100 Height = 20 END CREATE CREATE DBox2 AS QDiamondBox Caption = 'Diamond Box 2' Top = 30 Left = 100 Height = 20 Width = 140 HiLightColor = &H0000FF Font = Font ShowHint = 1 ' True Hint = 'Click me' OnClick = DBox2Click END CREATE CREATE DBox3 AS QDiamondBox Caption = 'Diamond Box 3' Top = 60 Left = 100 Height = 20 END CREATE ShowModal END CREATE SUB DBox2Click DBox2.InheritOnClick '' Inherit event ShowMessage('Diamond Box 2 clicked') END SUB ' =============================================================================== ' 2003 Holyguard.net - 2007_Abruzzoweb