简体   繁体   English

在运行时创建组件 - Delphi

[英]Creating components at runtime - Delphi

How can I create a component at runtime and then work with it (changing properties, etc.)?我如何在运行时创建一个组件然后使用它(更改属性等)?

It depends if it is a visual or non-visual component.这取决于它是可视组件还是非可视组件。 The principle is the same, but there are some additional considerations for each kind of component.原理是一样的,但是每种组件都有一些额外的注意事项。

For non-visual components对于非可视组件

var
  C: TMyComponent;
begin
  C := TMyComponent.Create(nil);
  try
    C.MyProperty := MyValue;
    //...
  finally
    C.Free;
  end;
end;

For visual components:对于视觉组件:

In essence visual components are created in the the same way as non-visual components.从本质上讲,可视化组件的创建方式与非可视化组件的创建方式相同。 But you have to set some additional properties to make them visible.但是您必须设置一些额外的属性才能使它们可见。

var
  C: TMyVisualComponent;
begin
  C := TMyVisualComponent.Create(Self);
  C.Left := 100;
  C.Top := 100;
  C.Width := 400;
  C.Height := 300;
  C.Visible := True;
  C.Parent := Self; //Any container: form, panel, ...

  C.MyProperty := MyValue,
  //...
end;

A few explanations to the code above:对上面代码的一些解释:

  • By setting the owner of the component (the parameter of the constructor) the component gets destroyed when the owning form gets destroyed.通过设置组件的所有者(构造函数的参数),当拥有表单被销毁时,组件将被销毁。
  • Setting the Parent property makes the component visible.设置Parent属性使组件可见。 If you forget it your component will not be displayed.如果您忘记了它,您的组件将不会显示。 (It's easy to miss that one:) ) (很容易错过那个:))

If you want many components you can do the same as above but in a loop:如果你想要很多组件,你可以像上面一样做,但是在一个循环中:

var
  B: TButton;
  i: Integer;
begin
  for i := 0 to 9 do
  begin
    B := TButton.Create(Self);
    B.Caption := Format('Button %d', [i]);
    B.Parent := Self;
    B.Height := 23;
    B.Width := 100;
    B.Left := 10;
    B.Top := 10 + i * 25;
  end;
end;

This will add 10 buttons at the left border of the form.这将在表单的左边框添加 10 个按钮。 If you want to modify the buttons later, you can store them in a list.如果以后要修改按钮,可以将它们存储在列表中。 ( TComponentList ist best suited, but also take a look at the proposals from the comments to this answer) TComponentList最适合,但也可以查看评论中对此答案的建议)

How to assign event handlers:如何分配事件处理程序:

You have to create an event handler method and assign it to the event property.您必须创建一个事件处理程序方法并将其分配给事件属性。

procedure TForm1.MyButtonClick(Sender: TObject);
var
  Button: TButton;
begin
  Button := Sender as TButton; 
  ShowMessage(Button.Caption + ' clicked');
end;

B := TButton.Create;
//...
B.OnClick := MyButtonClick;

To simplify the runtime component creation process, you can use GExperts .要简化运行时组件创建过程,您可以使用GExperts

  1. Create a component (or more components) visually and set its properties.可视化地创建一个组件(或多个组件)并设置其属性。
  2. Select one or more components and execute GExperts, Components to Code. Select 一个或多个组件并执行 GExperts,Components to Code。
  3. Paste the generated code into your application.将生成的代码粘贴到您的应用程序中。
  4. Remove component(s) from the visual form designer.从可视表单设计器中删除组件。

Example (TButton-creation code generated in this way):示例(以这种方式生成的 TButton 创建代码):

var
  btnTest: TButton;

btnTest := TButton.Create(Self);
with btnTest do
begin
  Name := 'btnTest';
  Parent := Self;
  Left := 272;
  Top := 120;
  Width := 161;
  Height := 41;
  Caption := 'Component creation test';
  Default := True;
  ParentFont := False;
  TabOrder := 0;
end;

