Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I refer to components created at runtime rather than in the form designer?

i have a little problem. I'm trying to create in Delphi7 a list of components at run-time and to resize them with form's .OnResize event but no use... i can't figure out how to do it.

Here's my code:

procedure TForm1.Button1Click(Sender: TObject);
var
  //ExtCtrls
  panel: TPanel;
  memo: TMemo;
  splitter: TSplitter;
  list: TListBox;
begin
  panel := TPanel.Create(Self);
  list := TListBox.Create(Self);
  splitter := TSplitter.Create(Self);
  memo := TMemo.Create(Self);

  with panel do
  begin
    Parent := Form1;
    BevelOuter := bvNone;
    Top := 12;
    Left := 12;
    Height := Form1.Clientheight - 24;
    Width := Form1.Clientwidth - 24;
  end;

  with list do
  begin
    Parent := panel;
    Align := alClient;
    Top := 0;
    Height := panel.Height;
  end;

  with splitter do
  begin
    Parent := panel;
    Top := 0;
    Width := 12;
    Align := alLeft;
  end;

  with memo do
  begin
    Parent := panel;
    Top := 0;
    Left := 0;
    Width := round(panel.Width / 4) * 3;
    Height := panel.Height;
    Align := alLeft;
  end;
end;

Do i have to somehow register their names in order to use them in form's event? Or maybe, to create a class and include them?

Any kind of help is really appreciated! Thank you in advance.

like image 266
Dimitris Nats Avatar asked Dec 17 '11 19:12

Dimitris Nats


3 Answers

Your variables are local to the procedure where they are created so you can't refer to them using those variables when outside that procedure. The solution is to make them fields of the form class.

type
  TForm1 = class(TForm)
    procedure FormResize(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    FPanel: TPanel;
    FMemo: TMemo;
    FSplitter: TSplitter;
    FList: TListBox;
  end;

Then your FormResize event handler can refer to them.

procedure TForm1.FormResize(Sender: TObject);
begin
  if Assigned(FPanel) then
  begin
    ...
  end;
end;

Don't forget to remove the local variables from Button1Click and use the fields instead.

procedure TForm1.Button1Click(Sender: TObject);
begin
  FPanel := TPanel.Create(Self);
  ...
end;
like image 188
David Heffernan Avatar answered Nov 16 '22 21:11

David Heffernan


Although David's answer is also very correct, I thought I would take a moment and go into some more detail. By the looks of it, you seem to be very new with Delphi. There is a very common issue with beginners, which David doesn't address in his answer, pertaining to creating and freeing these objects. Any and every time you ever call 'Create' on a class, at some point, when you're done with it, you have to also 'Free' that class. Failure to free anything will result in a memory leak, and no one wants that. Freeing is just as simple as creating - until you get into the subject of keeping a list of objects (which you don't need right now).

Let's say you wanted to create a text box (TEdit) control and place it in the center of your form. Now first of all, the Delphi IDE allows you to simply drop these controls in your form, just making sure you know. You don't necessarily need to create/free them yourself, unless there's some special scenario. But doing this is dangerous. For the sake of this example, we're assuming that this TEdit control will be there for the entire duration of your application.

First, you need to declare a variable somewhere for this control. The most reasonable place for this is inside the class where it will be used (in this case, your form which we'll call Form1). When working with variables (aka Fields) in your form, make sure you do not put anything above the private section. Everything above private is intended for auto-generated code by Delphi for anything which has been dropped (and is visual) in your form. Otherwise, any manually created things must go under either private or under public. The public area would be a good place for your control...

type
  TForm1 = class(TForm)
  private

  public
    MyEdit: TEdit;
  end;

Now that it's declared, we have to create (and free) it. It's a good practice that any and every time you ever create something, that you immediately put the code to also free it before you continue working. Make an event handler for your form's OnCreate and OnDestroy events...

procedure TForm1.FormCreate(Sender: TObject);
begin
  MyEdit:= TMyEdit.Create(nil);
  MyEdit.Parent:= Self;
  MyEdit.Left:= (ClientWidth div 2) - (Width div 2);
  MyEdit.Top:= (ClientHeight div 2) - (Height div 2);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if assigned(MyEdit) then MyEdit.Free;
end;

If this object is not created (before creation or after destruction), then you will get an "Access Violation" when trying to use it. This is because your application tries to access an area of the computer's memory which is not allocated or not matching with the type you meant to get.

Well, that's the basics to fix your scenario. However one more thing to show you. Suppose you need to just create an object for a short time, for the duration of a procedure. There's a different approach for this. In your code above, you declared your variable directly within the procedure. This example will show you when it is necessary to do this...

procedure TForm1.Button1Click(Sender: TObject);
var
  MyObject: TMyObject;
begin
  MyObject:= TMyObject.Create;
  try
    MyObject.DoSomething;
    Caption:= MyObject.GetSomething;
  finally
    MyObject.Free;
  end;
end;

You see, as long as MyObject will only be used in this one call to this procedure, then you can declare it here. But if the object is expected to stay in memory after this procedure is over and done with, then things get more complicated. Again, in your case, stick with putting this in the form's class until you're more familiar with dynamically creating objects.

A final note, as mentioned above, you do have the ability to place the TEdit control directly on your form in design-time without writing your own code. If you do this, you need to remember NOT to try to create or free these ones. This is also the case when Delphi will automatically put the code above the private section - is when there's something which you're not supposed to play with.

like image 37
Jerry Dodge Avatar answered Nov 16 '22 21:11

Jerry Dodge


I don't think I am eligible to "comment", so I'm phrasing this as an "answer". If you want to resize your runtime components when their parent changes size, take a good look at the Anchors property. It can save you a lot of work.

like image 37
Max Williams Avatar answered Nov 16 '22 22:11

Max Williams