Newsgroup: it.comp.lang.delphi
Titolo: gestire parametri evento TAction.onExecute

FormA crea FormB
 
procedcure CreaForm;
begin
fFormB := TFormB.Create(Self);           //crea il form
fFormB.actEsci.OnExecute := ChiudeForm;  //intercetta TAction
end;
 
 
//chiamata generica per qualunque form
//invece che avere ChiudeFormB, ChiudeFormC, etc
//
procedure ChiudeForm(Sender: TObject);
begin
   ShowMessage(Sender.ClassName); // TAction
 
   //devo chiudere FormB ... FormB.Close
   //liberare l'istanza fFormB  .. FreeAmdNil(fFormB)
 
   //problema mi serve un TForm per eseguire il Close
 
end;
 
posso dichiarare come segue ed il compilatore lo accetta
 
procedure ChiudeForm(Sender: TObject; fRifForm: TForm);
 
non posso chiamare cosi'
 
fFormB.actEsci.OnExecute := ChiudeForm(fFormB);
fFormB.actEsci.OnExecute := ChiudeForm(Sender, fFormB); //nella CreaForm 
non ho Sender
 
mi chiede come primo parametro TObJect e non so cosa passargli, a 
seguire TForm
 
[DCC Error] unt_Elaborazione.pas(116): E2010 Incompatible types: 
'TNotifyEvent' and 'procedure, untyped pointer or untyped parameter'

 &#39;TNotifyEvent&#39; and &#39;procedure, untyped pointer or untyped parameter&#39;
 
A mio avviso, stai costruendo una architettura piuttosto incasinata.
 
*OnExecute* è il nome di un evento a cui puoi assegnare il riferimento a un metodo (compatibile con il prototipo che l'evento si aspetta) che deve essere richiamato quando quell'evento si verifica (nella logica del componente/controllo chiamante).
 
Premesso questo, la tua assegnazione "con parametri" non ha alcun senso: non è una chiamata, né puoi cercare di stabilire quali parametri deve avere il metodo che viene richiamato, poiché è la logica a cui ti agganci a stabilire la firma di quel metodo, che deve essere rispettata, e a cui passa le informazioni che ritiene opportune (e non puoi definire all'esterno nuovi parametri che quella logica non saprebbe come valorizzare).
 
Credo sia opportuno che tu approfondisca questi concetti prima di imbastirci sopra delle soluzioni.
 
Poi secondo me non è formalmente (e forse anche praticamente) una buona idea andare ad agganciare il metodo di un Form come risposta a un componente che si trova in un altro form, magari annullando altri metodi di risposta assegnati.
 
Meglio implementare un pattern di tipo *Observer* (
http://it.wikipedia.org/wiki/Observer_pattern
), oppure semplicemente introdurre un evento personalizzato sul "FormB" che segnali ad altri form la chiusura effettuata.
 
Come detto sopra, comunque, credo sia meglio che ti documenti su questi concetti, altrimenti ti sarà impossibile imbastire soluzioni valide (magari funzionano all'inizio, ma poi saranno ingestibili se l'applicazione cresce).
 
Ciao,
  Marco.
 
-- 
MARCO BREVEGLIERI
#Home: 
http://www.marco.breveglieri.name
 
#Blog: 
http://www.compilaquindiva.net
 

 'TNotifyEvent' and 'procedure, untyped pointer or untyped parameter'
 
Non è per nulla chiaro ciò che vuoi sapere.
 
Per quel che riguarda l'errore, significa che non puoi assegnare ad
una property dichiarata come TNotifyEvent una procedura.
 
Quando hai un evento (che è un tipo particolare di property di un
oggetto), questo si aspetta in generale un *metodo*, ovvero una
"procedure of object", ovvero del codice associato ad una classe, non
una procedura pura.
 
In particolare, OnExecute vuole un TNotifyEvent; ad es (digitato qui,
error-prone):
 
type
TSomeEventHandler = class
  procedure CheFare(Sender: TObject);
end;
 
...
procedure TSomeEventHandler.CheFare(Sender: TObject)
begin
  ShowMessage(Sender.Classname + ' mi ha mandato un evento');
  with Sender as TForm do begin
    ShowMessage('In particolare, è stata una form chiamata ' + Name);
  end;
end;
 
 
Assegnazione:
 
...
anHandler := TSomeEventHandler.Create;
 
aForm := TMyForm.Create(Self);
aForm.OnSomeEvent := anHandler.CheFare;
...
 
 
Compili, scateni il SomeEvent e vedi che succede :-).
 
 
Se invece il tuo problema è un altro, facci capire (meglio) qual è.
 
 
Saluti
Paolo Arosio

 Se invece il tuo problema � un altro, facci capire (meglio) qual �.
 
Il problema era quello, di creare una gestione centralizzata di chiusura 
dei vari form.
 
In pratica una routine che ricevesse il TForm da chiudere.
 
Ringrazio Te e Marco per i suggerimenti ricevuti.
 
ciao

 In pratica una routine che ricevesse il TForm da chiudere.
 
Basta fare una procedura che prendere un *TForm* come parametro e ne chiama il metodo /Close/. :)