I would just like to add that when dynamically adding controls... it as a good idea to add them to an object list (TObjectList) as suggested in <1> by @Despatcher.我只想在动态添加控件时补充一点……将它们添加到 object 列表 (TObjectList) 是一个好主意,正如@Despatcher 在 <1> 中所建议的那样。

procedure Tform1.AnyButtonClick(Sender: TObject);
begin
  If Sender is TButton then
  begin
    Case Tbutton(Sender).Tag of 
    .
    .
    .
// Or You can use the index in the list or some other property 
// you have to decide what to do      
// Or similar :)
  end;
end;

procedure TForm1.BtnAddComponent(Sender: TObJect)
var
  AButton: TButton;
begin
  AButton := TButton.Create(self);
  Abutton. Parent := [Self], [Panel1] [AnOther Visual Control];
  AButton.OnClick := AnyButtonClick;
// Set Height and width and caption ect.
  .
  .
  . 
  AButton.Tag := MyList.Add(AButton);
end;

You need to add the Unit 'Contnrs' to your Uses list.您需要将单位“Contnrs”添加到您的使用列表中。 Ie System.Contnrs.pas the base Containers Unit And you can have many object lists.即 System.Contnrs.pas 基本容器单元并且您可以有许多 object 列表。 I suggest using a TObjectList for each type of control that you use eg我建议为您使用的每种类型的控件使用 TObjectList,例如

Interface
 Uses Contnrs;
Type
 TMyForm = class(TForm)
private
   { Private declarations }
public
   { Public declarations }
end;
 Var
  MyForm: TMyForm;
  checkBoxCntrlsList: TObjectList; //a list for the checkBoxes I will createin a TPanel
  comboboxCntrlsList: TObjectList; //a list of comboBoxes that I will create in some Form Container

this allows you to easily manipulate/manage each control as you will know what type of control it is eg这使您可以轻松地操作/管理每个控件,因为您将知道它是什么类型的控件,例如

Var comboBox: TComboBox;
I: Integer;

begin
 For I = 0 to comboboxCntrlsList.Count -1 do // or however you like to identify the control you are accessing such as using the tag property as @Despatcher said
   Begin
    comboBox := comboboxCntrlsList.Items[I] as TComboBox;
    ...... your code here
   End;
end;

This allows you to then use the methods and properties of that control Don't forget to create the TObjectLists, perhaps in the form create event...这允许您然后使用该控件的方法和属性 不要忘记创建 TObjectLists,也许以创建事件的形式......

checkBoxCntrlsList := TObjectList.Create;
comboboxCntrlsList := TObjectList.Create;

But if I don't surely know how many components I want to create, eg if it depends on user's decision.但是如果我不确定我想创建多少个组件,例如这取决于用户的决定。 So how can I declare components dynamically?那么如何动态声明组件呢?

The answer has been suggested - the easiest way is a List of Objects(components).已经提出了答案 - 最简单的方法是对象列表(组件)。 TObjectList is the simplest to use (in unit contnrs). TObjectList 是最简单易用的(单位为 contnrs)。 Lists are great!清单很棒!

  In Form1 Public
  MyList: TObjectList;
  procedure AnyButtonClick(Sender: TObject); 

// You can get more sophisticated and declare //TNotifyevents and assign them but lets keep it simple:). // 你可以变得更复杂并声明 //TNotifyevents 并分配它们,但让我们保持简单:)。 . . . .

procedure Tform1.AnyButtonClick(Sender: TObject);
begin
  If Sender is TButton then
  begin
    Case Tbutton(Sender).Tag of 
    .
    .
    .
// Or You can use the index in the list or some other property 
// you have to decide what to do      
// Or similar :)
  end;
end;

procedure TForm1.BtnAddComponent(Sender: TObJect)
var
  AButton: TButton;
begin
  AButton := TButton.Create(self);
  Abutton. Parent := [Self], [Panel1] [AnOther Visual Control];
  AButton.OnClick := AnyButtonClick;
