Creazione di componenti in fase di esecuzione - Delphi
-
05-07-2019 - |
Domanda
Come posso creare un componente in fase di esecuzione e quindi lavorare con esso (cambiando proprietà, ecc.)?
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 .
- Crea un componente (o più componenti) visivamente e imposta le sue proprietà.
- Seleziona uno o più componenti ed esegui GExperts, Componenti su codice.
- Incolla il codice generato nella tua applicazione.
- 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.