>
ma e' proprio cio' che non si puo' fare;
come hai gia' precisato e' il prototipo che detta le regole.
 
quindi potrei
- TForm.<Action>.OnExecute := ChiudeForm;
oppure
- TForm.onClose := ChiudeForm;
 
il metodo richiamato dovre' essere del tipo (Sender: TObject)
 
procedure ChiudeForm(Sender: TObject);
begin
 
   Close;
end;
 
cosi' facendo mi chiudera' il form principale, non potendo passare il 
*form* che voglio chiudere.
 
anche se creo
 
procedure Chiudi(aForm: TForm);
begin
   aForm.Close;
end;
 
non potro' richiamarla perche' non rispetta la logica prevista.
 
//non posso passare parametri
TForm.<Action>.OnExecute := Chiudi(<FormdaChiudere>);
 
*** salvo ricorrere a qualcosa di simile *** su ogni Form Child
funziona, ma mi pare ingarbugliata come soluzione.
 
type
   TChiudeForm = procedure(Sender: TObject) of object;
    ...
end;
 
type ...class (TForm)
 
   private
     FOnChiudeForm: TChiudeForm;
 
     procedure DoChiudeForm;
 
   published
     property OnChiudeForm: TChiudeForm read FOnChiudeForm write 
FOnChiudeForm;
 
... da un form che richiama i figli
 
procedure Tfrm_Prova.AnagrafeClienti(Sender: TObject);
begin
   frm_Child := Tfrm_Child.Create(Self);
   frm_Child.Parent := pnlForm;
   frm_Child.Height := Self.ClientHeight;
   frm_Child.Width := Self.ClientWidth;
   frm_Child.Show;
 
   //Assegna Evento Chiusura Form
   frm_Child.OnChiudeForm := ChiudeTest;
end;
 
procedure Tfrm_Child.FormClose(Sender: TObject;
   var Action: TCloseAction);
begin
   DoChiudeForm;
end;
 
procedure Tfrm_Child.DoChiudeForm;
begin
  if Assigned(FOnChiudeForm) then FOnChiudeForm(Self);
end;
 
ciao

 ma e' proprio cio' che non si puo' fare;
 
[???]
 
Abbiamo dei problemi di reciproca comprensione.
 
Cosa c'è che non funziona in:
 
  procedure ChiudiForm(quale: TForm);
  begin
    quale.close;
  end;
 
???
 
che, vorrai ammettere, è la risposta alla tua domanda "In pratica una
routine che ricevesse il TForm da chiudere".
 
Se poi, ancora, la domanda è un'altra, ancora non l'hai espressa in
modo che riusciamo a comprenderla.
 
Saluti
Paolo Arosio
 
 

>
 
niente altro , stessa domanda.

 end;
 
Questa roba non ha senso: l'evento /OnClose/ viene generato dal Form quando viene chiuso e consente di essere informati in qualche modo (per eseguire operazioni correlate) della chiusura del Form in questione (che però deve essere chiusa usando la solita "X" oppure con il metodo /Close/ da codice).
 
