2010-01-15 7 views
1

Cela a été discuté ici, mais pas dans ce détail.Comment fermer le formulaire non-modal dans Delphi

Je rencontre des problèmes lorsque j'essaie de fermer un sous-formulaire non modal. Je l'ai aviser le parent, mais je reçois des erreurs abstraites et d'autres exceptions. Qu'est-ce que je fais mal? Est-ce que le parent doit libérer le formulaire non-modal, ou simplement ne jamais essayer d'y accéder à travers cette variable?

forme principale:

NonModal := NonModalTForm.Create(Self); 
NonModal.Callback := Callback; 
NonModal.Show; 

Procedure TForm.Callback; // called by non-modal form when closing 
begin 
    FreeAndNil(NonModal); // or should this just be NonModal := nil so I don't try to access a dangling pointer? 
end; 

Dans NonModal.pas

procedure NonModalTForm.FormClose; 
begin 
    Callback; // calls parent 
end; 
+5

Votre code est l'équivalent de la programmation de couper la branche sur laquelle vous êtes assis. –

Répondre

6

Utilisez Cacher si vous souhaitez afficher la fenêtre plus tard.

Utilisez Fermer si vous souhaitez le fermer. (Fermeture de la fenêtre principale, ferme l'application). L'action exacte de Close dépend des paramètres du formulaire.

Voir la source de Fermer:

procedure TCustomForm.Close; 
var 
    CloseAction: TCloseAction; 
begin 
    if fsModal in FFormState then 
    ModalResult := mrCancel 
    else 
    if CloseQuery then begin 
     if FormStyle = fsMDIChild then 
     if biMinimize in BorderIcons then 
      CloseAction := caMinimize 
     else 
      CloseAction := caNone 
     else 
     CloseAction := caHide; 
     DoClose(CloseAction); 
     if CloseAction <> caNone then 
     if Application.MainForm = Self then 
      Application.Terminate 
     else if CloseAction = caHide then 
      Hide 
     else if CloseAction = caMinimize then 
      WindowState := wsMinimized 
     else 
      Release; 
    end; 
end; 

Mais attention, avec libre. Il peut y avoir quelques messages dans la file d'attente de Windows qui peuvent conduire à des plantages. Mieux utiliser Release pour nettoyer la fenêtre. Parce que cela attend les messages avant de le libérer.

4

Vous faites exactement ce que vous ne devriez pas faire.
Dans l'événement onClose de NonModalForm, vous appelez du code qui le libère sans le vouloir, alors qu'il se trouve toujours dans l'exécution des gestionnaires d'événements, ce qui fait que vous vous retrouvez avec un objet self qui n'est plus valide.
C'est le cas de l'affiche pour pourquoi utiliser Release au lieu de Free sur un formulaire. Comme l'a souligné Gamecat, il suffit d'appeler Fermer ...
La beauté de la VCL est souvent que c'est aussi simple que cela.

+0

>> Comme l'a fait remarquer Gamecat, il suffit d'appeler Fermer ... << Vous voulez appeler Close dans la procédure de rappel qui est elle-même appelée à partir du formulaire non-Modal lui-même? Cela ne déclenche-t-il pas un deuxième événement OnClose au formulaire non Modal? Ou êtes-vous en train de dire que la VCL est assez intelligente pour ne pas appeler le FormClose de non-Modal une seconde fois. – RobertFrank

+2

Votre fonction 'Callback' n'est pas pertinente pour cette discussion, Tom. Comme vous le constatez, vous l'appelez dans le gestionnaire d'événement 'OnClose' du formulaire. Cela signifie qu'au moment où la fonction de rappel est appelée, * votre formulaire est ** déjà ** fermé *, et donc toute votre question est sans intérêt: vous n'avez pas besoin d'apprendre comment fermer le formulaire parce que vous avez déjà accompli cela, même si vous ne savez pas comment vous l'avez géré. –

+0

Sans procédure de rappel, comment éviter une fuite de mémoire et/ou un pointeur dangling sur la partie du pointeur des parents vers la forme non-modale? Je dois libérer la mémoire pointée par NonModal ... et mettre NonModal à zéro alors je sais que ce n'est pas utilisé. La seule solution que je peux voir est encore une autre couche de logique: la forme non-modale, lors de la fermeture, appelle un rappel à parent, qui définit un drapeau "Forme non-modale fermée" à True. Plus tard (quand elle se ferme elle-même ou veut réutiliser la forme non-modale, elle vérifie ce drapeau et libère la mémoire.) Désolé de m'attarder là-dessus, mais j'essaie vraiment de comprendre cela sans – RobertFrank

10

Vous appelez close pour fermer votre formulaire d'un endroit autre que l'événement FormClose. En cas FormClose, vient de mettre en action égale à une des opérations suivantes:

  • caFree - mettre au rebut de la forme complètement
  • caMinimize - Réduire au minimum la forme
  • caHide - Masquer le formulaire
  • caNone - Ignorer la fin

par exemple:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); 
begin 
    Action := caFree; 
end; 
3

La VCL dispose déjà d'un mécanisme pour notifier les composants lorsque d'autres composants sont libérés. Vous pouvez l'utiliser de cette façon;

type 
    TfrmParent = class(TForm) 
    btnShowChild: TButton; 
    procedure btnShowChildClick(Sender: TObject); 
    private 
    FChild: TfrmChild; 
    public 
    procedure Notification(AComponent: TComponent; Operation: TOperation); override; 
    end; 


procedure TfrmParent.btnShowChildClick(Sender: TObject); 
begin 
    // Check status of child 
    if FChild = nil then 
    begin 
    // Child does not exist, create it 
    FChild:= TfrmChild.Create(Application); 
    FChild.Show; 

    // Ask Child to notify us when it is destroyed 
    FChild.FreeNotification(Self); 
    end 
    else 
    begin 
    // Child already exists 
    FChild.Show; 
    end; 
end; 

procedure TfrmParent.Notification(AComponent: TComponent; 
    Operation: TOperation); 
begin 
    inherited; 

    if (AComponent = FChild) and (Operation = opRemove) then 
    begin 
    // FChild is about to be freed, so set reference to Child to nil 
    FChild:= nil; 
    end; 
end; 

Après avoir créé le formulaire enfant, utilisez FreeNotification du formulaire créé la méthode pour vous inscrire pour recevoir une notification lorsque le formulaire enfant est détruit.

Pour réagir à la notification, remplacez la méthode de notification.Dans là, vous pouvez trouver quel composant est détruit et le comparer à la référence mémorisée au formulaire enfant. Lorsque vous recevez la notification, définissez simplement la référence au formulaire enfant sur zéro.

Dans l'enfant TfrmChild lui-même, vous n'avez rien d'autre à faire que skamradt: Il suffit de définir le paramètre Actionb sur caFree dans l'événement OnClose.

+4

+1. L'appel 'FreeNotification()' n'est même pas nécessaire lorsque 'Self' est passé à' TfrmChild.Create() 'au lieu de' Application' - la gestion de la propriété dans la VCL prend en charge les appels de notification. – mghie

+0

La seule décision de travail ici. Seulement, il définit la forme fermée à «nil». Bon travail. Je me demande pourquoi il y a moins de votes –

-2

Ne pas utiliser le callback

Il suffit d'appeler FreeAndNil(Self); pour libérer toutes les ressources de mémoire qui a été créé pour le formulaire. N'oubliez pas de libérer des objets créés par votre code d'implémentation. Les objets créés par le concepteur de formulaire sont très bien nettoyés par Delphi.

+0

Je ne pense pas que cette réponse complète les autres déjà données. Deuxièmement, comme [cette réponse] (http://stackoverflow.com/a/2075484/757830) (et en particulier [ce commentaire dessus] (http://stackoverflow.com/questions/2075405/how-to-close -non-modal-form-in-delphi # comment2006057_2075484)), libérant le formulaire dans son gestionnaire d'événements est vraiment un non-go. Désolé, -1. – NGLN

Questions connexes