动态创建组件(在运行时)

Most often when programming in Delphi you don't need to dynamically create a component. If you drop a component on a form, Delphi handles the component creation automatically when the form is created. This article will cover the correct way to programmatically create components at run-time.

通常,在Delphi中进行编程时,您不需要动态创建组件。 如果将组件放在表单上,​​则在创建表单时,Delphi会自动处理组件的创建。 本文将介绍在运行时以编程方式创建组件的正确方法。

动态组件创建 ( Dynamic Component Creation )

There are two ways to dynamically create components. One way is to make a form (or some other TComponent) the owner of the new component. This is a common practice when building composite components where a visual container creates and owns the subcomponents. Doing so will ensure that the newly-created component is destroyed when the owning component is destroyed.

有两种动态创建组件的方法。 一种方法是使表单(或其他TComponent)成为新组件的所有者。 当在可视容器创建并拥有子组件的位置构建复合组件时,这是一种常见的做法。 这样做将确保在销毁拥有的部件时销毁新创建的部件。

To create an instance (object) of a class, you call its "Create" method. The Create constructor is a class method, as opposed to virtually all other methods you’ll encounter in Delphi programming, which are object methods.

要创建类的实例(对象),请调用其“创建”方法。 Create构造函数是一个类方法 ,与您在Delphi编程中遇到的几乎所有其他方法(对象方法)相反。

For example, the TComponent declares the Create constructor as follows:

例如,TComponent声明Create构造器,如下所示:

constructor Create(AOwner: TComponent) ; virtual;

构造函数Create(AOwner:TComponent); 虚拟;

Dynamic Creation with OwnersHere's an example of dynamic creation, where Self is a TComponent or TComponent descendant (e.g., an instance of a TForm):

具有所有者动态创建以下是动态创建的示例,其中Self是TComponent或TComponent的后代(例如,TForm的实例):

with TTimer.Create(Self) dobeginInterval := 1000;Enabled := False;OnTimer := MyTimerEventHandler;end;

使用TTimer.Create(Self)dobeginInterval:= 1000; Enabled:= False; OnTimer:= MyTimerEventHandler; end;