// Set Height and width and caption ect.
  .
  .
  . 
  AButton.Tag := MyList.Add(AButton);
end;

An Object list can contain any object visual or not but that gives you an added overhead of sorting out which items are which - better to have related lists if you want multiple dynamic controls on similar panels for instance. Object 列表可以包含或不包含任何 object 视觉对象,但这会给您带来额外的开销来整理哪些项目是哪些 - 例如,如果您想要在类似面板上使用多个动态控件,最好有相关列表。

Note: like other commenters I may have over-simplified for brevity but I hope you ge the idea.注意:像其他评论者一样,为了简洁起见,我可能过于简化了,但我希望你能理解这个想法。 You need a mechanism to manage the objects once they are created and lists are excellent for this stuff.您需要一种机制来管理对象,一旦它们被创建,列表就非常适合这些东西。

Some components override the 'Loaded' method.某些组件会覆盖“已加载”方法。 This method will not be called automatically if you create an instance at runtime.如果您在运行时创建实例,则不会自动调用此方法。 It will be called by Delphi when loading from the form file (DFM) is complete.当从表单文件 (DFM) 加载完成时,它将由 Delphi 调用。

If the method contains initialization code, your application might show unexpected behaviour when created at runtime.如果该方法包含初始化代码,您的应用程序在运行时创建时可能会出现意外行为。 In this case, check if the component writer has used this method.在这种情况下,请检查组件编写者是否使用了此方法。

If you nest win controls in Group Boxes/Page Controls/Etc..., I think it is beneficial to have the parent group box also be the owner.如果您将 win 控件嵌套在 Group Boxes/Page Controls/Etc... 中,我认为让父组框也成为所有者是有益的。 I've noticed a sharp decrease in window close times when doing this, as opposed to having the owner always be the main form.我注意到这样做时 window 关闭时间急剧减少,而不是让所有者始终是主要形式。

During a research on "creating a delphi form using xml based template", I find something useful pointing out RTTI and using open tools api (ToolsApi.pas I think).在对“使用基于 883812976388 的模板创建 delphi 表单”的研究中,我发现一些有用的东西指出 RTTI 并使用开放工具 api(我认为是 ToolsApi.pas)。 Have a look at the interfaces in the unit.查看单元中的接口。

Very ease.很轻松。 Call Create.调用创建。 Example:例子:

procedure test
var
  b : TButton;
begin
  b:=TButton.Create(nil);
  b.visible:=false;
end;

This creates a component (TButton is a component) at runtime and sets the property visible.这会在运行时创建一个组件(TButton 是一个组件)并将属性设置为可见。


For the constructor: pass nil if you want to manage the memory yourself.对于构造函数:如果您想自己管理 memory,请传递 nil。 Pass a pointer another component if you want to have it destroyed when the other component is destroyed.如果您想在另一个组件被销毁时将其销毁,请将指针传递给另一个组件。

This is example how to emulate button tag on Evernote这是如何在 Evernote 上模拟按钮标签的示例

unit Unit7;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, CHButton, Vcl.ExtCtrls, RzPanel, CHPanel, RzCommon,RzBmpBtn, Vcl.StdCtrls;

type
  // This is panel Button
  TButtonClose = class (TRzPanel)
   CloseButton : TRzBmpButton;
   procedure CloseButtonClick(Sender: TObject);
   procedure CloseButtonMouseEnter(Sender: TObject);
   procedure MouseDown(Sender: TObject; Button: TMouseButton;
             Shift: TShiftState; X, Y: Integer);
   procedure MouseUp(Sender: TObject; Button: TMouseButton;
             Shift: TShiftState; X, Y: Integer);
public
   constructor Create(AOwner: TComponent); override;
   destructor Destroy; override;
end;

