Newsgroup: it.comp.lang.delphi
Titolo: Ancora guai creando oggetti VCL a runtime :-(

Salve,
avevo risolto poco tempo fa il problema guardando nelle FAQ
del NG, ma la cosa si è rivelata iù difficile del previsto...
OK, io ho creato una classe derivata da TButton (un bottone più
qualche info associata) e ora voglio creare a runtime una lista di
questi oggetti: come da FAQ ho scritto questo codice:
procedure TFormMain.FormCreate(Sender: TObject);
var Posteria: TList; { una lista di bottoni PostoButton }
Bottone: ^PostoButton;
{PostoButton è il nome della classe bottone che ho creato}
begin
  Posteria.Create;
  for i:=1 to Maxcol do
    for j:= 1 to DatiArray[i] do
      begin
        Bottone^.Create(FormMain);
        Bottone^.Parent:=FormMain;
        Bottone^.OnClick:=PostoClick;
        ( ...altre cosette... )
        Posteria.Add(Bottone);
      end;
(ecc. ecc.)
La procedura che voglio associare all'evento OnClick è:
procedure  TFormMain.PostoClick(Sender: TObject);
begin
(ecc. ecc.)
end;
Ora, in fase di compilazione fila tutto liscio, ma appena parte,
l'EXE mi spara immediatamente un errore a runtime:
(Project 'FormMain.exe' raised exception class EAccessViolation with
message 'Access violation at address xxxxxxx in module 'FormMain.exe'.
Reading of address 00000000'. Process stopped etc. etc...
Sono certo che il responsabile è la TList di bottoni, perchè il resto
del codice è abbastanza pedestre (e soprattutto perchè non era mai
successo, prima che usassi la lista per i bottoni...)
Che cosa sto sbagliando? Secondo me è il fatto di usare un puntatore
all'oggetto da creare invece di crearlo direttamente, però ho bisogno
di poter accedere ai bottoni creati con sistematicità e siccome sono tanti
(centinaia e in quantità non nota a priori) devo per forza usare una lista
o (al limite) un array... insomma non so come tirar fuori le gambe.
E' possibile creare degli array di funzioni in Object Pascal? Forse me
posso cavare associando ad ogni PostoButton una funzione che sia
associata anche ad un array esterno di puntatori a funzione...
Help!...

 Reading of address 00000000'. Process stopped etc. etc...
Provato con il debug dove si arrabbia ???
Quasi sicuramente sulla creazione della lista
Ma ti serve proprio il puntatore al tuo bottone??
Se no allora prova :
 procedure TFormMain.FormCreate(Sender: TObject);
 var Posteria: TList; { una lista di bottoni PostoButton }
 Bottone: PostoButton;
 {PostoButton č il nome della classe bottone che ho creato}
 begin
   Posteria := TList.Create;
   for i:=1 to Maxcol do
     for j:= 1 to DatiArray[i] do
       begin
         Bottone = PostoButton.Create(Self);
         Bottone.Parent:=Self;
         Bottone.OnClick:=PostoClick;
         ( ...altre cosette... )
         Posteria.Add(Bottone);
       end;
dovrebbe funzionare lo stesso
NB Codice non testato
Saluti NEA

 Ma ti serve proprio il puntatore al tuo bottone??
Il puntatore? A me serve un mezzo per accedere ai bottoni dopo
creati (che sono tanti e in numero ignoto, ricordalo) e la lista mi sembrava
la soluzione ovvia al problema...
Però se hai un'idea migliore, sono tutto orecchi :-)

(centinaia e in quantità non nota a priori)
Una sola domanda: sei sicuro che ti servano *centinaia* di
controlli ?
-- 
Andrea Laforgia
#exclude <windows.h>

         Posteria.Add(Bottone);
Una volta che l'hai creato con un Owner e' nella property Components
dell'Owner, una volta che gli hai assegnato un Parent e' nella property
Controls del Parent.
Cio' detto, se vuoi separare la gestione dei bottoni da quella degli
altri componenti della form, ti consiglio di mettere un TObjectList (o
discendente) fra i campi private del tuo form, allochi quello nel
FormCreate, (lo disallochi nell'OnDestroy), crei i tuoi TPostoButton
senza Owner e ne fai l'Add sulla lista (TObjectList di default
_possiede_ gli oggetti della lista, quindi e' responsabile della loro
deallocazione).
Cos'e' l'applicazione, un sistema di prenotazione per cinema/teatri?
Saluti
Paolo Arosio

 Perň se hai un'idea migliore, sono tutto orecchi :-)
Per prima cosa sposta la variabile "Posteria" fuori dal Create del form e
mettila globale
(cosě ci puoi accedere da ovunque). Poi fai come ti ha detto NEA per la
creazione degli oggetti.
Per prenderli invece della "Posteria" fai:
procedure Metodo.CheVuoi(Parametri);
var
  BottoneTMP:PostoButton;
begin
  ....
  BottoneTMP:=Posteria.Items[Index] as PostoButton;
  ...
end;
e ti dovrebbe andare tutto ok...

 controlli ?
Mah... potrei cavarmela con una specie di mappa cliccabile
disegnata nella form di cui ridisegnerei il settore in cui viene
cliccata, per simulare il cambiamento di stato del "bottone"...
perň mi piace poco, preferivo molto il metodo "un bottone
per ogni casella". Una questione di "pulizia" architetturale
insomma (e anche estetica), per non ricorrere ad un sistema
che sarebbe molto piů macchinoso da gestire.

che sarebbe molto piů macchinoso da gestire.
Se ci tieni realmente alla "pulizia architetturale", fai a
meno dei bottoni e usa una mappa cliccabile.  Ne guadagnerai
anche in consumo di risorse.


 senza Owner
Un momento, un momento: ma TPostoButton.Create deve prendere
per forza un argomento che è l'owner del controllo, che deve essere
di tipo TComponent. Quindi devo per forza assegnare un Owner al
bottone che creo... e non può essere la Posteria, perchè è una
TObjectList e non un TComponent. E se provo a chiamare Create
senza parametri mi becco un errore di compilazione. (Ho provato: un
cast da TObjectList a TComponent non funge...)
E' questa la "invalid property", giusto?? Ma come cambio l'Owner?
Per giunta è a sola lettura...

 Cos'e' l'applicazione, un sistema di prenotazione per cinema/teatri?
Precisamente... non ci andava piů di farle a mano :-)
Complimenti per la sagacia!

 ??? vuoi dire che cosě poi sarebbe un casino deallocarli?
No: solo che _hai gia'_ una lista che li contiene (anzi 2: Components &
Controls).
ad es: se li crei con un parent tipo un tpanel e non metti dentro il
panel altri controlli hai gia' li' la lista di tutti i pulsanti.
Saluti
Paolo Arosio

 di tipo TComponent.
o nil.
Dopo, facendo l'Add sulla TObject questa ne acquisisce l'ownership (cmq,
puoi anche usare una TComponentList). In ogni caso la lista, come dico
in altro msg, _secondo me_ non e' indispensabile
Saluti
Paolo Arosio

 panel altri controlli hai gia' li' la lista di tutti i pulsanti.
Forse dico una cavolata :
Se invece del TPanel ti crei N form con i bottoni che ti servono e dopo
averle create gli assegni il parent della form Main diventano come pannelli
ma vivono di vita propria
Saluti