Dynamic Creation with an Explicit Call to FreeThe second way to create a component is to use nil as the owner. Note that if you do this, you must also explicitly free the object you create as soon as you no longer need it (or you'll produce a memory leak). Here's an example of using nil as the owner:

明确调用Free进行动态创建创建组件的第二种方法是使用nil作为所有者。 请注意,如果这样做,则还必须在不再需要创建的对象时显式释放它(否则会产生内存泄漏 )。 这是使用nil作为所有者的示例:

with TTable.Create(nil) dotryDataBaseName := 'MyAlias';TableName := 'MyTable';Open;Edit;FieldByName('Busy').AsBoolean := True;Post;finallyFree;end;

使用TTable.Create(nil)dotryDataBaseName:='MyAlias'; TableName:='MyTable'; Open; Edit; FieldByName('Busy')。AsBoolean:= True; Post; finallyFree; end;

Dynamic Creation and Object ReferencesIt is possible to enhance the two previous examples by assigning the result of the Create call to a variable local to the method or belonging to the class. This is often desirable when references to the component need to be used later, or when scoping problems potentially caused by "With" blocks need to be avoided. Here's the TTimer creation code from above, using a field variable as a reference to the instantiated TTimer object:

动态创建和对象引用可以通过将Create调用的结果分配给方法本地的或属于该类的变量来增强前面的两个示例。 当以后需要使用对组件的引用,或者需要避免潜在的由“ With”块引起的作用域确定问题时,这通常是合乎需要的。 这是上面的TTimer创建代码,使用字段变量作为实例化TTimer对象的引用:

FTimer := TTimer.Create(Self) ;with FTimer dobeginInterval := 1000;Enabled := False;OnTimer := MyInternalTimerEventHandler;end;

FTimer:= TTimer.Create(Self);使用FTimer dobeginInterval:= 1000; Enabled:= False; OnTimer:= MyInternalTimerEventHandler; end;

In this example "FTimer" is a private field variable of the form or visual container (or whatever "Self" is). When accessing the FTimer variable from methods in this class, it is a very good idea to check to see if the reference is valid before using it. This is done using Delphi's Assigned function:

在此示例中,“ FTimer”是表单或可视容器(或任何“ Self”是)的私有字段变量。 从此类中的方法访问FTimer变量时,最好在使用引用之前先检查引用是否有效。 这是使用Delphi的Assigned函数完成的:

if Assigned(FTimer) then FTimer.Enabled := True;

如果为Assigned(FTimer),则FTimer.Enabled:= True;

Dynamic Creation and Object References without OwnersA variation on this is to create the component with no owner, but maintain the reference for later destruction. The construction code for the TTimer would look like this:

没有所有者的动态创建和对象引用对此的一种变化是创建没有所有者的组件,但保留引用以供以后销毁。 TTimer的构造代码如下所示:

FTimer := TTimer.Create(nil) ;with FTimer dobegin...end;

FTimer:= TTimer.Create(nil);使用FTimer dobegin ... end;

And the destruction code (presumably in the form's destructor) would look something like this:

销毁代码(可能在表单的析构函数中)看起来像这样:

FTimer.Free;FTimer := nil;(*Or use FreeAndNil (FTimer) procedure, which frees an object reference and replaces the reference with nil.*)

FTimer.Free; FTimer:= nil;(*或使用FreeAndNil(FTimer)过程,该过程将释放对象引用并将引用替换为nil。*)

Setting the object reference to nil is critical when freeing objects. The call to Free first checks to see if the object reference is nil or not, and if it isn't, it calls the object's destructor Destroy.

释放对象时,将对象引用设置为nil至关重要。 对Free的调用首先检查对象引用是否为nil,如果不是,则调用对象的析构函数Destroy。

没有所有者的动态创建和本地对象引用 ( Dynamic Creation and Local Object References without Owners )

Here's the TTable creation code from above, using a local variable as a reference to the instantiated TTable object:

这是上面的TTable创建代码,使用本地变量作为实例化TTable对象的引用:

localTable := TTable.Create(nil) ;trywith localTable dobeginDataBaseName := 'MyAlias';TableName := 'MyTable';end;...// Later, if we want to explicitly specify scope:localTable.Open;localTable.Edit;localTable.FieldByName('Busy').AsBoolean := True;localTable.Post;finallylocalTable.Free;localTable := nil;end;

localTable:= TTable.Create(nil);尝试使用localTable dobeginDataBaseName:='MyAlias'; TableName:='MyTable'; end; ... //稍后,如果我们要显式指定scope:localTable.Open; localTable.Edit ; localTable.FieldByName('Busy')。AsBoolean:= True; localTable.Post; finallylocalTable.Free; localTable:= nil; end;

In the example above, "localTable" is a local variable declared in the same method containing this code. Note that after freeing any object, in general it is a very good idea to set the reference to nil.

在上面的示例中,“ localTable”是在包含此代码的同一方法中声明的局部变量 。 请注意,释放任何对象后,通常最好将引用设置为nil。

警告语 ( A Word of Warning )

IMPORTANT: Do not mix a call to Free with passing a valid owner to the constructor. All of the previous techniques will work and are valid, but the following should never occur in your code:

重要说明:不要将对Free的调用与将有效所有者传递给构造函数混合使用。 所有先前的技术都可以使用并且有效,但是在您的代码中绝不出现以下技术:

with TTable.Create(self) dotry...finallyFree;end;

使用TTable.Create(self)dotry ... finallyFree; end;

The code example above introduces unnecessary performance hits, impacts memory slightly, and has the potential to introduce hard to find bugs. Find out why.

上面的代码示例引入了不必要的性能损失,对内存的影响很小,并且有可能引入难以发现的错误。 找出为什么。

Note: If a dynamically created component has an owner (specified by the AOwner parameter of the Create constructor), then that owner is responsible for destroying the component. Otherwise, you must explicitly call Free when you no longer need the component.

注意:如果动态创建的组件具有所有者(由Create构造函数的AOwner参数指定),则该所有者负责销毁该组件。 否则,当您不再需要该组件时,必须显式调用Free。

Article originally written by Mark Miller

最初由Mark Miller撰写的文章

A test program was created in Delphi to time the dynamic creation of 1000 components with varying initial component counts. The test program appears at the bottom of this page. The chart shows a set of results from the test program, comparing the time it takes to create components both with owners and without. Note that this is only a portion of the hit. A similar performance delay can be expected when destroying components. The time to dynamically create components with owners is 1200% to 107960% slower than that to create components without owners, depending on the number of components on the form and the component being created.

在Delphi中创建了一个测试程序,以动态创建1000个具有不同初始组件数的组件。 测试程序将显示在此页面的底部。 该图表显示了测试程序的一组结果,比较了创建组件的时间(包括所有者和不所有者)。 请注意,这只是点击的一部分。 销毁组件时,可能会出现类似的性能延迟。 动态创建具有所有者的组件的时间比创建不具有所有者的组件的时间要慢1200%到107960%,这取决于表单上的组件数和所创建的组件。

测试程序 ( The Test Program )

Warning: This test program does not track and free components that are created without owners. By not tracking and freeing these components, times measured for the dynamic creation code more accurately reflect the real time to dynamically create a component.

警告:此测试程序不会跟踪和释放在没有所有者的情况下创建的组件。 通过不跟踪和释放这些组件,为动态创建代码测量的时间可以更准确地反映动态创建组件的实时时间。

Download Source Code

下载源代码

警告! ( Warning! )

If you want to dynamically instantiate a Delphi component and explicitly free it sometime later, always pass nil as the owner. Failure to do so can introduce unnecessary risk, as well as performance and code maintenance problems. Read the "A warning on dynamically instantiating Delphi components" article to learn more...

如果要动态实例化Delphi组件并在以后的某个时间显式释放它,请始终以nil作为所有者。 否则,可能会带来不必要的风险以及性能和代码维护问题。 阅读“有关动态实例化Delphi组件的警告”一文,以了解更多信息。

翻译自: https://www.thoughtco.com/creating-components-dynamically-at-run-time-1058151

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java Swing中,可以使用`SwingUtilities.updateComponentTreeUI(Component c)`方法来动态更新组件的外观。该方法会递归地更新指定组件及其所有子组件的外观。 下面是一个示例代码,演示了如何动态创建并更新Swing组件的外观: ```java import javax.swing.*; import java.awt.*; public class DynamicComponentCreation extends JFrame { private JPanel panel; public DynamicComponentCreation() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(400, 300); setTitle("Dynamic Component Creation"); panel = new JPanel(); panel.setLayout(new FlowLayout()); JButton button = new JButton("Click me!"); button.addActionListener(e -> { JLabel label = new JLabel("New Label"); panel.add(label); SwingUtilities.updateComponentTreeUI(this); }); panel.add(button); add(panel); } public static void main(String[] args) { SwingUtilities.invokeLater(() -> { DynamicComponentCreation frame = new DynamicComponentCreation(); frame.setVisible(true); }); } } ``` 在上述代码中,我们创建了一个`JFrame`窗口,并在窗口中添加了一个`JPanel`面板和一个按钮。当点击按钮,会动态创建一个新的`JLabel`标签,并将其添加到面板中。然后,我们调用`SwingUtilities.updateComponentTreeUI(this)`方法来更新窗口及其所有子组件的外观。 运行上述代码,你将看到一个窗口和一个按钮。每次点击按钮,都会动态创建一个新的标签,并将其添加到面板中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值