TForm7 = class(TForm)
   CHButton1: TCHButton;
   RzPanel1: TRzPanel;
   RzBmpButton1: TRzBmpButton;
   procedure CHButton1Click(Sender: TObject);
   procedure RzBmpButton1Click(Sender: TObject);
   procedure RzPanel1MouseDown(Sender: TObject; Button: TMouseButton;
     Shift: TShiftState; X, Y: Integer);
   procedure RzPanel1MouseUp(Sender: TObject; Button: TMouseButton;
     Shift: TShiftState; X, Y: Integer);
   procedure RzPanel1MouseEnter(Sender: TObject);
   procedure RzBmpButton1MouseEnter(Sender: TObject);
   procedure FormMouseEnter(Sender: TObject);
   procedure FormCreate(Sender: TObject);
private
  { Private declarations }
public
  { Public declarations }
end;

var
  Form7: TForm7;
  MyCloseButton : TButtonClose;

implementation

{$R *.dfm}

// constructor for on the fly component created
constructor TButtonClose.Create(AOwner: TComponent);
begin
   inherited Create(AOwner);

   // Set Events for the component
   Self.OnMouseEnter := Self.CloseButtonMouseEnter;
   Self.OnMouseDown := Self.MouseDown;
   Self.OnMouseUp := Self.MouseUp;
   Self.Height := 25;

   // Close button on top panel Button
   // Inherited from Raize Bitmap Button
   CloseButton := TRzBmpButton.Create(self);
   // Set On Click Event for Close Button
   CloseButton.OnClick := Self.CloseButtonClick;
   // Place Close Button on Panel Button
   CloseButton.Parent := self;
   CloseButton.Left := 10;
   CloseButton.Top := 5;
   CloseButton.Visible := False;
   // Setting the image for the button
   CloseButton.Bitmaps.Up.LoadFromFile(ExtractFilePath(Application.ExeName)+'\close.bmp');
end;

procedure TButtonClose.CloseButtonClick(Sender: TObject);
begin
   // Free the parent (Panel Button)
   TControl(Sender).Parent.Free;
end;

procedure TButtonClose.CloseButtonMouseEnter(Sender: TObject);
begin
   // Show the Close button
   CloseButton.Visible := True;
end;

procedure TButtonClose.MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
   // Emulate Button down state, since it is panel
   TRzPanel(Sender).BorderOuter := fsLowered;
end;

procedure TButtonClose.MouseUp(Sender: TObject; Button: TMouseButton;
 Shift: TShiftState; X, Y: Integer);
begin
   // Emulate Button up state, since it is panel
   TRzPanel(Sender).BorderOuter := fsRaised;
end;

destructor TButtonClose.Destroy;
begin
   inherited Destroy;
end;

procedure TForm7.FormCreate(Sender: TObject);
begin
   // Create Panel Button on the fly
   MyCloseButton := TButtonClose.Create(self);
   MyCloseButton.Caption := 'My Button';
   MyCloseButton.Left := 10;
   MyCloseButton.Top := 10;
   // Don't forget to place component on the form
   MyCloseButton.Parent := self;
end;

procedure TForm7.FormMouseEnter(Sender: TObject);
begin
   if Assigned(RzBmpButton1) then
      RzBmpButton1.Visible := False;

   // Hide when mouse leave the button
   // Check first if myCloseButton Assigned or not before set visible property
   if Assigned(MyCloseButton.CloseButton) then
      MyCloseButton.CloseButton.Visible := False;
end;

procedure TForm7.RzBmpButton1Click(Sender: TObject);
begin
   TControl(Sender).Parent.Free;
end;

procedure TForm7.RzBmpButton1MouseEnter(Sender: TObject);
begin
   RzBmpButton1.Visible := True;
end;

procedure TForm7.RzPanel1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  TRzPanel(Sender).BorderOuter := fsLowered;
end;

procedure TForm7.RzPanel1MouseEnter(Sender: TObject);
begin
   RzBmpButton1.Visible := True;
end;

procedure TForm7.RzPanel1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
   TRzPanel(Sender).BorderOuter := fsRaised;
end;

procedure TForm7.CHButton1Click(Sender: TObject);
begin
   FreeAndNil(Sender);
end;

end.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM