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 completelycaMinimize
- Minimize the formcaHide
- Hide the formcaNone
- 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.
精彩评论