In breve, all'evento /OnClose/ si aggancia un metodo che riceve una notifica quando qualsiasi cosa chiude la finestra.
 
Quanto hai scritto tu non ha alcun senso: intercetti un evento per sapere quando un Form viene chiuso, ma gli associ invece una procedura che fa questo mestiere, e tra l'altro non ha nemmeno una firma compatibile con quella che si attende l'evento.
 
Sembra quasi che tu stia buttando dentro cose un po' alla rinfusa sperando che, mescolandosi, magicamente si incastrino alla perfezione e inizino a funzionare come ti aspetti, ma secondo me non è l'approccio corretto.
 
Studia a fondo gli elementi della OOP e come vengono gestiti gli eventi in Delphi; se non hai chiaro concetti di *procedura* oppure *metodo* e affini, allora non puoi continuare.
 
Ciao,
  Marco.


 niente altro , stessa domanda.
 
Evidentemente no.
 
Saluti
Paolo Arosio

>>
 
Ciao Maurizio,
sei partito dal come stavi facendo una cosa e sei stato corretto sul 
perchè non funzionasse, ma forse, se vuoi davvero essere aiutato 
dovresti dirci qual'è il comportamento che vorresti e ti si può 
indicare la strada da prendere.
Fermo restando che quello che ti ha detto Marco mi sembra chiarissimo, 
e da li dovresti partire.
Un saluto
 
-- 
Emanuele Desideri
 
 

>
solitamnente gestisco cosi' i form:
- tutti non Modal
- un form che fa da contenitore (padre)
- n form Figli che si collocano nel Form padre
 
un esempio concreto : per creare dal form padre Tfrm_MenuAree
 
   frm_Anagrafe := Tfrm_Anagrafe.Create(Self);
   VisualizzaForm(frm_Anagrafe, 0, 0);
 
   //.... con l'istanza frm_Anagrafe posso accedere ai vari
   //Componenti del Form, per abilitarli/disabilitarli, etc.
 
   //si occupa di chiudere il form e liberare la risorsa
   frm_Anagrafe.act_Uscita.OnExecute := LiberaAnagrafe;
...
 
procedure Tfrm_MenuAree.VisualizzaForm(Modulo: TForm; iTop, iLeft: integer);
begin
   Modulo.Parent := Self;
   Modulo.Top := 0;
   Modulo.Left := 0;
   Modulo.Width := Self.ClientWidth;
   Modulo.Height := Self.ClientHeight;
   Modulo.Show;
end;
 
procedure Tfrm_MenuAree.LiberaAnagrafe(Sender: TObject);
begin
   frm_Anagrafe.Close;
 
   //il form padre frm_MenuAree e' sempre attivo
   //libero perche' potrebbe essere chiamata n volte
   //frm_Anagrafe := Tfrm_Anagrafe.Create(Self)
   FreeAndNil(frm_Anagrafe);
end;
 
cosi' tutto funziona e le risorse le libero dopo il close del form figlio
 
Mi era venuta l'idea di usare una procedure comune, che ricevuto il 
TForm potesse fare la stessa cosa di LiberaAnagrafe.
 
Ma come ben sapete <Form>.act_Uscita.OnExecute := <procedure>, non 
accetta parametri.
 
Perche' farlo ?
 
Invece di n Libera<Form> avrei potuto averne 1 sola,
ma non si puo' fare , il prototipo vuole Sender
 
procedure Tfrm_MenuAree.LiberaForm(Sender: TObject);
begin
   //qui non posso indicare quale Form chiudere
end;
 
evidentemente non e' possibile farlo.

 evidentemente non e' possibile farlo.
 
ne sei certo?
e comunque, sei sicuro che sia l'unica strada percorribile?
Cos'è Sender? E chi è?
 
-- 
Emanuele Desideri
 
 

 evidentemente non e' possibile farlo.
 
IMHO, invece, evidentemente non sai esattamente cosa vuoi ottenere.
 
Sorvolo sul fatto di avere un form padre che controlla come abilitare
ecc i controlli di un form figlio...
 
