Domanda

Come posso creare un componente in fase di esecuzione e quindi lavorare con esso (cambiando proprietà, ecc.)?

È stato utile?

Soluzione

Dipende se si tratta di un componente visivo o non visivo. Il principio è lo stesso, ma ci sono alcune considerazioni aggiuntive per ogni tipo di componente.

Per componenti non visivi

var
  C: TMyComponent;
begin
  C := TMyComponent.Create(nil);
  try
    C.MyProperty := MyValue;
    //...
  finally
    C.Free;
  end;
end;

Per i componenti visivi:

In sostanza i componenti visivi sono creati allo stesso modo dei componenti non visivi. Ma devi impostare alcune proprietà aggiuntive per renderle visibili.

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;

Alcune spiegazioni al codice sopra:

  • Impostando il proprietario del componente (il parametro del costruttore) il componente viene distrutto quando il modulo proprietario viene distrutto.
  • L'impostazione della proprietà Parent rende visibile il componente. Se lo dimentichi, il componente non verrà visualizzato. (È facile perdere quello :))

Se vuoi molti componenti puoi fare come sopra ma in un ciclo:

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;

Questo aggiungerà 10 pulsanti sul bordo sinistro del modulo. Se si desidera modificare i pulsanti in un secondo momento, è possibile memorizzarli in un elenco. ( TComponentList è più adatto, ma dai anche un'occhiata alla proposte dai commenti a questa risposta)

Come assegnare i gestori di eventi:

Devi creare un metodo del gestore eventi e assegnarlo alla proprietà dell'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;

Altri suggerimenti

Per semplificare il processo di creazione del componente di runtime, puoi utilizzare GExperts .

  1. Crea un componente (o più componenti) visivamente e imposta le sue proprietà.
  2. Seleziona uno o più componenti ed esegui GExperts, Componenti su codice.
  3. Incolla il codice generato nella tua applicazione.
  4. Rimuovi i componenti dal designer del modulo visivo.

Esempio (codice di creazione di TButton generato in questo modo):

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;

Vorrei solo aggiungere che quando si aggiungono dinamicamente i controlli ... è una buona idea aggiungerli a un elenco di oggetti (TObjectList) come suggerito in < 1 > da @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;

È necessario aggiungere l'unità "Contnrs" all'elenco degli usi. Ad esempio System.Contnrs.pas l'unità container di base E puoi avere molti elenchi di oggetti. Suggerisco di utilizzare una TObjectList per ogni tipo di controllo che si utilizza per es.

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

questo ti consente di manipolare / gestire facilmente ogni controllo poiché saprai che tipo di controllo è ad esempio

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;

Ciò consente di utilizzare i metodi e le proprietà di quel controllo Non dimenticare di creare le TObjectLists, forse nel modulo create event ...

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

Ma se sicuramente non so quanti componenti voglio creare, ad es. se dipende dalla decisione dell'utente. Quindi, come posso dichiarare i componenti in modo dinamico?

La risposta è stata suggerita: il modo più semplice è un Elenco di oggetti (componenti). TObjectList è il più semplice da usare (in unità contnrs). Le liste sono fantastiche!

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

// Puoi diventare più sofisticato e dichiarare // TNotifyevents e assegnarli ma lascia che sia semplice :)   .   .   .

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;

Un elenco di oggetti può contenere qualsiasi oggetto visivo o meno, ma ciò ti dà un ulteriore sovraccarico di ordinare quali elementi sono - meglio avere elenchi correlati se, ad esempio, desideri più controlli dinamici su pannelli simili.

Nota: come altri commentatori, potrei aver semplificato troppo per brevità, ma spero che tu abbia idea. È necessario un meccanismo per gestire gli oggetti una volta creati e gli elenchi sono eccellenti per questo materiale.

Durante una ricerca su " creazione di un modulo delphi usando un modello basato su xml " ;, trovo qualcosa di utile sottolineando RTTI e usando api open tools (ToolsApi.pas credo). Dai un'occhiata alle interfacce nell'unità.

Molto facile. Chiama Crea. Esempio:

procedure test
var
  b : TButton;
begin
  b:=TButton.Create(nil);
  b.visible:=false;
end;

Questo crea un componente (TButton è un componente) in fase di esecuzione e imposta la proprietà visibile.


Per il costruttore: passa zero se vuoi gestire tu stesso la memoria. Passa un puntatore a un altro componente se vuoi che venga distrutto quando l'altro componente viene distrutto.

Alcuni componenti hanno la precedenza sul metodo "Loaded". Questo metodo non verrà chiamato automaticamente se si crea un'istanza in fase di esecuzione. Verrà chiamato da Delphi quando il caricamento dal file del modulo (DFM) è completo.

Se il metodo contiene codice di inizializzazione, l'applicazione potrebbe mostrare un comportamento imprevisto quando viene creata in fase di esecuzione. In questo caso, controlla se il writer dei componenti ha utilizzato questo metodo.

Se annidate i controlli delle vincite in Riquadri di gruppo / Controlli di pagina / Etc ..., penso che sia utile che anche la casella di gruppo padre sia il proprietario. Ho notato una netta riduzione dei tempi di chiusura della finestra durante questa operazione, invece di avere sempre il proprietario la forma principale.

Questo è un esempio di come emulare il tag del pulsante su 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.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top