【发布时间】:2010-11-03 13:43:30
【问题描述】:
如何在运行时创建组件然后使用它(更改属性等)?
【问题讨论】:
标签: delphi runtime components
如何在运行时创建组件然后使用它(更改属性等)?
【问题讨论】:
标签: delphi runtime components
这是如何在 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.
【讨论】:
我只想在动态添加控件时添加它... 按照@Despatcher 在 中的建议,将它们添加到对象列表(TObjectList)中是个好主意。
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;
您需要将 Unit 'Contnrs' 添加到您的 Uses 列表中。 即 System.Contnrs.pas 基本容器单元 你可以有很多对象列表。 我建议为您使用的每种类型的控件使用 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
这使您可以轻松操作/管理每个控件,因为您将知道它是什么类型的控件,例如
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;
这使您可以使用该控件的方法和属性 不要忘记创建 TObjectLists,也许以创建事件的形式...
checkBoxCntrlsList := TObjectList.Create;
comboboxCntrlsList := TObjectList.Create;
【讨论】:
这取决于它是可视组件还是非可视组件。原理是一样的,但是每种组件都有一些额外的注意事项。
对于非视觉组件
var
C: TMyComponent;
begin
C := TMyComponent.Create(nil);
try
C.MyProperty := MyValue;
//...
finally
C.Free;
end;
end;
对于视觉组件:
本质上,可视组件的创建方式与非可视组件相同。但是您必须设置一些额外的属性才能使其可见。
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;
对上面代码的一些解释:
Parent 属性使组件可见。如果您忘记了它,您的组件将不会显示。 (很容易错过那个:))如果您想要许多组件,您可以执行与上述相同的操作,但需要循环:
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;
这将在表单的左边框添加 10 个按钮。如果您想稍后修改按钮,可以将它们存储在列表中。 (TComponentList 最适合,但也可以看看 cmets 对这个答案的建议)
如何分配事件处理程序:
您必须创建一个事件处理程序方法并将其分配给事件属性。
procedure TForm1.MyButtonClick(Sender: TObject);
var
Button: TButton;
begin
Button := Sender as TButton;
ShowMessage(Button.Caption + ' clicked');
end;
B := TButton.Create;
//...
B.OnClick := MyButtonClick;
【讨论】:
在研究“使用基于 xml 的模板创建 delphi 表单”时,我发现一些有用的东西指出了 RTTI 并使用了开放工具 api(我认为是 ToolsApi.pas)。查看单元中的接口。
【讨论】:
如果您在 Group Boxes/Page Controls/Etc... 中嵌套 win 控件,我认为让父组框也成为所有者是有益的。我注意到这样做时窗口关闭时间急剧减少,而不是让所有者始终是主要窗体。
【讨论】:
某些组件会覆盖“已加载”方法。如果您在运行时创建实例,则不会自动调用此方法。当从表单文件 (DFM) 加载完成时,它将被 Delphi 调用。
如果方法包含初始化代码,您的应用程序在运行时创建时可能会出现意外行为。在这种情况下,请检查组件编写器是否使用了此方法。
【讨论】:
非常轻松。调用创建。示例:
procedure test
var
b : TButton;
begin
b:=TButton.Create(nil);
b.visible:=false;
end;
这会在运行时创建一个组件(TButton 是一个组件)并设置属性可见。
对于构造函数:如果您想自己管理内存,请传递 nil。如果您想在另一个组件被销毁时将其销毁,请将指针传递给另一个组件。
【讨论】:
但如果我不确定要创建多少个组件,例如如果这取决于用户的决定。那么如何动态声明组件呢?
已经提出了答案 - 最简单的方法是对象(组件)列表。 TObjectList 是最简单的使用(以单位 contnrs 为单位)。清单很棒!
In Form1 Public
MyList: TObjectList;
procedure AnyButtonClick(Sender: TObject);
// 你可以变得更复杂并声明 //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;
对象列表可以包含或不包含任何视觉对象,但这会给您带来额外的开销来分类哪些项目是哪些 - 例如,如果您想要在类似面板上使用多个动态控件,则最好有相关列表。
注意:和其他评论者一样,为了简洁起见,我可能过于简化了,但我希望你能理解这个想法。您需要一种机制来管理创建对象后的对象,而列表非常适合这些东西。
【讨论】:
为了简化运行时组件的创建过程,您可以使用GExperts。
示例(以这种方式生成的 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;
【讨论】: