开发者

How to close non-modal form in Delphi

开发者 https://www.devze.com 2022-12-16 08:37 出处:网络
This has been开发者_如何学编程 discussed here, but not in this detail. I\'m having trouble when trying to have a non-modal subform close itself. I have it notify the parent, but I\'m getting abstract

This has been开发者_如何学编程 discussed here, but not in this detail.

I'm having trouble when trying to have a non-modal subform close itself. I have it notify the parent, but I'm getting abstract errors and other exceptions. What am I doing wrong? Does the parent have to free the non-modal form, or just never try again to access it through that variable?

Main form:

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;

In NonModal.pas

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


You call close to close your form from someplace other than the FormClose event. In the FormClose event, just set Action equal to one of the following:

  • caFree - dispose of the form completely
  • caMinimize - Minimize the form
  • caHide - Hide the form
  • caNone - Ignore the close

for example:

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


The VCL already has a mechanism to notify components when other components are freed. You can use it this way;

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;

After creating the child form, use the created form's FreeNotification method to register yourself to receive a notification when the child form is destroyed.

To react to the notification, overwrite the Notification method. In there, you can find out which component is destroyed and compare it to the remembered reference to the child form. When you receive the notification, just set the reference to the child form to nil.

In the child TfrmChild itself you don't have to do anything else but what skamradt has written: Just set the parameter Actionb to caFree in the OnClose event.


Use Hide if you want to show the window later on.

Use Close if you want to close it. (Closing the main window, closes the application). The exact action of Close depends on the form parameters.

See the source of Close:

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;

But be careful with free. There can be some messages in the windows queue left which can lead to crashes. Better use Release to cleanup the window. Because this waits for the messages before freeing it.


You are exactly doing what you should not do.
In the event onClose of the NonModalForm, you call some code that is bluntly freeing it, while it is still inside the event handlers execution, so you end up with a self object that is not valid anymore.
That is the poster case for why to use Release instead of Free on a Form.

As Gamecat pointed it out, just simply call Close...
The beauty of the VCL is often that it is as simple as that.


Do Not use the callback

Just call FreeAndNil(Self); to release all memory resource that was created for the Form.

Remember to free objects that was created by your implementation code. The objects created by the form designer are very well cleaned by Delphi.

0

精彩评论

暂无评论...
验证码 换一张
取 消