Recent

Author Topic: How to dynamically create components on form?  (Read 62090 times)

TurboRascal

  • Hero Member
  • *****
  • Posts: 672
  • "Good sysadmin. Bad programmer."™
Re: How to dynamically create components on form?
« Reply #15 on: March 19, 2010, 12:27:00 pm »
Ok now, let me get off the soapbox and back to the topic ;)

Now I'm continuing towards creating a class for the dynamic objects to create. For example, to dynamically create panels with two buttons, to avoid creating every object for itself and reparenting buttons etc. I'd create a class like this:

Code: [Select]
 
type
...
TMyPanel = class (TPanel)
    ButtonA: TButton;
    ButtonB: TButton;
  end;      

Then I create a dynamic instance of it, like before:

Code: [Select]
procedure TForm1.FormCreate(Sender: TObject);
begin
  MyPanel := TMyPanel.Create(self);
  MyPanel.Parent := Self;
end;  

Then I get a panel. What needs to be done to create / initialize buttons which don't exist as objects in that moment?

I must note that I've tried doing this by using a frame, and it's actually a perfect solution. It's created visually, then it's simply added to the uses list, created a object using the frame's class, and 'reparented' it to the control where I wanted it to be. Yet I'd still like to know how could it be done manually like above... It's easy to use frames (and maybe object lists like someone mentioned somewhere), but I believe it's beneficial to understand how to use the language fully and without aids if need be.
Regards, ArNy the Turbo Rascal
-
"The secret is to give them what they need, not what they want." - Scotty, STTNG:Relics

Zoran

  • Hero Member
  • *****
  • Posts: 1831
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: How to dynamically create components on form?
« Reply #16 on: March 19, 2010, 01:49:45 pm »

Code: [Select]
 
type
...
  TMyPanel = class (TPanel)
    ButtonA: TButton;
    ButtonB: TButton;
  end;      

Then I create a dynamic instance of it, like before:

Code: [Select]
procedure TForm1.FormCreate(Sender: TObject);
begin
  MyPanel := TMyPanel.Create(self);
  MyPanel.Parent := Self;
end;  

Then I get a panel. What needs to be done to create / initialize buttons which don't exist as objects in that moment?


The best approach in my opinion is to add constructor and destructor in your TMyPanel class:

Code: [Select]
 TMyPanel = class(TPanel)
  private
    ButtonA: TButton;
    ButtonB: TButton;
  public
    constructor Create(TheOwner: TComponent); override;
    destructor Destroy; override;
  end;

....

constructor TMyPanel.Create(TheOwner: TComponent);
begin
  inherited Create(TheOwner);

  ButtonA := TButton.Create(Self); // Note that here "Self" is MyPanel object,
  ButtonA.Parent := Self;          // NOT form!

  ButtonB := TButton.Create(Self);
  ButtonB.Parent := Self;

// and code here to position buttons in panel...

end;

destructor TMyPanel.Destroy;
begin
  ButtonB.Free;
  ButtonA.Free;

  inherited Destroy;
end;



The application is attached!
« Last Edit: March 19, 2010, 01:54:20 pm by Zoran »

eny

  • Hero Member
  • *****
  • Posts: 1634
Re: How to dynamically create components on form?
« Reply #17 on: March 19, 2010, 04:11:12 pm »
Another approach is applying the decorator design pattern.
All posts based on: Win10 (Win64); Lazarus 2.0.10 'stable' (x64) unless specified otherwise...

jarto

  • Full Member
  • ***
  • Posts: 106
Re: How to dynamically create components on form?
« Reply #18 on: March 19, 2010, 07:57:15 pm »
Zoran, that is a nice and clear example. However, I think that the destructor is not needed as ButtonA and ButtonB are owned by the panel. Hence, they are automatically free'ed when the panel is free'ed.

Zoran

  • Hero Member
  • *****
  • Posts: 1831
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: How to dynamically create components on form?
« Reply #19 on: March 20, 2010, 09:11:24 am »
Zoran, that is a nice and clear example. However, I think that the destructor is not needed as ButtonA and ButtonB are owned by the panel. Hence, they are automatically free'ed when the panel is free'ed.

