Criação de componentes em tempo de execução - Delphi
-
05-07-2019 - |
Pergunta
Como posso criar um componente em tempo de execução e, em seguida, trabalhar com ele (como alterar as propriedades, etc.)?
Solução
Depende se é um componente visual ou não visual. O princípio é o mesmo, mas há algumas considerações adicionais para cada tipo de componente.
Para componentes não-visuais
var
C: TMyComponent;
begin
C := TMyComponent.Create(nil);
try
C.MyProperty := MyValue;
//...
finally
C.Free;
end;
end;
Para componentes visuais:
componentes visuais, em essência, são criados no da mesma maneira que os componentes não-visuais. Mas você tem que definir algumas propriedades adicionais para torná-los visíveis.
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;
Algumas explicações para o código acima:
- Ao definir o proprietário do componente (o parâmetro do construtor) o componente é destruída quando o formulário possuir é destruído.
- Definir a propriedade
Parent
torna visível o componente. Se você esquecer seu componente não será exibido. (É fácil perder que um :))
Se você quiser muitos componentes você pode fazer o mesmo que acima, mas em um 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;
Isto irá adicionar 10 botões na borda esquerda do formulário. Se você deseja modificar os botões mais tarde, você pode armazená-los em uma lista. ( TComponentList ist o mais adequado, mas também ter um olhar para o propostas dos comentários a esta resposta)
Como manipuladores de eventos atribuir:
Você tem que criar um método de manipulador de eventos e atribuí-lo à propriedade de evento.
procedure TForm1.MyButtonClick(Sender: TObject);
var
Button: TButton;
begin
Button := Sender as TButton;
ShowMessage(Button.Caption + ' clicked');
end;
B := TButton.Create;
//...
B.OnClick := MyButtonClick;
Outras dicas
Para simplificar o processo de criação do componente de tempo de execução, você pode usar GExperts .
- Criar um componente (ou mais componentes) visualmente e definir as suas propriedades.
- Selecione um ou mais componentes e executar GExperts, Componentes para código.
- Cole o código gerado em sua aplicação.
- componente Remove (s) do designer de forma visual.
Exemplo (código TButton-criação gerado desta maneira):
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;
Eu gostaria apenas de acrescentar que ao adicionar dinamicamente controles ... -lo como uma boa idéia para adicioná-los a uma lista de objetos (TObjectList) como sugerido em <1> por @Despatcher.
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;
Você precisa adicionar a Unidade 'Contnrs' à sua lista de usos. Ou seja System.Contnrs.pas a Unidade Contentores de base E você pode ter muitas listas de objetos. Eu sugiro usar um TObjectList para cada tipo de controle que você usa por exemplo.
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
isso permite que você manipule / gerenciar cada controle como você vai saber que tipo de controle é por exemplo facilmente.
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;
Isso permite que você use os métodos e propriedades do que o controle Não se esqueça de criar as TObjectLists, talvez sob a forma criar evento ...
checkBoxCntrlsList := TObjectList.Create;
comboboxCntrlsList := TObjectList.Create;
Mas se eu não certamente sabe quantos componentes Quero criar, por exemplo, se depender da decisão do usuário. Então, como posso declarar componentes dinamicamente?
A resposta foi sugerida - a maneira mais fácil é uma lista de objetos (componentes). TObjectList é o mais simples de usar (em contnrs unidade). As listas são ótimo!
In Form1 Public
MyList: TObjectList;
procedure AnyButtonClick(Sender: TObject);
// Você pode obter mais sofisticados e declarar // TNotifyevents e atribuí-los, mas vamos mantê-lo simples :) . . .
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;
Uma lista de objeto pode conter qualquer objeto visual ou não, mas que lhe dá um adicional sobrecarga de separar quais itens são que -. Melhor ter listas relacionadas Se quiser vários controles dinâmicos em painéis semelhantes para instância
Nota: como outros comentadores eu possa ter mais simplificada para a brevidade, mas eu espero que você ge a idéia. Você precisa de um mecanismo para gerenciar os objetos quando eles são criados e listas são excelentes para este material.
Durante uma pesquisa sobre "a criação de uma forma delphi usando o modelo baseado em XML", eu encontrar algo que aponta útil fora RTTI e usando aberto ferramentas de api (ToolsApi.pas eu acho). Ter um olhar para as interfaces na unidade.
Muito facilidade. Chamada Criar. Exemplo:
procedure test
var
b : TButton;
begin
b:=TButton.Create(nil);
b.visible:=false;
end;
Isso cria um componente (TButton é um componente) em tempo de execução e define a propriedade visível.
Para o construtor: passar nil se pretende gerir a memória sozinho. Passar um ponteiro outro componente se você quiser tê-lo destruído quando o outro componente é destruído.
Alguns componentes substituir o método 'Loaded'. Este método não será chamado automaticamente se você criar uma instância em tempo de execução. Ele será chamado pelo Delphi quando o carregamento do arquivo de formulário (DFM) está completa.
Se o método contém código de inicialização, o aplicativo pode apresentar um comportamento inesperado quando criado em tempo de execução. Neste caso, verifique se o escritor componente tem utilizado este método.
Se você ninho ganhar controles no Grupo Boxes / Controles Página / etc ..., eu acho que é benéfico ter a caixa de grupo pai também ser o proprietário. Tenho notado uma diminuição acentuada nos tempos fechar janela ao fazer isso, em vez de ter o proprietário sempre ser a principal forma.
Esta é exemplo como emular tag botão no 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.