我应该为多页表单使用delphi tframes吗?

我应该为多页表单使用delphi tframes吗?,delphi,tframe,Delphi,Tframe,我在我的应用程序中有一些表单,根据用户正在做什么,它们具有不同的“状态”;例如,当通过他的文件列表时,表单在网格中显示有关该文件的一些数据,但如果他单击某个按钮,网格将被与其相关的图形所取代。简单地说,表单中的控件取决于用户想要做什么 当然,最明显的方法是根据需要显示/隐藏控件,这对小数字来说很有吸引力,但一旦每个州达到10/15+个控件(或者超过3个州),它就不可用了 我现在正在尝试使用TFrames:我为每个状态创建一个框架,然后在窗体上的每个框架上创建一个实例,然后使用Visible只显示

我在我的应用程序中有一些表单,根据用户正在做什么,它们具有不同的“状态”;例如,当通过他的文件列表时,表单在网格中显示有关该文件的一些数据,但如果他单击某个按钮,网格将被与其相关的图形所取代。简单地说,表单中的控件取决于用户想要做什么

当然,最明显的方法是根据需要显示/隐藏控件,这对小数字来说很有吸引力,但一旦每个州达到10/15+个控件(或者超过3个州),它就不可用了

我现在正在尝试使用TFrames:我为每个状态创建一个框架,然后在窗体上的每个框架上创建一个实例,然后使用Visible只显示我想要的一个,而在它上面有一些控件,在任何框架之外,因为它们都共享它们

这是做我想做的事的正确方式,还是我在做的过程中错过了什么?我以为我只能创建一个tframe实例,然后选择要在其中显示的实例,但看起来不是这样


谢谢

看起来框架是这个场景的最佳选择。我想补充一点,您可以使用基本框架和视觉继承来创建公共接口


至于第二部分:你设计了一个像表单一样的框架,但是你使用它像控件一样,很少有限制。请注意,您可以同样轻松地使用Create/Free而不是Show/Hide。更好的方法取决于它们的资源量。

有一种更好的方法来处理不会占用那么多内存的帧。动态创建框架是一个非常优雅的解决方案。这是我过去的做法

在窗体上,添加一个属性和一个setter,用于处理该属性在窗体上的放置:

TMyForm = class(TForm)
private
  FCurrentFrame : TFrame;
  procedure SetCurrentFrame(Value : TFrame);
public
  property CurrentFrame : TFrame read FCurrentFrame write SetCurrentFrame;
end;

procedure TMyForm.SetCurrentFrame(Value : TFrame)
begin
  if Value <> FCurrentFrame then
  begin
    if assigned(FCurrentFrame) then
      FreeAndNil(FCurrentFrame);
    FCurrentFrame := Value;
    if assigned(FCurrentFrame) then
    begin
      FCurrentFrame.Parent := Self;  // Or, say a TPanel or other container!
      FCurrentFrame.Align := alClient;
    end;
  end;
end;
如果要删除帧,只需将
nil
指定给CurrentFrame属性:

MYFrame1.CurrentFrame := nil;

它工作得非常好。

我有一句话要告诉你:TFrameStack。这就是名字的含义

它有几种方法:PushFrame(AFrame)、PopFrame、poptoop(AFrame)、poptoop(Index), 和一些属性:StackTop;帧[索引:整数];计数

  • 应该是不言自明的
堆栈顶部的框架是可见的。当执行类似“上一帧/上一帧”的操作时,您不需要知道当前帧之前是什么帧:) 创建框架时,您可以创建并一键推送框架stack.push(TAFrame.create)等,创建框架时调用BeforeShow过程并使其可见,并在堆栈中返回其索引:)

但它在很大程度上依赖于从一个共同的祖先继承您的框架。这些框架(在我的例子中)都有程序:BeforeShow;自由之前;隐藏之前;在可见之前。 在push、pop和top期间,FrameStack对象会调用它们

您只需要从主窗体访问FrameStack.Stacktop.which。我将我的堆栈设置为全局:),因此可以很容易地从其他对话框/窗口等进行访问

另外,不要忘记在应用程序关闭时创建一个Free方法覆盖来释放堆栈中的所有帧(如果所有者为nil)——另一个好处是您不需要显式跟踪它们:)

创建TFrameStack列表对象只需少量工作。在我的应用程序中,工作就像做梦一样


Timbo

我还使用了@Tim Sullivan所描述的方法,并添加了一些内容。在每一帧中,我定义了帧初始化的过程——设置其组件的默认属性

TAnyFrame = class(TFrame)
  public
    function initFrame() : boolean; // returns FALSE if somesthing goes wrong
    ...
end;
在创建框架之后,我称之为这个过程

aFrame := TAnyFrame.Create(nil);
if not aFrame.initFrame() then
  FreeAndNil(aFrame)
else
  ... // Associate frame with form and do somthing usefull
此外,当您更改可见框架时,不必销毁上一个框架,因为它可以包含有用的数据。假设您在第一个框架/页面中输入了一些数据,然后转到下一个,然后决定再次更改第一个页面上的数据。如果破坏了前一帧,则丢失了其中包含的数据,需要恢复这些数据。解决方案是保留所有已创建的帧,并仅在必要时创建新帧

page_A : TFrameA;
page_B : TFrameB;
page_C : TFrameC;
current_page : TFrame;

// User click button and select a frame/page A
if not assigned(page_A) then begin
  // create and initialize frame
  page_A := TFrameA.Create(nil);
  if not page_A.initFrame() then begin
    FreeAndNil(page_A);
    // show error message
    ...
    exit;
  end;
  // associate frame with form
  ...
end;
// hide previous frame
if assigned(current_page) then
  current_page.hide();
// show new page on the form
current_page := page_A;
current_page.Show();

+1,但请注意,代码本身会导致内存泄漏。除非在销毁MYFrame1之前或销毁MYFrame1时将MYFrame1.CurrentFrame显式设置为nil,否则它将不会被释放。最好将MyFrame1作为所有者传递给构造函数,利用VCL中的自动生存期管理。@mghie:IIRC控件的父控件也会接管生存期管理,因此Tim的代码中应该没有内存泄漏。实际上,我认为如果不是调用TSomeFrame.Create(nil),而是调用TSomeFrame.Create(nil)(自我)这将使窗体成为帧的所有者,并且窗体将在其自身销毁时释放它。或者,您可以在OnDestroy事件中将CurrentFrame设置为nil。从一个帧更改为另一个帧不会导致内存泄漏,因为setter会在将FCurrentFrame设置为另一个帧之前释放它。现在,我已经回顾了我的一些操作ld代码,我个人使用了帧的接口。因此,当FCurrentFrame设置为nil时,帧会自动释放。但是,这比我上面的示例更高级。@Ulrich:感谢您的评论,作为TWinControl,您似乎是对的。Destroy确实在其所有控件上调用Destroy。它复制了TCom的功能ponent.DestroyComponents有。我不知道为什么控件的处理方式需要与其他组件不同,是吗?无论如何,答案中的代码不会受到内存泄漏的影响,只要在新帧设置为活动时立即释放旧帧。
page_A : TFrameA;
page_B : TFrameB;
page_C : TFrameC;
current_page : TFrame;

// User click button and select a frame/page A
if not assigned(page_A) then begin
  // create and initialize frame
  page_A := TFrameA.Create(nil);
  if not page_A.initFrame() then begin
    FreeAndNil(page_A);
    // show error message
    ...
    exit;
  end;
  // associate frame with form
  ...
end;
// hide previous frame
if assigned(current_page) then
  current_page.hide();
// show new page on the form
current_page := page_A;
current_page.Show();