我在一个PageControl,然后再在上面动态建立TableSheet,再建立窗体与TableSheet之上,代码如下
申明:
frmProgramFile : TfrmProgramFile;
pgWinRect: TPageControl;
住窗体创建子窗体的代码:
procedure TfrmMain.btnSendTaskClick(Sender: TObject);
var
tab : TTabSheet;
begin
if findForm('frmProgramFile')=false then
begin
try
tab := TTabSheet.Create(pgWinRect);
tab.PageControl := pgWinRect;
tab.Show;
frmProgramFile := TfrmProgramFile.Create(self);
frmProgramFile.Parent := tab;
frmProgramFile.Align := alClient;
frmProgramFile.BorderStyle := bsNone;
frmProgramFile.Show;
tab.Caption := frmProgramFile.Caption;
except
//....
end;
end;
子窗体想要关闭父控件,也就是上一级的TableSheet
procedure TfrmProgramFile.SpeedButton4Click(Sender: TObject);
begin
{ ClearWindows;
self.Parent.Visible := FALSE;
self.Close;
self.Parent.Free;}
或者:
frmProgramFile.Parent.Free;
end;
上面注释掉的地方也试过,的确能关闭父控件,但关闭之后,必报内存错误,哪位兄弟给指点一下?
申明:
frmProgramFile : TfrmProgramFile;
pgWinRect: TPageControl;
住窗体创建子窗体的代码:
procedure TfrmMain.btnSendTaskClick(Sender: TObject);
var
tab : TTabSheet;
begin
if findForm('frmProgramFile')=false then
begin
try
tab := TTabSheet.Create(pgWinRect);
tab.PageControl := pgWinRect;
tab.Show;
frmProgramFile := TfrmProgramFile.Create(self);
frmProgramFile.Parent := tab;
frmProgramFile.Align := alClient;
frmProgramFile.BorderStyle := bsNone;
frmProgramFile.Show;
tab.Caption := frmProgramFile.Caption;
except
//....
end;
end;
子窗体想要关闭父控件,也就是上一级的TableSheet
procedure TfrmProgramFile.SpeedButton4Click(Sender: TObject);
begin
{ ClearWindows;
self.Parent.Visible := FALSE;
self.Close;
self.Parent.Free;}
或者:
frmProgramFile.Parent.Free;
end;
上面注释掉的地方也试过,的确能关闭父控件,但关闭之后,必报内存错误,哪位兄弟给指点一下?
解决方案 »
- 如何获得指定进程的句柄?
- 如何实现treeview中数据的快速查询
- dxdbgrid-按安回车自动换行时,如何设置可以自动增加记录的呢
- 星战前传3-西斯的复仇[DVD中文字幕]
- 两个dxcurrencyedit中的数据如何相加啊,加结果在第三个dxcurrencyedit中显示
- 如何用quickreport实现这样的主从报表:
- 关于B/S系统的设计,请大家来讨论讨论。
- 如何传参??
- 菜鸟问题:在Function中可以引用控件的值吗?
- 可不可以让StringGrid的不同cell响应不同的事件?有高分啊!!!!!
- 求救,求用delphi实现将sql server中的数据导入oracle的方法
- BDE连接ORACLE数据库的问题
...P := 父;
子窗.free;
TTabSheet(P).Free;
-----------------------------------------program Project1;uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2};{$R *.res}begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.------------------------------------------unit Unit1;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;type
TForm1 = class(TForm) //Tform1这是主窗体类
Button1: TButton; //在PageControl上创建Tab_sheet和Frm的按钮
FrmOwner_pageconer: TPageControl;
Button2: TButton; //从外部调用销毁动态创建的对象
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;var
Form1: TForm1;implementationuses Unit2; //引用Unit2,以便使用TForm2窗体类{$R *.dfm}var
Frm:TForm2;//下面这个过程,是复制以前的一段代码,作者非我
procedure TForm1.Button1Click(Sender: TObject); //动态创建tSheet和Frm
var
tSheet:TTabSheet;
begin
Frm:=TForm2.Create(self);
frm.Name:='abc';
tSheet:=TtabSheet.Create(FrmOwner_pageconer);
tSheet.Name:='Sheet_'+Frm.name;
tSheet.PageControl := FrmOwner_pageconer;
FrmOwner_pageconer.ActivePage:= tSheet;
tSheet.Visible:=true;
Frm.parent := tSheet;
Frm.Show();
end;//这段代码是我添加的,在创建的frm外,释放frm及其父
procedure TForm1.Button2Click(Sender: TObject);
var
P : pointer;
begin
P := frm.Parent;
frm.Destroy;
TTabSheet(P).Free;
end;end.---------------------------------------unit Unit2;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;type
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;var
Form2: TForm2;implementation{$R *.dfm}//这段代码是我添加的,在frm的一个按钮单击事件里,释放frm及其父
procedure TForm2.Button1Click(Sender: TObject);
var
P : pointer;
begin
P := self.Parent;
self.Free;
TTabSheet(P).Free;
end;end.
TFormInfo = class
private
FFormClass:TFormClass;
FForm:TForm;
FormName:string;
public
procedure CreateForm;
procedure DestroyForm;
procedure Show(AParent:TWinControl);
end; TFormManager = class
FFormList:TList;
public
procedure RegisterForm(const AName:string;AfrmClass:TFormClass);
function GetFormByName(const AName:string):TFormInfo;
property Items[Index:Integer]:TFormInfo read GetItem; default;
end;大概思路就是这样,如果将TForm类换成TFrame更合适一些,可参考:
http://blog.csdn.net/aiirii/archive/2005/02/14/287580.aspx
WM_DELETEOBJECT = WM_USER + 10;type
TForm1 = class(TForm)
//...
procedure WMDELETEOBJECT(var Msg: TMessage); message WM_DELETEOBJECT;
end;//...procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
PostMessage(Handle, WM_DELETEOBJECT, Integer(SpeedButton1.Parent), 0);
end;procedure TForm1.WMDELETEOBJECT(var Msg: TMessage);
begin
if Msg.WParam <> 0 then TObject(Msg.WParam).Free;
end;
var
tmp: TWinControl
begin
ClearWindows;
tmp := self.Parent;
tmp.Visible := FALSE;
self.Parent := nil;
self.Close;
tmp.Free;
end;
这里有个问题,那就是,在父窗体被释放的时候,他会在适当的时候释放其包含的子窗体。
因此,在释放你动态创建的窗体之前,先将其父窗口保存起来,然后在将其父窗体属性置空。
这样就能保证你的form和tab之间没有了关联关系。那么,在释放的时候,就不会导致被重复释放
//关键的原因wxieyang分析得对
procedure TForm1.Button1Click(Sender: TObject);
var
vParent: TWinControl;
begin
vParent := TControl(Sender).Parent;
TControl(Sender).Parent := nil;
vParent.Free;
TObject(Sender).Free;
end;
var
tmp: TWinControl;
begin
tmp := self.Parent;
tmp.Visible := FALSE;
self.Parent := nil;
self.Close;
tmp.Free;
end;
procedure TFrmTest.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FrmTest:=nil;
end;
Self是FrmTest的实例不是按钮
我的习惯是调试成功才贴代码
我提供了两种方案:以下你的调试代码是怎样的?procedure TForm1.Button1Click(Sender: TObject);
var
vParent: TWinControl;
begin
vParent := TControl(Sender).Parent;
TControl(Sender).Parent := nil;
vParent.Free;
TObject(Sender).Free;
end;以下你的调试代码是怎样的?
const
WM_DELETEOBJECT = WM_USER + 10;type
TForm1 = class(TForm)
//...
procedure WMDELETEOBJECT(var Msg: TMessage); message WM_DELETEOBJECT;
end;//...procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
PostMessage(Handle, WM_DELETEOBJECT, Integer(SpeedButton1.Parent), 0);
end;procedure TForm1.WMDELETEOBJECT(var Msg: TMessage);
begin
if Msg.WParam <> 0 then TObject(Msg.WParam).Free;
end;
最简单的代码,Button.OnClick事件中释放Button就会出错你的代码中,释放parent时,parent会释放它所拥有的所有子控件,即是间接的"在对象的事件中释放自身",违背了这个原则.解决方法可以是,PostMessage到父窗口,让父窗口释放TabSheet,TabSheet会自动释放它拥有的子窗口
是一关闭父控件就报错还是退出TfrmMain时才报错?楼主要弄清楚Owner和Parent的区别:TComponent.Create(Owner),这OWNER表示这创建后的组件的释放是由Owner来管理,而TControl.Parent是管理控件显示层次。所以,你可以试一下把"tab := TTabSheet.Create(pgWinRect);"改为“tab := TTabSheet.Create(nil);”那么你在子窗体里释放tab就应该行。另外,还要把“rmProgramFile := TfrmProgramFile.Create(self); ”改为“Application.CreateForm(TfrmProgramFile,rmProgramFile) ;”
窗体创建不同普通类的创建。
var
w:TWinControl;
begin
w := Parent;
Self.Visible := False;
Self.Parent := nil;
w.Free;
end;
unit Unit1;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;const WM_CLOSE_TAB = WM_USER + 100;type
TForm1 = class(TForm)
PageControl1: TPageControl;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
procedure CloseTab(var msg: TMessage); message WM_CLOSE_TAB;
public
{ Public declarations }
end;var
Form1: TForm1;implementationuses Unit2;{$R *.dfm}procedure TForm1.CloseTab(var msg: TMessage);
var
i: Integer;
begin
if CloseWindow(msg.WParam) then
for i := (PageControl1.PageCount - 1) downto 0 do
if (PageControl1.Pages[i].Handle = msg.LParam) then
begin
PageControl1.Pages[i].Free;
Break;
end;
end;procedure TForm1.Button1Click(Sender: TObject);
var
tab: TTabSheet;
begin
tab := TTabSheet.Create(PageControl1);
tab.Caption := 'test';
tab.PageControl := PageControl1;
tab.Show; Form2 := TForm2.Create(tab);
Form2.Parent := tab;
Form2.Align := alClient;
Form2.Show;
end;end.//////////////////////////子窗口///////////////////////
uses Unit1;{$R *.dfm}procedure TForm2.Button1Click(Sender: TObject);
begin
PostMessage(Form1.Handle, WM_CLOSE_TAB, Self.Handle, Self.Parent.Handle);
end;
会报内存错误
所以,你可以试一下把"tab := TTabSheet.Create(pgWinRect);"改为“tab := TTabSheet.Create(nil);”那么你在子窗体里释放tab就应该行。
以上,正解。
Parent是控件所在容器控件,而Owner是拥有此组件的组件(控件)。
如果Parent和Owner是两个不同的对象,那么在控件事件里面Free掉Parent是没有问题的;问题是,很多情况下不是这样的,往往Parent和Owner是同一个对象。
这种情况下要解决这个问题,就只有一个办法:用代码创建控件,创建时指定其Owner为nil,然后再指定其Parent。