我在一个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;   
    
  上面注释掉的地方也试过,的确能关闭父控件,但关闭之后,必报内存错误,哪位兄弟给指点一下?

解决方案 »

  1.   

    这么写肯定问题多多,不如TList管理TTabSheet,从子窗体外部释放。
      

  2.   

    呵呵,用指针变量,先把父对象保存起来,free掉子窗体后,再free指针对象。var P : Pointer;
    ...P := 父;
    子窗.free;
    TTabSheet(P).Free;
      

  3.   

    这是我的试验代码,可能很粗糙,很雍肿,不过,能达到效果就行了。
    -----------------------------------------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.
      

  4.   

    这里大家没有发现问题的根本,问题的根本就是这个创建的子窗体是基于一个局部的变量,在它释放的时候是无法通过自身完成的,如果是通过 Application.CreateForm(TFORM,FORM)来创建,我想就可以free掉,而且Free代码不需要那么写,直接将self.parent.free 就可以了,因为删除父类,其所有成员都会被删除!
      

  5.   

    create()创建函数 如果里面的参数是父级控件 那么在父类释放时 也就自动释放了不再需要手动释放 如果没有指定Parent那么需要你手工释放
      

  6.   

    设计的问题,可以创建专门的窗体管理类来统一管理子窗体的创建和释放如:
      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
      

  7.   

    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;
      

  8.   

    将释放部分改成如下形式:
    var
      tmp: TWinControl
    begin
          ClearWindows;   
          tmp := self.Parent;
          tmp.Visible   :=   FALSE;   
          self.Parent := nil;
          self.Close;   
          tmp.Free;
    end;
    这里有个问题,那就是,在父窗体被释放的时候,他会在适当的时候释放其包含的子窗体。
    因此,在释放你动态创建的窗体之前,先将其父窗口保存起来,然后在将其父窗体属性置空。
    这样就能保证你的form和tab之间没有了关联关系。那么,在释放的时候,就不会导致被重复释放
      

  9.   

    //这样也可以
    //关键的原因wxieyang分析得对
    procedure TForm1.Button1Click(Sender: TObject);
    var
      vParent: TWinControl;
    begin
      vParent := TControl(Sender).Parent;
      TControl(Sender).Parent := nil;
      vParent.Free;
      TObject(Sender).Free;
    end;
      

  10.   

    我试了wxieyang(斜阳) 和zswang(伴水清清)(专家门诊清洁工)两位朋友的代码还是会弹出内存错误窗口.procedure TFrmTest.SpeedButton5Click(Sender: TObject);
    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;
      

  11.   

    为什么不去试试将创建方式改为application.CreateForm方式来创建呢 这样你不会删除不掉了
      

  12.   

    ...
    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;
      

  13.   

    不要试图在对象的事件中释放自身
    最简单的代码,Button.OnClick事件中释放Button就会出错你的代码中,释放parent时,parent会释放它所拥有的所有子控件,即是间接的"在对象的事件中释放自身",违背了这个原则.解决方法可以是,PostMessage到父窗口,让父窗口释放TabSheet,TabSheet会自动释放它拥有的子窗口
      

  14.   

    你想释放啊,那就用消息吧,记得用POSTMESSAGE就可以了
      

  15.   

    楼主:
    是一关闭父控件就报错还是退出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) ;”
    窗体创建不同普通类的创建。
      

  16.   

    我完整测试过application.createForm来创建出来的窗体不会出现Free不掉的情况!为什么楼主不去试试看呢
      

  17.   

    谢谢大家,刚才按hawk_e2e(hawk_e2e) 的方法测试成功.我再试一下zswang(伴水清清)(专家门诊清洁工)的第二种方案.
      

  18.   

    突发奇想。是不是可以用SendMessage.在Child Destroy的时候,向Parent Send一个Message,Parent收到这个消息之后,调用一个循环,判断当这个Child = nil 的时候,就把自己释放掉。有兴趣可以加我讨论.QQ:35732910
      

  19.   

    伴水晕了....那个谁,快去人工呼吸.听说,postmessage是异步的,sendmessage是同步的...
      

  20.   

    用PostMessage发送消息给祖先窗体。由它来安排什么该先Free,什么该后Free
      

  21.   

    procedure   TfrmProgramFile.SpeedButton4Click(Sender:   TObject);   
    var
    w:TWinControl;
    begin
      w := Parent;
      Self.Visible := False;
      Self.Parent := nil;
      w.Free;
    end;
      

  22.   

    winxkm说的有点卓见,你在局部创建后,当这段代码运行完毕后会自动释放TAB,如果你在释放一次的吧,会认为多次释放,所以会报一个错误。
      

  23.   

    这个程序的关键在于TfrmProgramFile.Create(self),你去查查CREATE,如果你create的参数时父窗体,那么就可以直接销毁了
      

  24.   

    //主窗口
    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;
      

  25.   

    因为这时正在和系统API通讯暂时无法断开连接
    会报内存错误
      

  26.   

    hawk_e2e(hawk_e2e) ( ) 信誉:100  
    所以,你可以试一下把"tab := TTabSheet.Create(pgWinRect);"改为“tab := TTabSheet.Create(nil);”那么你在子窗体里释放tab就应该行。
      
     以上,正解。
      

  27.   

    要区分Owner和Parent。
    Parent是控件所在容器控件,而Owner是拥有此组件的组件(控件)。
    如果Parent和Owner是两个不同的对象,那么在控件事件里面Free掉Parent是没有问题的;问题是,很多情况下不是这样的,往往Parent和Owner是同一个对象。
    这种情况下要解决这个问题,就只有一个办法:用代码创建控件,创建时指定其Owner为nil,然后再指定其Parent。