Yes, you are right. However, I prefer to free myself what I created in code.

eny

  • Hero Member
  • *****
  • Posts: 1634
Re: How to dynamically create components on form?
« Reply #20 on: March 20, 2010, 10:55:31 am »
However, I prefer to free myself what I created in code.

Normally I'd agree: but (visual) components, being what they are, save us the trouble of having to free them manually.
That means less code and less chances to make mistakes  :)
All posts based on: Win10 (Win64); Lazarus 2.0.10 'stable' (x64) unless specified otherwise...

garlar27

  • Hero Member
  • *****
  • Posts: 652
Re: How to dynamically create components on form?
« Reply #21 on: March 20, 2010, 11:12:06 pm »
If you need AND ONLY IF YOU NEED to destroy a component at runtime you can do something like this:

Code: [Select]
var
   AWinControl: TWinControl;
begin
  AWinControl := ThisComponent;
  ThisCommponent.Parent.RemoveComponent(AWinControl);
  FreeAndNil(AWinControl);
end;

NOTE: MORE OR LESS THIS IS THE SAFE WAY TO REMOVE A COMPONENT. BUT I MIGHT BE FORGETTING SOMETHING, SO YOU BETTER SEARCH IN HELPS AN DOCUMENTS HOW TO DO IT. It's been a quite long time since last time I did something like that and I can't remember exactly the right way to do it.

Good luck!!!

Zoran

  • Hero Member
  • *****
  • Posts: 1831
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: How to dynamically create components on form?
« Reply #22 on: March 21, 2010, 06:38:08 am »
If you need AND ONLY IF YOU NEED to destroy a component at runtime you can do something like this:

Code: [Select]
var
   AWinControl: TWinControl;
begin
  AWinControl := ThisComponent;
  ThisCommponent.Parent.RemoveComponent(AWinControl);
  FreeAndNil(AWinControl);
end;

NOTE: MORE OR LESS THIS IS THE SAFE WAY TO REMOVE A COMPONENT. BUT I MIGHT BE FORGETTING SOMETHING, SO YOU BETTER SEARCH IN HELPS AN DOCUMENTS HOW TO DO IT. It's been a quite long time since last time I did something like that and I can't remember exactly the right way to do it.

Good luck!!!

1. RemoveComponent has nothing to do with parent, but it removes the component from owner's ComponentList. Your code is wrong, as parent and owner can be diferrent.

2. RemoveComponent is quite pointless if you destroy the component in the next line as you do.
It can be used for example if you want to destroy the owner and not this component.

jarto

  • Full Member
  • ***
  • Posts: 106
Re: How to dynamically create components on form?
« Reply #23 on: March 21, 2010, 07:09:50 am »
Yes, you are right. However, I prefer to free myself what I created in code.

In that case it's a good idea to set the owner to nil in create:

ButtonA:=TButton.Create(nil);
ButtonB:=TButton.Create(nil);

Zoran

  • Hero Member
  • *****
  • Posts: 1831
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: How to dynamically create components on form?
« Reply #24 on: March 22, 2010, 02:45:12 pm »
Yes, you are right. However, I prefer to free myself what I created in code.

In that case it's a good idea to set the owner to nil in create:

ButtonA:=TButton.Create(nil);
ButtonB:=TButton.Create(nil);

Yes, I agree that it is a better programming practise. However, I used it in my example though, just not paying attention to the "details".

BUT, thanks to this post of yours, now I have found an article which explains that it is more than that! Read this: http://delphi.about.com/od/kbcurt/ss/dynamiccreateno.htm

So, using nil is not only better programming practise, but doing otherwise should be actually regarded as bad code, as it can have serious impact on performance.

markusloew

  • Newbie
  • Posts: 4
Re: How to dynamically create components on form?
« Reply #25 on: April 20, 2012, 04:37:56 pm »
I hope there will be read my late question to this topic:

How do I detect and process a mouse click or other events on this object "buttons" ?
Or more general: How do I detect events on objects having been created dynamically?

markusloew

  • Newbie
  • Posts: 4
