Recent

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

TurboRascal

  • Hero Member
  • *****
  • Posts: 672
  • "Good sysadmin. Bad programmer."™
How to dynamically create components on form?
« on: March 16, 2010, 02:36:39 pm »
I'm not really sure how to dynamically create components on forms at runtime. The classic OOP approach would be something like:


Code: [Select]
var
  Form1: TForm1;
  Button2: TButton;
implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
Button2 := TButton.Create(Form1);

which does not work in this component model. I must admit I don't yet fully understand Delphi/Lazarus/*CL visual component model  :-[ Logic for using  simple OOP like above, or directly accessing procedural widget set interfaces like Win32 (yuck) or GTK+, doesn't help here... And since normally widgets are part of the form class, it puzzles me even more concerning this...  %)

The button is just an example, I'd actually need to create new pages in a page control to create a pseudo-MDI interface (like most editors now do)... How could I do it?
Regards, ArNy the Turbo Rascal
-
"The secret is to give them what they need, not what they want." - Scotty, STTNG:Relics

eny

  • Hero Member
  • *****
  • Posts: 1634
Re: How to dynamically create components on form?
« Reply #1 on: March 16, 2010, 03:18:03 pm »
For example:
Code: [Select]
procedure TForm1.FormCreate(Sender: TObject);
var btn: TButton;
begin
  btn := TButton.Create(self);  
  btn.parent := self;  
  btn.top := ....
  // etc...
end;

<<edit>>
A note on dynamically adding pages to a pagecontrol: this doesn't work well onWinXP, lazaruys 0.9.29.
Components aren't aligned properly, lose settings etc.
The only way I managed to get it working was:
- add a new page to the pagecontrol
- make the newly added page the active one
- add components to the newly added page.
If you don't activate the page you put components on, alignment settings don't work properly.
« Last Edit: March 16, 2010, 03:22:59 pm by eny »
All posts based on: Win10 (Win64); Lazarus 2.0.10 'stable' (x64) unless specified otherwise...

TurboRascal

  • Hero Member
  • *****
  • Posts: 672
  • "Good sysadmin. Bad programmer."™
Re: How to dynamically create components on form?
« Reply #2 on: March 16, 2010, 03:39:45 pm »

Code: [Select]

  btn.parent := self;  



Thank you very much! That was what I missed, setting the parent did the trick ('self' in this case is the TForm1, right?) So it showed on the form...

Another question while we're at it, how do I create components _on_ some other components, also by setting the parent component? Like, something you put on the new page should set the new page as parent?

EDIT: I'm now thinking about assembling a class which already contains all components I need, so I could dynamically create instances of that instead putting new components on the new page. I could also create a frame, so I could construct that visually then create instances of it... Not that I'd need a visual arrangement, since the only visible component would be a datagrid and even that would be stretched to parent...
« Last Edit: March 16, 2010, 03:58:51 pm by Turbo Rascal »
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 #3 on: March 16, 2010, 05:20:11 pm »

Another question while we're at it, how do I create components _on_ some other components, also by setting the parent component? Like, something you put on the new page should set the new page as parent?

Yes. For example,
Button1.Parent := Panel1;


EDIT: I'm now thinking about assembling a class which already contains all components I need, so I could dynamically create instances of that instead putting new components on the new page. I could also create a frame, so I could construct that visually then create instances of it... Not that I'd need a visual arrangement, since the only visible component would be a datagrid and even that would be stretched to parent...

Do not forget to free the controls you create:

Button1.Free;

jarto

  • Full Member
  • ***
  • Posts: 106
Re: How to dynamically create components on form?
« Reply #4 on: March 16, 2010, 07:15:18 pm »

Do not forget to free the controls you create:

Button1.Free;


Only, if you do create them with owner set to nil.

Button1:=TButton.Create(Form1); //Form1 will automatically free it
Button2:=TButton.Create(nil); //Needs to be free'ed with Button2.Free;

TurboRascal

  • Hero Member
  • *****
  • Posts: 672
  • "Good sysadmin. Bad programmer."™
Re: How to dynamically create components on form?
« Reply #5 on: March 16, 2010, 09:35:17 pm »
Needs to be freed when closing its parent component, right? That's something people easily forget ;)

@jarto, you say there is no need to free components if their parent is also their owner? Actually when they have any owner at all?
Regards, ArNy the Turbo Rascal
-
"The secret is to give them what they need, not what they want." - Scotty, STTNG:Relics

eny

  • Hero Member
  • *****
  • Posts: 1634
