Newsgroup: it.comp.lang.delphi
Titolo: FormShow da Form modali?

Pazientate per la mia scarsa preparazione.
Mi sto scontrando con qualcosa che forse non si può fare.
Dal MainForm MDI apro una Child, creata a Run-Time, che a sua volta mezzo un
ToolButton apre una Form modale. Quest'ultima, se l'utente clicca sul button
'Calcola' esegue un'operazione piuttosto lunga e avrei voluto utilizzare
un'altra Form sempre modale, richiamata da 'Calcola', con caption 'In
elaborazione', BorderStyle bsDialog e una ProgressBar.
Il problema si manifesta al termine dell'operazione, alla chiusura di
quest'ultima Form, con Delphi che mi restituisce un Access Violation.
Ho provato anche a togliere i modali ma nulla di fatto.
Può dipendere dal fatto che la Form chiamante principale è una Child?
Mi rendo conto che senza codice è difficile venirne a capo ma potete provare
a darmi qualche suggerimento?
Grazie
Valter



















 provare a darmi qualche suggerimento?
ripulisci il codice da tutte le parti superflue e posta qui solo quelle che 
riguardano la creazione della form a run-time e della form secondaria 
modale.
-- 
Mauro Falzari 
 
 "Se guardi nell'abisso, anche l'abisso guarda in te." 
   (Friedrich Nietzsche)

 elaborazione', BorderStyle bsDialog e una ProgressBar.
[CUT]
Puoi sapere quanto tempo prende l'operazione?
Se e' un'operazione legata a dei record in un database puoi sapere 
quanti sono i record coinvolti?
Se la risposta a entrambe le domande e' si' questo e' il mio consiglio.
- metti una ProgressBar invisibile nella form dove hai il button "Calcola"
- dopo che l'utente ha premuto "Calcola" ne setti la proprieta' Max al 
tempo massimo da impiegare (in secondi) o al numero di record da elaborare
- rendi visibile la ProgressBar e disabilitato il button "Calcola"
- esegui la luuuuuuuuuuuuuunga procedura che, ovviamente, aggiornera' la 
Position della ProgressBar e ne fara' il Refresh (ti consiglio di fare 
il refresh della ProgressBar ogni volta che Position e' divisibile per 5 
e di tutto il form ogni volta che e' divisibile per 10)
- alla fine rendi invisibile la ProgressBar, riabiliti "Calcola" e 
mostri un bel MessageBox "Utente! Ho finito."
- come l'utente preme OK per confermare che ha visto il MessageBox (non 
ho detto "letto", quella e' fantascienza) chiudi la form modale.

 modale.
Sto facendo dei cas.... inenarrabili...ho postato 'fuori'...
comunque
Questo è il codice 'essenziale'.
Quello che mi premerebbe sapere è se le chiamate che eseguo in sequenza ai
vari Form sono consentite.
Questo è il click sul MainMenu della Form MDI che richiama la Child
procedure TfrmMain.Archivi1Click(Sender: TObject);
begin
  DM.ArchSE.Open;
  frmVediArch:= TfrmVediArchSE.Create(Self);
  frmVediArch.Refresh;
  frmVediArch.Show;
end;
Nella child tra i vari componenti c'e una DBGrid.
Per modificare i dati contenuti nella grid clicco sul ToolButton Modify che
richiama un'altra form dove sono i campi relativi al record selezionato.
Prima di renderla visibile cambio alcune proprietà
procedure TfrmVediArch.TB_btnModifyClick(Sender: TObject);
var
  [cut]
begin
  with frmVediArchedit do
  begin
    gbVediArchSedit.Caption := 'Modifica estrazione';
    sbFormGestisciEstr.SimpleText := ' [Modifica]';
    with DM.ArchSE do
    begin
      [cut]
    end; // end with DM.Arch
    ShowModal;
  end; // with frmVediArchedit
end;
In questa form modifico i dati e clicco su Update. Dato che la modifica di
un solo record richiede poi una modifica di tutti i campi degli altri record
l'operazione richiede circa un minuto e contavo di mostrare un'altra form
con Caption := 'In elaborazione', BorderStyle := bsDialog ed una ProgressBar
procedure TfrmVediArchedit.tbtnUpdateClick(Sender: TObject);
var
  [cut]
begin
  [cut]
  // Inserisco i dati modificati nella tabella
  frmAggDB.ShowModal; // qui eseguo la chiamata alla form con la ProgressBar
  frmVediArchSEedit.Close;
end;
procedure TfrmAggDB.FormShow(Sender: TObject);
begin
  AllineaDB;
end;
procedure TfrmAggDB.AllineaDB;
begin
  // qui eseguo tutte le operazioni richieste mentre la ProgressBar procede
  // chiamata a varie procedure presenti nella stessa unit
  frmAggDB.Close
end;
Fino a questo punto tutto procede al meglio ma appena si esegue il
frmAggDB.Close ottengo un Access Violation.
Subito dopo la chiamata frmAggDB.Close si incontra frmVediArchEdit.Close e a
questo punto dovrei ritrovarmi sulla Child chiamante. Ma qualcosa va storto.
In questo momento ho modificato il tutto come suggerito da warp10 e tutto
procede al meglio quindi l'errore non dovrebbe dipendere dal codice
restante.
Avete idee?
Valter