Re: How to dynamically create components on form?
« Reply #26 on: April 20, 2012, 04:50:33 pm »
Sorry for that question: The answer is so simple !!!

buttons.onclick := @buttonsclick ;

 


xxpyton

  • Newbie
  • Posts: 5
Re: How to dynamically create components on form?
« Reply #27 on: December 15, 2012, 08:00:39 pm »
hello guys,
i'm new here and started learning programming a few weeks ago %)  i'm trying to add combo boxes to my form by pressing a button, but it doesn't work (nothing happens, only TList.Count increases):

Code: [Select]
procedure TForm1.btnAddComboBoxClick(Sender: TObject);
var
  i: integer;
begin
  cbText:= TComboBox.Create(Self);
  cbText.Parent:= Self;
  cbText.Top:= btnAddComboBox.Top + btnAddComboBox.Height + 1;
  cbText.Left:= btnAddComboBox.Left;
  cbText.Height:= btnAddComboBox.Height;
  cbText.Width:= 256;
  for i:= 0 to slData.Count-1 do
  begin
    cbText.Items[i]:= slData.Names[i];
  end;
  cbList.Add(cbText);
  TComboBox(cbList[cbList.Count-1]).Top:= TComboBox(cbList[cbList.Count-1]).Top + TComboBox(cbList[cbList.Count-1]).Height + 1 ;
end;
 

i took a look on eny's example for this and thought by creating again another TComboBox on cbText i would get a second one, but it seems to be the same box at the same address or did i access the TList wrong?

What is really new to me is this:
TComboBox(cbList[cbList.Count-1])

What happens in this expression? i get the pointer to what? cbText or a pointer to the copy of the data stored in cbText when it was added to the cbList? and why do i use the class(?) TComboBox with brackets?

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: How to dynamically create components on form?
« Reply #28 on: December 15, 2012, 08:24:29 pm »
, but it doesn't work (nothing happens, only TList.Count increases):
What do you mean? What TList, what Count?

Code: [Select]
  for i:= 0 to slData.Count-1 do
  begin
    cbText.Items[i]:= slData.Names[i];
  end;
You never created the items, yet you are assigning values to uninitialized array. I assume you meant something like:
Code: [Select]
  for i:= 0 to slData.Count-1 do
  begin
    cbText.Items.Add(slData.Names[i]);
  end;

Last line seems a little confusing written like that, you can do:
Code: [Select]
with TComboBox(cbList[cbList.Count-1]) do
  Top:=Top+Height+1;

TComboBox(cbList[cbList.Count-1]) is a pointer to TComboBox component itself. Component means all the strings, position data and others.

Blaazen

  • Hero Member
  • *****
  • Posts: 3241
  • POKE 54296,15
    • Eye-Candy Controls
Re: How to dynamically create components on form?
« Reply #29 on: December 15, 2012, 08:34:32 pm »
Code: [Select]
procedure TForm1.btnAddComboBoxClick(Sender: TObject);
var
  i: integer;
begin
  cbText:= TComboBox.Create(Self);
  cbText.Parent:= Self;
  i:=cbList.Items.Count;
  cbText.Name:='Combo'+inttostr(i);
  cbText.Top:= btnAddComboBox.Top + i*(btnAddComboBox.Height + 1);
  cbText.Left:= btnAddComboBox.Left;
  cbText.Height:= btnAddComboBox.Height;
  cbText.Width:= 256;
  cbList.AddItem(cbText.Name, cbText);
end;               
You can see on the last line, you add string together with object to combobox.
You can acces this object: TComboBox(cbText.Items.Objects)

Quote
What is really new to me is this:
TComboBox(cbList[cbList.Count-1])
It is error.
You can do
Code: [Select]
cbText.Items[i]to get strings or
Code: [Select]
cbText.Items.Objects[i]to get objects as I mentioned above.
Lazarus 2.3.0 (rev main-2_3-2863...) FPC 3.3.1 x86_64-linux-qt Chakra, Qt 4.8.7/5.13.2, Plasma 5.17.3
Lazarus 1.8.2 r57369 FPC 3.0.4 i386-win32-win32/win64 Wine 3.21

Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

 

TinyPortal © 2005-2018