Qui sotto incollo *tutto* il codice di un main form che fa quello che
chiedi: invoca una procedura che chiude un form ChiudiForm(which:
TForm), che è in grado di chiudere *qualunque* form, che è ciò che hai
chiesto.
 
Button1Click per creare i form, listbox per visualizzare la lista di
form figli, edit1 per scriverci quale form vuoi chiudere, Button2Click
invoca la procedura per chiuderli.
Per semplicità, in TForm2 c'è solo una label.
 
 
 
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
Forms,
  Dialogs, StdCtrls;
 
type
  TForm1 = class(TForm)
    Button1: TButton;
    ListBox1: TListBox;
    Label1: TLabel;
    Edit1: TEdit;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private { Private declarations }
    formCounter: integer;
    formList: TStringList;
    procedure showFormList;
  public    { Public declarations }
    procedure closeHandler(Sender: TObject; var Action: TCloseAction);
  end;
 
var
  Form1: TForm1;
 
implementation
 
uses unit2;
 
{$R *.dfm}
 
procedure ChiudiForm(which: TForm);
begin
  which.Close;
end;
 
procedure TForm1.closeHandler(Sender: TObject; var Action:
TCloseAction);
var idx: integer;
    frmName: string;
begin
  frmName := (Sender as TForm2).Name;
  action := caFree;
  idx := formList.IndexOf(frmName);
  if idx >= 0 then begin
    formList.Delete(idx);
  end;
  Label1.Caption := 'Chiusa (ed eliminata) form ' + frmName;
  showFormList;
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  formList := TStringList.Create;
  formCounter := 0;
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
var i: integer;
begin
  for i := formList.Count-1 downto 0 do begin
    TForm2(formList.Objects[i]).OnClose := nil;
    TForm2(formList.Objects[i]).Free;
  end;
  formList.Free;
end;
 
procedure TForm1.showFormList;
begin
  ListBox1.Items.Assign(formList);
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var newForm: TForm2;
begin
  formCounter := formCounter +1;
  newForm := TForm2.Create(nil);
  newForm.Name := 'Form2_istance'+inttostr(formCounter);
  newForm.Caption := newForm.Name;
  newForm.Label1.Caption := newForm.Label1.Caption + ' '
+inttostr(formCounter);
  formList.AddObject(newForm.Name, newForm);
  newForm.OnClose := closeHandler;
  newForm.Show;
  edit1.Text := newForm.Name;
  showFormList;
end;
 
procedure TForm1.Button2Click(Sender: TObject);
var i: integer;
    frmToClose: TForm;
begin
  if edit1.Text = '' then exit;
  for i := 0 to Pred(Screen.FormCount) do begin
    if Screen.Forms[1].Name = Edit1.Text then begin
      ChiudiForm(Screen.Forms[1]);
      exit;
    end;
  end;
  ShowMessage(Edit1.Text  + ' form not found');
end;
 
end.
 
 

 end;
 
ciao.
 
prova a guardare chi e' il parametro Sender: se come penso e' l'azione  
act_Uscita della form frm_Anagrafe, allora puoi risalire alla form
in questo modo: 
 
// non testato.
procedure Tfrm_MenuAree.LiberaForm(Sender: TObject);
begin
   TForm( TAction(Sender).Owner ).Close();
end;
 
 
ciao
Giacomo

       ChiudiForm(Screen.Forms[1]);
 
CORRIGE:
    if Screen.Forms[i].Name = Edit1.Text then begin
      ChiudiForm(Screen.Forms[i]);
 
Saluti
Paolo Arosio

> end;
 
create 5 istanze 1,2,3,4,5 con Button1
su Edit1 indico Form2_instance3
click su Button2  -> Form2_instance3 form not fount
 
solo modificando cosi' chiudera' il form indicato in Edit1
anche se non in primo piano **Screen.Forms[1]**
 
   if Screen.Forms[i].Name = Edit1.Text then begin
      ChiudiForm(Screen.Forms[i]);


