Delphi TFrame的创建和销毁

5

如何在主TForm上创建(显示)和销毁(隐藏)框架? 框架的对齐方式为alClient。

我尝试了这个:

表单:

unit main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, uFrame1, uFrame2;

type
  TFormMain = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    f1: TFrame1;
    f2: TFrame2;
  end;

var
  FormMain: TFormMain;

implementation

{$R *.dfm}

procedure TFormMain.FormCreate(Sender: TObject);
begin
  f1 := TFrame1.Create(Self);
  f1.Parent := Self;
end;

end.

第一帧:

unit uFrame1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type
  TFrame1 = class(TFrame)
    btn1: TButton;
    procedure btn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

implementation

{$R *.dfm}

uses main, uFrame2;

procedure TFrame1.btn1Click(Sender: TObject);
begin
  Self.Free;
  FormMain.f2 := TFrame2.Create(FormMain);
  FormMain.f2.Parent := FormMain;
end;

end.

第二个框架:

unit uFrame2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type
  TFrame2 = class(TFrame)
    lbl1: TLabel;
    btn1: TButton;
    procedure btn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

implementation

{$R *.dfm}

uses main, uFrame1;

procedure TFrame2.btn1Click(Sender: TObject);
begin
  Self.Free;
  FormMain.f1 := TFrame1.Create(FormMain);
  FormMain.f1.Parent := FormMain;
end;

end.

但是当我在FrameStart或Frame1上单击按钮时,它会崩溃并显示访问冲突(Access Violation)错误(TForm FormCreate工作正常,即创建并显示FrameStart)。
使用Delphi 7。
带有第一个框架的图片和第二个框架的图片。

4
Self.Free? 0_o,我不确定VCL的事件处理代码是否能够处理这个 - 对于窗体而言,例如https://dev59.com/dnRB5IYBdhLWcg3wJUau - Boris Treukhov
1
自由使用self是可以的,但在调用后不要对self进行任何操作,否则可能会出现访问冲突等问题。 - Tony Hopkinson
1
还是有点不确定...即使 AV 没有被捕获,也不意味着没有 AV 存在。 https://dev59.com/C1XTa4cB1Zd3GeqP36-I https://dev59.com/rVLTa4cB1Zd3GeqPZEpd https://dev59.com/xXE95IYBdhLWcg3wApBU - Boris Treukhov
4
@Tony 不,Self.Free 绝对不好。 - David Heffernan
2
@TonyHopkinson:... TFrame没有Release()方法,但是可以很容易地模仿 - 使用PostMessage(Self.Handle, CM_RELEASE, 0, 0)代替Self.Free,然后在TFrame.WndProc()方法处理CM_RELEASE消息时调用Self.Free - Remy Lebeau
显示剩余7条评论
2个回答

9

在这些事件处理程序中,您不能调用Self.Free。当事件处理程序返回时,执行的VCL代码仍然使用您刚释放的对象的引用。这就是访问冲突的来源。如果您一直在完全调试模式下运行FastMM,那么您将看到一个有用的诊断消息。

这些框架将不得不以更加迂回的方式自我销毁。向框架发布一个CM_RELEASE消息,请求其调用Free来释放该框架。您发布而不是发送消息,以便先处理所有正在进行的消息。您需要添加一个消息处理程序来响应该消息。


2
并且Boris的链接向您展示了一些例子... - Whiler
没错!然而,我无法使用问题中的代码(至少在Delphi 2009中)重现AV。 - TLama
1
@tlama 这就是这种 AV 类型的本质。以快速 MM 全调试模式运行,你会看到一些动作。这个错误是经典中的经典。 - David Heffernan
我知道我永远不应该试图砍掉一根树枝,我只是想知道为什么这么安静。 - TLama
一切取决于实例所引用的self是否调用了任何方法,以及在self指向的内存被用于其他用途之前是否已经使用。这是一个不要这样做的情况,如果你这样做了,当你的代码立即崩溃时,请感到高兴。 - Tony Hopkinson
1
谢谢 :) 这个答案和@Whiler的https://dev59.com/xXE95IYBdhLWcg3wApBU#2502613链接对我很有帮助。 - Alex P.

4

你已经有了一些基础。

这种东西背后的基本思想。

在主窗体中添加一个私有属性来保存框架。

在按钮单击处理程序中,假设您只想同时使用一个,请执行以下操作

if assigned(fMyFrame) then
begin
  fMyFrame.Free;
  fMyFrame := nil;
end;
fMyFrame := TSomeFrame.Create(self);
fMyFrame.Parent := self;
fMyFrame.blah...

当您只是想摆脱它而不是替换它时

if assigned(fMyFrame) then
begin
  fMyFrame.Free;
  fMyFrame := nil;
end;

如果你想让一个框架提升另一个框架,请在那里重复上述操作。

如果你想让提升的框架成为框架中的兄弟节点,例如具有相同的父级,则不要使用 Form1 变量。

fMyNextFrame.Parent = self.Parent;

一旦您把它搞定了,有很多方法可以进一步改进它,这是界面和/或继承的经典场景,但先解决这一部分。

mySomething := TMySomething.Create();

现在你可以使用某个东西做一些事情了。 在你调用free之后,它不是不能,而是不要,并且也不要让其他任何东西这样做。

不要使用self.free,这就像在油桶中玩火柴一样危险。会受伤的...


这样做不行。你需要将事件处理程序移动到表单方法才能使其工作。而这又违背了使用框架的目的。 - David Heffernan
同意,但是必须有一些东西来管理对框架的引用,而这正是需要管理其生命周期的东西。我试图传达管理方面的内容,应该选择其他示例而不是框架。 - Tony Hopkinson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接