Un dettaglio che può confondere:
frmArchSE e frmArch sono la stessa cosa...un pò ho tagliato, un pò ho
riscritto e ho dimenticato SE...idem per frmVediArchSEedit e DM.ArchSE...

   ProgressBar frmVediArchSEedit.Close;
Non ho capito molto bene dove crei le form, nel tuo codice, comunque se ti 
può essere di qualche aiuto per le form secondarie (modali o non modali) in 
genere si può adottare questo sistema, che butto giù rapidamente:
1) rimuovere la riga che le crea automaticamente all'avvio 
dell'applicazione, cioè eliminare dal file DPR cose del tipo
    Application.CreateForm(TForm2, Form2);
2) se il codice di default della form secondaria è
unit Unit2;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, 
Forms,
  Dialogs;
type
  TForm2 = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form2: TForm2;
implementation
{$R *.dfm}
end.
3) si può trasformarlo in questo, per mostrarla come modale:
unit Unit2;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, 
Forms,
  Dialogs;
type
  TForm2 = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;
function ExecuteModal: Integer;
implementation
{$R *.dfm}
var
  Form2: TForm2;
function ExecuteModal: Integer;
begin
  if not Assigned(Form2) then
    Form2 := TForm2.Create(nil);
  try
    Result := Form2.ShowModal;
  finally
    FreeAndNil(Form2)
  end
end;
end.
4) oppure questo per mostrarla come NON modale (non più di una istanza di 
Fomr2 alla volta):
unit Unit2;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, 
Forms,
  Dialogs;
type
  TForm2 = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
procedure Execute;
implementation
{$R *.dfm}
var
  Form2: TForm2;
procedure Execute;
begin
  if not Assigned(Form2) then
    Form2 := TForm2.Create(nil);
  Form2.Show;
end;
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  Form2 := nil;
end;
end.


 questa (frmVediArchSEedit) dove l'hai creata?
Non la creo, come non creo frmAggDB. Le ho lasciate nell'Autocreate Forms
delle Opzioni del Progetto. Invece frmVediArch, che è una Child, l'ho
eliminata e la ricreo a Run-Time. Se non è corretto dimmelo...
Quello che ti vorrei chiedere è, riepilogando brevemente, se le chiamate
seguenti sono consentite...insomma se nella logica delle Forms è possibile
implementare una struttura del genere:
Da una MDIForm richiamare una Child (creata a Run-Time) la quale richiama
una Form Modale che richiama a sua volta un'altra Form Modale (queste ultime
due Form autocreate al lancio dell'applicazione)...quindi chiudere in
sequenza le due form Modali per tornare alla Child.
frmMain (MDI) --> frmVediArch.Create(Self) --> frmVediArchEdit.ShowModal -->
frmAggDB.ShowModal
Fino a qui arrivo ma appena chiudo le Form Modali ottengo un Access
Violation.
Se tu mi dici che la sequenza è possibile cercherò di capire dov'è l'errore,
magari predisponendo un piccolo progetto privo di tutto il codice e con le
chiamate essenziali...
Grazie
Ciao
Valter

 chiamate essenziali...
Ti posso confermare che il mini progetto non restituisce alcun errore...
quindi
frm1 (MDI) --> frm2.Create(Self) (Child) -->
frm3.ShowModal -->frm4.ShowModal
funziona regolarmente come anche le rispettive chiusure.
Se hai da consigliarmi dell'ottimizzazione del codice è ben accetto ma mi sa
che mi tocca partire con il commentare le varie istruzioni fino a trovare
quella che causa l'errore.
Ciao
Valter

ok, ho risolto il problema inserendo un semplice Else...al "ritorno"
incontrava un'istruzione che non gli piaceva (comunque un mio errore).
Vorrei chiederti un'ultima cosa per concludere questo "ciclo".
Nel quarto ed ultimo Form che apro, quello con la ProgressBar, ho dovuto
mettere la chiamata alla Unit che processa tutto il database nell'evento
OnActivate in quanto nell'OnShow eseguiva tutta l'elaborazione ma senza
mostrarmi il form che appariva solo ad operazione conclusa.
Per chiudere in maniera automatica questo form (e tornare a quello chiamante
che a sua volta si chiude riportandomi al famoso Child) se utilizzo
Form4.Close in OnActivate l'istruzione non viene eseguita. (nemmeno in
Form4.Show).
Ho dunque messo un Timer ad un secondo che si attiva immediatamente dopo la
conclusione di tutte le operazioni (come ultima riga nell'evento
OnActivate).
Nell'evento OnTimer un semplice Form4.close che funziona immagino io perchč
si conclude l'evento OnActivate:
C'č un modo piů "pulito" per uscire dal form4 senza l'intervento
dell'utente?
Grazie per l'attenzione che mi hai prestato.
Ciao
Valter

 nell'evento OnActivate).
per ovviare a questo inconventiente prova a usare 
Application.ProcessMessages durante le operazioni "lunghe", magari non a 
ogni ciclo ma ogni tot:
if I mod 5 = 0 then 
  Application.ProcessMessages
Per ovviare al fatto che nell'OnShow non viene visualizzata tutta la form, 
puoi provare a usare:
- Invalidate
- Application.ProcessMessages 
- Sleep(1)