infatti Sender.ClassName ritorna TAction;
quello che hai scritto fa esattamente cio' che cercavo.
 

 quello che hai scritto fa esattamente cio' che cercavo.
 
Ti sei riletto, invece, cosa hai chiesto?
 
Saluti
Paolo Arosio


ho chiesto questo :
Invece di n Libera<Form> avrei potuto averne 1 sola,
ma non si puo' fare , il prototipo vuole Sender
 
e questo lo fa:
 
procedure Tfrm_MenuAree.LiberaForm(Sender: TObject);
begin
     TForm( TAction(Sender).Owner ).Close();
end;
 
per qualunque Form chiamero'
<Form>.act_Uscita.OnExecute := LiberaForm;
 
invece di
<FormSpecifico1>.act_Uscita.OnExecute := Libera<FormSpecifico1>;
<FormSpecifico2>.act_Uscita.OnExecute := Libera<FormSpecifico2>;
...
 
non c'e' contraddizione con la richiesta.

 non c'e' contraddizione con la richiesta.
 
42
 
Quanto fa sei per sette?
 
Che numero hai di piede?
 
Saluti
Paolo Arosio

>
 
giralo come vuoi... risulta sempre ..69....
 

 giralo come vuoi... risulta sempre ..69....
 
Ciao Maurizio,
senza fare polemica, ma la domanda non era estremamente chiara, o 
almeno, ti si è risposto per ciò che tu hai esposto, e non comprendevi 
le risposte.
E fin qui, non c'è nemmeno il problema, ma a questo punto vorrei 
capire, volevi la pappa pronta o volevi imparare?
perchè nel primo caso, c'è un enorme fraintendimento di fondo, nel 
secondo invece, dovresti approfondire alcuni argomenti emersi dalla 
discussione.
Non voglio perorare la causa di nessuno, nè iniziare una polemica, ma 
nemmeno passare per scemo.
 
-- 
Emanuele Desideri
 
 

 non c&#39;e&#39; contraddizione con la richiesta.
 
Ma la vera domanda è: con il tuo meccanismo, cosa hai guadagnato?
 
Bastava fare doppio clic sull'azione di chiusura del Form e dentro l'evento che Delphi crea in automatico, senza doverlo fare da codice, scrivere semplicemente
 
  Close;
 
Che senso ha fare una "procedura globale" che fa questa operazione, da associare "a mano" (tramite codice esplicito) a qualsiasi Execute delle azioni di chiusura affinché sulla loro esecuzione, facendo il "triplo cast mortale", si arrivi a ottenere lo stesso effetto?
 
Questo è proprio come sparare a una formica con un cannone.
 
Se implementi architetture di questo tipo semplicemente per chiudere una finestra, non immagino cosa esca fuori quando dovrai davvero concentrarti sulla cosiddetta "business logic" della tua applicazione. :-O


>
di questo non ti devi preoccupare, non hai la minima idea di cosa faccia 
l'applicazione, di professori alla guida in Italia ce ne sono anche 
troppi, e di i risultati "aumenti + tasse" li leggiamo/sentiamo tutti i 
giorni, a meno che tu non sia un'aspirante "politicante", scendi pure 
dal piedistallo, e occupati dle tuo business.
 
bye Maurizio
 
 
 

 bye Maurizio
 
Buon lavoro!


 giorni, a meno che tu non sia un&#39;aspirante &quot;politicante&quot;, scendi pure 


 wrote:
 
[... cut all ...]
 
No, no, cose come disaccoppiamento, information hiding, e simili sono
per fighetti incapaci di programmare veramente. Il real programmer non
ha paura di switch innestati, non si spaventa per poche decine di
livelli di decisione, l'unica cosa che lo mette un po' in crisi e`
decodificare i messaggi del compilatore.
 
E poi anche tu "non hai la minima idea di cosa faccia l'applicazione".
 
Non siamo i soli, credo.
 
Saluti
Paolo Arosio

Azz, Marco!
Prp tu che sei forse il meno aggressivo ed il piu' pacato del ng vieni attaccato a fucilate.
Di solito risposte cosi' pungenti arrivano a me...  :-D
 
A.