Re: How to dynamically create components on form?
« Reply #6 on: March 16, 2010, 09:55:17 pm »
Needs to be freed when closing its parent component, right? That's something people easily forget ;)

Not really: it's done automatically if the owner is used in the constructor.
Have a look at TComponent.Create(TheOwner: TComponent)...
All posts based on: Win10 (Win64); Lazarus 2.0.10 'stable' (x64) unless specified otherwise...

TurboRascal

  • Hero Member
  • *****
  • Posts: 672
  • "Good sysadmin. Bad programmer."™
Re: How to dynamically create components on form?
« Reply #7 on: March 17, 2010, 03:17:50 pm »
Ok now, thank you all for your answers. Now I have another doubt concerning dynamic creation, if someone could help...

If I do this:

Code: [Select]
var
  Form1: TForm1;
  Buttons: array [0..9] of TButton;

implementation

procedure TForm1.FormCreate(Sender: TObject);

var i: integer

begin
  for i := 0 to 9 do
  begin
    Buttons[i] := TButton.Create(self);
    with Buttons[i] do
    begin
      Parent := self;
      Top := ... ;
      ...
    end
  end
end;

This creates ten buttons on the form, and it is all that I can get when I declared [0..9]. Since the dynamic array form "Buttons : array of TButton" doesn't work, but AFAIK memory for objects is only allocated when they are Created, would it be any difference in memory allocation if I say "array[0..255]" or "array[0..65535]" for example? Perhaps there is another, more elegant way to declare the object array? (I've never created arrays of objects before  :D )
Regards, ArNy the Turbo Rascal
-
"The secret is to give them what they need, not what they want." - Scotty, STTNG:Relics

eny

  • Hero Member
  • *****
  • Posts: 1634
Re: How to dynamically create components on form?
« Reply #8 on: March 17, 2010, 04:11:12 pm »
Eh, TList??

Code: [Select]
var
  Form1: TForm1;
  ButtonList: TList;

implementation

procedure TForm1.FormCreate(Sender: TObject);

var i: integer
   btn: TButton;    
begin
  // List to store buttons
  ButtonList := TList.Create;
  for i := 0 to 9 do
  begin
    btn := TButton.Create(self);
    btn.Parent := self;
    btn.Top := (i * (btn.Height + 3));
    // ....

    // Store the button for later reference
    ButtonList.Add(btn);
  end;

  // Demo: label all buttons
  for i := 0 to ButtonList.Count-1 do
    TButton(ButtonList[i]).Caption := format('Button %d', [i]);

end;

Note: put all variables related to the form in the form class type specification.

For example:
Code: [Select]
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { private declarations }
    ButtonList: TList;
  public
    { public declarations }
  end;
« Last Edit: March 17, 2010, 04:20:08 pm by eny »
All posts based on: Win10 (Win64); Lazarus 2.0.10 'stable' (x64) unless specified otherwise...

TurboRascal

  • Hero Member
  • *****
  • Posts: 672
  • "Good sysadmin. Bad programmer."™
Re: How to dynamically create components on form?
« Reply #9 on: March 17, 2010, 05:08:17 pm »
Thanks, eny this helps too.
I've browsed around the forum a bit more, and I found this post to be quite helpful :http://www.lazarus.freepascal.org/index.php/topic,8489.msg41166.html#msg41166

There is also a suggestion to use TObjectList, although an example is given how to use an object array anyway. The answer to my previous question was to use SetLength procedure to resize the array. Therefore, I can resize the array when needed. So my code becomes this:

Code: [Select]
var
  Form1: TForm1;
  Buttons : array of TButton;

implementation

procedure TForm1.FormCreate(Sender: TObject);

var i: integer;

begin
  SetLength(Buttons, 10);
  for i := 0 to 9 do
    begin
      Buttons[i] := TButton.Create(self);
      Buttons[i].Parent := (self);
     ...
    end;

  SetLength (Buttons, 20);
  for i := 10 to 19 do
    begin
      Buttons[i] := TButton.Create(self);
      Buttons[i].Parent := (self);
    ...
    end;

end;                               

Btw. I've remembered the answer to my other question, and it is true that memory for OBJECTS themselves isn't allocated, but it's actually array of pointers that is created here. Every pointer is a dword on a 32-bit CPU, so "array [0..65535] of TButton" would actually use 64k dwords in memory, right? :)
Regards, ArNy the Turbo Rascal
-
"The secret is to give them what they need, not what they want." - Scotty, STTNG:Relics

eny

  • Hero Member
  • *****
  • Posts: 1634
