Pregunta

¿Cómo puedo crear un componente en tiempo de ejecución y luego trabajar con él (cambiar propiedades, etc.)?

¿Fue útil?

Solución

Depende de 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 debe establecer algunas propiedades adicionales para que sean 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 del código anterior:

  • Al establecer el propietario del componente (el parámetro del constructor), el componente se destruye cuando se destruye el formulario propietario.
  • Establecer la propiedad Parent hace que el componente sea visible. Si lo olvida, su componente no se mostrará. (Es fácil perderse esa :))

Si desea muchos componentes , puede hacer lo mismo que anteriormente 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 adelante, puede almacenarlos en una lista. ( TComponentList es el más adecuado, pero también eche un vistazo a 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;

Otros consejos

Para simplificar el proceso de creación de componentes en tiempo de ejecución, puede usar GExperts .

  1. Cree un componente (o más componentes) visualmente y establezca sus propiedades.
  2. Seleccione uno o más componentes y ejecute GExperts, Componentes para codificar.
  3. Pegue el código generado en su aplicación.
  4. Eliminar los componentes del diseñador de formularios visuales.

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;

Me gustaría agregar eso 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 puedes tener muchas listas de objetos. Sugiero usar una 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 propiedades de ese control No olvides crear las TObjectLists, tal vez en el formulario create event ...

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

Pero si no sé cuántos componentes quiero crear, p. ej. 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 fácil es una Lista de objetos (componentes). TObjectList es el más simple de usar (en los controles de unidades). ¡Las listas son geniales!

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

// Puedes ser más sofisticado y declarar // TNotifyevents y asignarlos, pero vamos a hacerlo simple :)   .   .   .

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 da una sobrecarga adicional de clasificar 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 simplificado demasiado por brevedad, pero espero que tenga la idea. Necesita un mecanismo para administrar los objetos una vez que se crean y las listas son excelentes para estas cosas.

Durante una investigación sobre " crear un formulario delphi usando la plantilla basada en xml " ;, encuentro algo útil señalando RTTI y usando herramientas abiertas api (ToolsApi.pas, creo). Eche un vistazo a las interfaces en la unidad.

Muy fácil. Llamada 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 desea administrar la memoria usted mismo. Pase un puntero a otro componente si desea que se destruya cuando se destruya el otro componente.

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, verifique si el escritor de componentes ha utilizado este método.

Si anida controles ganadores 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 fuerte disminución en los tiempos de cierre de las ventanas al hacer esto, en lugar de que el propietario sea siempre el formulario principal.

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.
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top