Creando componentes en tiempo de ejecución-Delphi
runtime components (9)
Pero si no sé cuántos componentes quiero crear, por ejemplo, si depende de la decisión del usuario. Entonces, ¿cómo puedo declarar componentes dinámicamente?
Se ha sugerido la respuesta: la forma más sencilla es una Lista de objetos (componentes). TObjectList es el más simple de usar (en contadores de unidad). ¡Las listas son geniales!
In Form1 Public
MyList: TObjectList;
procedure AnyButtonClick(Sender: TObject);
// Puede ser más sofisticado y declarar // TNotifyevents y asignarlos, pero sea sencillo :). . .
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;
Una lista de objetos puede contener cualquier objeto visual o no, pero eso le brinda una sobrecarga adicional de ordenar qué elementos son cuáles: mejor tener listas relacionadas si desea múltiples controles dinámicos en paneles similares, por ejemplo.
Nota: al igual que otros comentaristas, es posible que haya sido simplificado en exceso por brevedad, pero espero que sea una idea más acertada. Necesita un mecanismo para administrar los objetos una vez que se crean y las listas son excelentes para esto.
¿Cómo puedo crear un componente en tiempo de ejecución y luego trabajar con él (cambiar propiedades, etc.)?
Algunos componentes anulan el método ''Cargado''. Este método no se llamará automáticamente si crea una instancia en tiempo de ejecución. Delphi lo llamará cuando se complete la carga desde el archivo de formulario (DFM).
Si el método contiene código de inicialización, su aplicación puede mostrar un comportamiento inesperado cuando se crea en tiempo de ejecución. En este caso, compruebe si el escritor de componentes ha utilizado este método.
Depende si es un componente visual o no visual. El principio es el mismo, pero hay algunas consideraciones adicionales para cada tipo de componente.
Para componentes no visuales.
var
C: TMyComponent;
begin
C := TMyComponent.Create(nil);
try
C.MyProperty := MyValue;
//...
finally
C.Free;
end;
end;
Para componentes visuales:
En esencia, los componentes visuales se crean de la misma manera que los componentes no visuales. Pero tienes que establecer algunas propiedades adicionales para hacerlas visibles.
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;
Algunas explicaciones al código anterior:
- Al establecer el propietario del componente (el parámetro del constructor), el componente se destruye cuando se destruye la forma propietaria.
- Establecer la propiedad
Parent
hace que el componente sea visible. Si lo olvidas tu componente no se mostrará. (Es fácil perdérsela)
Si desea muchos componentes , puede hacer lo mismo que arriba pero en un bucle:
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;
Esto agregará 10 botones en el borde izquierdo del formulario. Si desea modificar los botones más tarde, puede almacenarlos en una lista. ( TComponentList es el más adecuado, pero también echa un vistazo a las propuestas de los comentarios a esta respuesta)
Cómo asignar controladores de eventos:
Debe crear un método de controlador de eventos y asignarlo a la propiedad del 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;
Durante una investigación sobre "crear un formulario Delphi usando una plantilla basada en xml", encuentro algo útil al señalar RTTI y usar una api de herramientas abiertas (creo que ToolsApi.pas). Echa un vistazo a las interfaces en la unidad.
Muy fácil. Llamar a crear. Ejemplo:
procedure test
var
b : TButton;
begin
b:=TButton.Create(nil);
b.visible:=false;
end;
Esto crea un componente (TButton es un componente) en tiempo de ejecución y establece la propiedad visible.
Para el constructor: pase nil si quiere administrar la memoria usted mismo. Pase un puntero a otro componente si desea destruirlo cuando se destruya el otro componente.
Para simplificar el proceso de creación del componente de tiempo de ejecución, puede utilizar GExperts .
- Cree un componente (o más componentes) visualmente y establezca sus propiedades.
- Seleccione uno o más componentes y ejecute GExperts, Componentes a Código.
- Pegue el código generado en su aplicación.
- Elimine los componentes del diseñador visual de formas.
Ejemplo (código de creación de TButton generado de esta manera):
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;
Si anida los controles de ganancia en Cuadros de grupo / Controles de página / Etc ..., creo que es beneficioso que el cuadro de grupo principal también sea el propietario. He notado una gran disminución en los tiempos de cierre de la ventana al hacer esto, en lugar de tener al propietario siempre como el formulario principal.
Solo me gustaría agregar que al agregar controles dinámicamente ... es una buena idea agregarlos a una lista de objetos (TObjectList) como se sugiere en <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;
Debe agregar la Unidad ''Contnrs'' a su lista de Usos. Es decir, System.Contnrs.pas la unidad de contenedores base Y puede tener muchas listas de objetos. Sugiero usar un TObjectList para cada tipo de control que use, por ejemplo,
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
esto le permite manipular / administrar fácilmente cada control, ya que sabrá qué tipo de control es, por ejemplo,
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;
Esto le permite usar los métodos y las propiedades de ese control. No se olvide de crear las listas de objetos, tal vez en el formulario crear evento ...
checkBoxCntrlsList := TObjectList.Create;
comboboxCntrlsList := TObjectList.Create;
Este es un ejemplo de cómo emular una etiqueta de botón en 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.