Re: How to dynamically create components on form?
« Reply #10 on: March 17, 2010, 06:09:04 pm »
I'd say 'object reference' is a better description than 'pointer'. But basically you're right.

As with most things, understand the consequences of your choices: if you use a TObjectList you have to remember that a TObjectList by default frees all objects in the list. And that's probably not what you want when creating objects components dynamically.

It's better to use constants with a meaningful name than magic numbers (9, 10, 20).

All posts based on: Win10 (Win64); Lazarus 2.0.10 'stable' (x64) unless specified otherwise...

Zoran

  • Hero Member
  • *****
  • Posts: 1831
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: How to dynamically create components on form?
« Reply #11 on: March 17, 2010, 09:04:36 pm »
You can use dynamic arrays of objects, as well as dyn. arrays of anything else. You are right about memory allocations of your static array. If you use dynamic array, you just need to set its length to be big enough before assigning.
I created a small application to show you.
It's attached to this message. I Hope it's helpful.

TurboRascal

  • Hero Member
  • *****
  • Posts: 672
  • "Good sysadmin. Bad programmer."™
Re: How to dynamically create components on form?
« Reply #12 on: March 17, 2010, 10:10:48 pm »
I'd say 'object reference' is a better description than 'pointer'. But basically you're right.
Well, that is designated 'object reference' all right, but technically it is a pointer. ;) That fact was important because I was trying to understand what is actually happening on a lower level... It's always important to understand what the compilers and runtime libraries do in the background, otherwise we could as well write scripts instead of programs ;)

I created a small application to show you.
It's attached to this message. I Hope it's helpful.

Very nice of you to take the effort! This is exactly what I had in mind, and although small, it's a nice and elaborate example of how to use it in practice. Thanks.
Regards, ArNy the Turbo Rascal
-
"The secret is to give them what they need, not what they want." - Scotty, STTNG:Relics

eny

  • Hero Member
  • *****
  • Posts: 1634
Re: How to dynamically create components on form?
« Reply #13 on: March 17, 2010, 10:45:39 pm »
What's in a name...  :D

It's always important to understand what the compilers and runtime libraries do in the background...

Actually, no, that's why we have high level languages.
I wouldn't say it's unimportant, but most of the time it's not really interesting what happens below the surface.
All posts based on: Win10 (Win64); Lazarus 2.0.10 'stable' (x64) unless specified otherwise...

TurboRascal

  • Hero Member
  • *****
  • Posts: 672
  • "Good sysadmin. Bad programmer."™
Re: How to dynamically create components on form?
« Reply #14 on: March 18, 2010, 01:33:55 pm »
Actually, no, that's why we have high level languages.
I wouldn't say it's unimportant, but most of the time it's not really interesting what happens below the surface.

You are right. or more correctly, you SHOULD be right...
Unfortunately, there is almost no way one can create a high performance (think multimedia) or an efficient program (think embedded) without knowing low-level stuff, even with a high-level language. People even use assembly for critical code sections, but careful high-level code optimization can do wonders if you know what's behind the scenes. Of course, for common applications, one could use "even higher level" languages like java and c# - they are so slow you shouldn't need to worry about that anyway :D They are even garbage-collected so you don't even have to worry about forgetting to free something...

Btw. I guess using programming systems like Delphi or Lazarus/FPC is a great middle ground - as compiled languages have always been, in between of assembly and interpreted languages. You can have pretty fast programs without knowing arcane details of assembly, yet you can easily create them, not needing those nasty bytecode VM's i mentioned above ;) Anything else becomes the matter of code tweaking...

On the other side, when you use compilers that produce "real" programs, you do have performance impact if you allocate unneeded memory, provoke copying a string without knowing (see below), and such stuff. Not to mention freeing and memory leaks... Caring for such stuff helps develop good programming practices, people say...

I'd like to quote two nice articles on similar subjects. This one shows the benefits of knowing how the strings are handled (in C for this example):
http://www.joelonsoftware.com/articles/fog0000000319.html
This one deals with good programming practices and relying too much on help of high-level languages: http://www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html

And well, I do find things below the surface interesting, it must be because my work primarily deals with hardware and OS intrinsics. I guess programmers approaching from the other side, e.g. from mathematics or business perspective, don't really care for all that, they just want to get their task done without tinkering with nuts and bolts of computers...
« Last Edit: March 18, 2010, 05:18:42 pm by Turbo Rascal »
Regards, ArNy the Turbo Rascal
-
"The secret is to give them what they need, not what they want." - Scotty, STTNG:Relics

 

TinyPortal © 2005-2018