Author: Tomas Rutkauskas
I am using a TPageControl and show some forms on its pages. So, whenever I want to
show a form I create a new page on the TPageControl for that form and then displat
the form in that page. Now I want free that page when the user closes the form
sitting on it. I tried using the form's OnClose or OnDestroy events to free the
parent tabsheet of the form but I get an access violation.
Answer:
Solve 1:
It is difficult enough to destroy a control from an event handler of that control,
trying to destroy its parent adds even more problems to that. The best way to
handle this is to leave the destruction of the tabsheet to a neutral 3rd party, in
this case the form holding the pagecontrol. In the embedded forms OnClose you post
(via postmessage) a custom message to the form holding the pagecontrol and then
hide the embedded form. The host form then destroys the tabsheet and that also
destroys the embedded form. Posting the message delays the action long enough to
allow any code in the embedded form to complete safely. Example:
1 {Unit for the embedded form}
2
3 unit Unit2;
4
5 interface
6
7 uses
8 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
9 Dialogs,
10 StdCtrls;
11
12 const
13 UM_KILLCONTROL = WM_USER + 666;
14 type
15 TUMKillControl = record
16 msg: Cardinal;
17 control: TControl;
18 unused: LPARAM;
19 result: LRESULT;
20 end;
21 type
22 TForm2 = class(TForm)
23 Button1: TButton;
24 procedure Button1Click(Sender: TObject);
25 procedure FormClose(Sender: TObject; var Action: TCloseAction);
26 private
27 public
28 end;
29
30 implementation
31
32 {$R *.dfm}
33
34 procedure TForm2.Button1Click(Sender: TObject);
35 begin
36 close
37 end;
38
39 procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
40 begin
41 action := caHide;
42 PostMessage(GetParentForm(self).Handle, UM_KILLCONTROL, Integer(parent), 0);
43 end;
44
45 end.
46
47 {Unit for the host form}
48
49 unit Unit1;
50
51 interface
52
53 uses
54 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
55 ComCtrls, StdCtrls, unit2;
56
57 type
58 TForm1 = class(TForm)
59 StatusBar: TStatusBar;
60 Button1: TButton;
61 PageControl1: TPageControl;
62 procedure Button1Click(Sender: TObject);
63 private
64 { Private declarations }
65 procedure UMKillControl(var msg: TUMKillControl); message UM_KILLCONTROL;
66 public
67 { Public declarations }
68 end;
69
70 var
71 Form1: TForm1;
72
73 implementation
74
75 {$R *.DFM}
76
77 procedure TForm1.Button1Click(Sender: TObject);
78 var
79 tab: TTabSheet;
80 begin
81 tab := TTabSheet.Create(self);
82 tab.PageControl := pagecontrol1;
83 with TForm2.create(self) do
84 begin
85 borderstyle := bsNone;
86 parent := tab;
87 tab.caption := caption;
88 align := alclient;
89 show;
90 end;
91 end;
92
93 procedure TForm1.UMKillControl(var msg: TUMKillControl);
94 begin
95 msg.control.Free;
96 end;
97
98 end.
Solve 2:
As long as the child form is not "owned" by the tabsheet on which it is parented
(being owned by the form which owns the PageControl is OK), you can do this:
99 { ... }
100 type
101 TfNastyChild = class(TForm)
102 procedure FormClose(Sender: TObject; var Action: TCloseAction);
103 private
104 { Private declarations }
105 fKillParent: TWinControl;
106 public
107 { Public declarations }
108 destructor Destroy; override;
109 end;
110
111 implementation
112
113 {$R *.dfm}
114
115 procedure TfNastyChild.FormClose(Sender: TObject; var Action: TCloseAction);
116 begin
117 if (Parent is TTabsheet) and (Owner <> Parent) then
118 begin
119 Hide;
120 fKillParent := Parent;
121 Parent := nil;
122 end;
123 action := caFree;
124 end;
125
126 destructor TfNastyChild.Destroy;
127 begin
128 if assigned(fKillParent) and not (csDestroying in fKillParent.ComponentState) then
129 fKillParent.Free;
130 inherited;
131 end;
|