【问题标题】:How do I refer to components created at runtime rather than in the form designer?如何引用在运行时而不是在表单设计器中创建的组件?
【发布时间】:2011-12-17 19:56:12
【问题描述】:

我有一个小问题。我试图在运行时在 Delphi7 中创建一个组件列表,并使用表单的 .OnResize 事件调整它们的大小,但没有用......我不知道该怎么做。

这是我的代码:

procedure TForm1.Button1Click(Sender: TObject);
var
  //ExtCtrls
  panel: TPanel;
  memo: TMemo;
  splitter: TSplitter;
  list: TListBox;
begin
  panel := TPanel.Create(Self);
  list := TListBox.Create(Self);
  splitter := TSplitter.Create(Self);
  memo := TMemo.Create(Self);

  with panel do
  begin
    Parent := Form1;
    BevelOuter := bvNone;
    Top := 12;
    Left := 12;
    Height := Form1.Clientheight - 24;
    Width := Form1.Clientwidth - 24;
  end;

  with list do
  begin
    Parent := panel;
    Align := alClient;
    Top := 0;
    Height := panel.Height;
  end;

  with splitter do
  begin
    Parent := panel;
    Top := 0;
    Width := 12;
    Align := alLeft;
  end;

  with memo do
  begin
    Parent := panel;
    Top := 0;
    Left := 0;
    Width := round(panel.Width / 4) * 3;
    Height := panel.Height;
    Align := alLeft;
  end;
end;

我是否必须以某种方式注册他们的名字才能在表单的活动中使用它们?或者,创建一个类并包含它们?

非常感谢任何形式的帮助!提前谢谢你。

【问题讨论】:

    标签: delphi delphi-7


    【解决方案1】:

    您的变量在创建它们的过程中是本地的,因此您不能在该过程之外使用这些变量来引用它们。解决方案是让它们成为表单类的字段。

    type
      TForm1 = class(TForm)
        procedure FormResize(Sender: TObject);
        procedure Button1Click(Sender: TObject);
      private
        FPanel: TPanel;
        FMemo: TMemo;
        FSplitter: TSplitter;
        FList: TListBox;
      end;
    

    那么您的FormResize 事件处理程序可以引用它们。

    procedure TForm1.FormResize(Sender: TObject);
    begin
      if Assigned(FPanel) then
      begin
        ...
      end;
    end;
    

    不要忘记从Button1Click 中删除局部变量并改用字段。

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      FPanel := TPanel.Create(Self);
      ...
    end;
    

    【讨论】:

    • 大卫成功了。现在我只需要研究这会如何影响其他一些事情,例如一系列 tpanels 等。非常感谢!
    • @DimitrisNats 控件数组的处理方式类似。您只需要适当类型的类的字段,例如FPanels: array of TPanel.
    【解决方案2】:

    虽然大卫的回答也很正确,但我想我会花点时间详细说明一下。从外观上看,您似乎对 Delphi 很陌生。初学者有一个非常常见的问题,David 在他的回答中没有解决这个问题,与创建和释放这些对象有关。每当你在一个类上调用“创建”时,在某些时候,当你完成它时,你也必须“释放”那个类。没有释放任何东西会导致内存泄漏,没有人想要这样。释放就像创建一样简单 - 直到您进入保存对象列表的主题(您现在不需要)。

    假设您想创建一个文本框 (TEdit) 控件并将其放置在表单的中心。现在首先,Delphi IDE 允许您简单地将这些控件放到您的表单中,只要确保您知道。您不一定需要自己创建/释放它们,除非有一些特殊情况。但这样做很危险。为了这个例子,我们假设这个TEdit 控件在您的应用程序的整个持续时间内都存在。

    首先,您需要在某处为该控件声明一个变量。最合理的地方是在将使用它的类中(在这种情况下,我们将调用您的表单Form1)。在表单中使用变量(又名字段)时,请确保不要在 private 部分上方放置任何内容。 private 以上的所有内容都是由 Delphi 自动生成的代码,用于在您的表单中删除(并且是可视的)任何内容。否则,任何手动创建的东西都必须在privatepublic 下。 public 区域将是您控制的好地方...

    type
      TForm1 = class(TForm)
      private
    
      public
        MyEdit: TEdit;
      end;
    

    现在它已被声明,我们必须创建(并释放)它。这是一个很好的实践,每次你创建一些东西时,在你继续工作之前,你立即把代码也释放掉。为表单的 OnCreateOnDestroy 事件创建一个事件处理程序...

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      MyEdit:= TMyEdit.Create(nil);
      MyEdit.Parent:= Self;
      MyEdit.Left:= (ClientWidth div 2) - (Width div 2);
      MyEdit.Top:= (ClientHeight div 2) - (Height div 2);
    end;
    
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      if assigned(MyEdit) then MyEdit.Free;
    end;
    

    如果这个对象没有被创建(在创建之前或销毁之后),那么你在尝试使用它时会得到一个“访问冲突”。这是因为您的应用程序试图访问计算机内存中未分配或与您要获取的类型不匹配的区域。

    嗯,这就是解决方案的基础。然而,还有一件事要告诉你。假设您需要在一个过程的持续时间内短时间创建一个对象。对此有不同的方法。在上面的代码中,您直接在过程中声明了变量。此示例将向您展示何时需要执行此操作...

    procedure TForm1.Button1Click(Sender: TObject);
    var
      MyObject: TMyObject;
    begin
      MyObject:= TMyObject.Create;
      try
        MyObject.DoSomething;
        Caption:= MyObject.GetSomething;
      finally
        MyObject.Free;
      end;
    end;
    

    你看,只要MyObject只会在这一次调用这个过程中使用,那么你就可以在这里声明它。但是,如果在此过程结束并完成后对象预计会留在内存中,那么事情就会变得更加复杂。同样,在您的情况下,坚持将其放在表单的类中,直到您更熟悉动态创建对象为止。

    最后一点,如上所述,您可以在设计时将TEdit 控件直接放置在您的表单上,而无需编写自己的代码。如果你这样做,你需要记住不要试图创建或释放这些。当 Delphi 会自动将代码放在 private 部分上方时也是这种情况 - 当有一些你不应该玩的东西时。

    【讨论】:

    • @Jerry 我不是反对者,但我认为你错过的是构造函数都将Self 作为Owner 参数传递。这意味着当表单被销毁时,这些对象都将被销毁。
    • @Nick 我很惊讶,我以为你会反对通过公开这些字段来引入广泛的范围...... ;-)
    • 另外,请不要写if assigned(MyEdit) then MyEdit.Free。您可以在 nil 对象引用上调用 Free。这样做是因为替代方案不可读。
    • @DavidHeffernan 我不得不说我实际上并不知道。我以前创造过这样的东西并且没有遇到过问题。再一次,我思考了为什么它需要同时设置“所有者”和“父母”——这可以解释它。从Self 更改为nil。另外,我在网站上输入了这个,所以我没有编译它来确认它的有效性。
    • 伟大的信息杰里和大卫。我读过你所有的 cmets,你让我明白了很多我不知道的事情。非常感谢。
    【解决方案3】:

    我认为我没有资格“评论”,因此我将其表述为“答案”。如果您想在父级更改大小时调整运行时组件的大小,请仔细查看 Anchors 属性。它可以为您节省大量工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-07
      • 2012-11-23
      • 1970-01-01
      • 2011-04-12
      • 1970-01-01
      • 2012-02-14
      • 2019-06-16
      • 2015-09-08
      相关资源
      最近更新 更多