We help IT Professionals succeed at work.

Creating a form....

Xperl
Xperl asked
on
Medium Priority
224 Views
Last Modified: 2010-04-06
Read Fully Before Answering Please...

I am interested in creating a form at run time by code rather than doing it via the IDE as normal, along with the labels, edit boxes, buttons, etc that i would like on the form.

I would have a main form which would then create and then show the new form at the press of a button on the main form.

Anyone got good working examples and advice of how to do this?  I am willing to increase the points for full on getting this done.
Comment
Watch Question

Commented:
Here is some code I put together over a year ago. The purpose of the form is to track the progress of something and disallowing the user from interacting with the form. Tool the code to fit what you are trying to do. Personally I think you may be better served to develope the forms in the IDE and then call on them when you need them.


var  
  frmProgress, frmAskForBranch: TForm;
  pnlParent: TPanel;
  stStatic: TStaticText;
  prgPrgs: array[0..1] of TProgressBar;
  lblLabels: array[0..1] of TLabel;



      frmProgress := TForm.CreateNew(Self);
      with frmProgress do begin
        BorderIcons := [];
        BorderStyle := bsNone;
        FormStyle := fsStayOnTop;
        Caption := '';
        Position := poScreenCenter;
        Width := 200;
        Font.Name := 'Arial';
        Font.Size := 9;

        pnlParent := TPanel.Create(frmProgress);
        with pnlParent do begin
          Parent := frmProgress;
          Align := alClient;
          BevelWidth := 2;
          Caption := '';
          Visible := True;
        end;

        stStatic := TStaticText.Create(frmProgress);
        with stStatic do begin
          Parent := pnlParent;
          Top := 2;
          Left := 2;
          Alignment := taCenter;
          BorderStyle := sbsNone;
          Color := clActiveCaption;
          Font.Name := 'Arial';
          Font.Size := 9;
          Font.Style := [fsBold];
          Font.Color := clCaptionText;
          Caption := 'Updating';
          Visible := True;
          AutoSize := False;
          Width := frmProgress.Width - 4;
        end;

        for i := 0 to 1 do begin
          lblLabels[i] := TLabel.Create(frmProgress);
          with lblLabels[i] do begin
            AutoSize := False;
            Parent := pnlParent;
            ParentFont := True;
            Width := frmProgress.Width;
            Height := frmProgress.Canvas.TextHeight('A');
            Transparent := True;
            Alignment := taCenter;
            Top := stStatic.Top + stStatic.Height + 6;
            Caption := 'Progress';
            Visible := True;
            if i = 1 then begin
              Top := prgPrgs[0].Top + Height + 7;
              Caption := 'Records Dispached';
            end;
            prgPrgs[i] := TProgressBar.Create(frmProgress);
            with prgPrgs[i] do begin
              Parent := pnlParent;
              Height := lblLabels[i].Height;
              Left := 6;
              Width := pnlParent.Width - (Left * 2);
              Position := 0;
              Top := lblLabels[i].Top + lblLabels[i].Height + 2;
              Visible := True;
            end;
          end;
        end;
        prgPrgs[0].Max := iTrackProg;
        prgPrgs[0].Position := 0;
        Height := prgPrgs[1].Top + prgPrgs[1].Height + 6;
        Show;
        pnlParent.Refresh;
        for i := 0 to 1 do lblLabels[i].Refresh;
      end;


The Crazy One

Commented:
something like this? :

procedure TForm1.Button1Click(Sender: TObject);
var
   form : TForm;
   edit : TEdit;
   ix : integer;
begin
     form := TForm.Create(application);
     form.show;
     for ix := 0 to 9 do
     begin
          edit := TEdit.create(form);
          edit.parent := form;
          edit.Left := 0;
          edit.top := ix*(edit.height+3);
          edit.text := 'This Is Edit #'+intToStr(ix);
     end;
end;

You just have to ensure that your uses clause has all the units that define the VCL's you'l need. Ex, the help says:

Unit
StdCtrls

so to use a TEdit we need make sure that stdCtrls is in the uses clause.


I'm guessing that the following prob. isn't what you're looking for, but you could even try something like this:

procedure TForm1.Button1Click(Sender: TObject);
var
   script : variant;
begin
     script := createOLEObject('ScriptControl');
     script.language := 'JScript';
     script.eval('var ie = new ActiveXObject("InternetExplorer.Application");');
     script.eval('ie.navigate("about:blank");while (ie.busy){};');
//     script.eval('for (ix=0;ix<10;ix++){ edit = ie.document.createElement("INPUT"); edit.type = "text"; ie.document.body.insertBefore(edit, null);};');
     script.eval('for (ix=0;ix<10;ix++){ ie.document.body.innerHTML += "<input value=''This Is Edit #"+ix+"''><br>";};');
     script.eval('ie.visible = 1;');
end;

then yur uses just need's comObj, but you're limited to what ever Interne Explorer can draw.


GL
Mike

Commented:
oooh CrazyOne you beat me while I squandered precious minutes comming up with my scripted HTML example! LOL :)

GL
Mike

Commented:
LOL :>)

Hi Mike

Author

Commented:
Thanks guys

Edey, i shall point some points for you when i close this question to thank you for your input.

CrazyOne, Just a couple of questions on your example:

"frmProgress := TForm.CreateNew(Self);"

If i move the example to another unit other than the main form the above line gives an error - the "Self" is undeclared - so how can i fix that?

Also, i assume i should free the form after i am finished with it - such as on exiting the routine - but does that free all of the components i used on the form as well or should i free them as well then free the form?

How would i handle the "OnClick" event of a button on the form if it is created this way?  (ie: if i want to have a cancel and ok button on the form - i assume once i find out how to do it with the Onclick event the other events are similar).



Commented:
>Edey, i shall point some points for you when i close this
>question to thank you for your input.

Oh, that's certainly not necessary. if you're interested in the html solution (which was more a gag then anything :) that's one thing, but CrazyOne's got the line in this one I think.

>"frmProgress := TForm.CreateNew(Self);"
>
>If i move the example to another unit other than the main
>form the above line gives an error - the "Self"
>is undeclared - so how can i fix that?

Try using application instead.

>Also, i assume i should free the form after i am finished
>with it - such as on exiting the routine -
>but does that free all of the components i used on the
>form as well or should i free them as well then
>free the form?

the component you pass in the constructor (self, or application for example) is the new component's _owner_. The owner is responsible for freeing it's children when it is destroyed. So if I do this:

with TForm.create(application) do
 TEdit.create(self);
application.terminate;

I should _not_ have a memory leak.

>How would i handle the "OnClick" event of a button on the
>form if it is created this way?  (ie: if i

You'll need to have a method (not just a procedure) already written. Then you can just assign the onClick property to the new button.

here's an example:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, stdCtrls,
  Dialogs;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    procedure handleMyDynBtnClick(Sender: TObject);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
     with TButton.create(self) do
     begin
          parent := self;
          caption := 'Quit';
          onClick := handleMyDynBtnClick;
     end;
end;

procedure TForm1.handleMyDynBtnClick(Sender: TObject);
begin
     application.terminate;
end;

end.

>want to have a cancel and ok button on the form - i
>assume once i find out how to do it with the Onclick
>event the other events are similar).

Yup, you just have to have a method that takes the right parameters & you're set.

GL
Mike

Commented:
Nice explaination Mike.

Also if you explicitly free the form you can do this

TForm.create(Nil);

By using the nil this will sometimes speed up the creation process but doing so requires that the form must be freed explicitly because it is not owned by anything.

As long as the components you add to your newly created form are owned by the form or parented to a component that is owned by the Form you don't need to explicitly free these components because when the Form is freed the Form will free the components.

Author

Commented:
Thanks.

I have moved all of the code i use to create a form and a single button on it to a unit (not a new form unit) and all works except that i am still unable to get the onclick event to work - it won't compile.

Any ideas?

Commented:
What kind of error do yet get at compile time?

GL
Mike

Author

Commented:
This is the code i am using at the moment - however, i am unable to get an onclick event to work no matter what i do.

Any suggestions on what i am doing wrong?

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses unit2;
{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
infoForm;

end;

end.


unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

procedure InfoForm;

implementation

procedure InfoForm;
var
Info           :  TForm;
  Okbttn       :  TButton;
  Label1       :  TLabel;
begin

  Info := TForm.CreateNew(nil);
     with Info do begin

       BorderStyle := bsSingle;
       Caption := 'Information';
       Position := poScreenCenter;
       Height:=376;
       Width := 325;
       Font.Name := 'MS Sans Serif';
       Font.Size := 8;
       end;


   OkBttn := TButton.Create(Info);
      with OkBttn do begin
         Parent := Info;
         Font.Name := 'MS Sans Serif';
         Font.Size := 8;
         Height:=20;
         Width:=50;
         Caption:= '&OK';
         Left:=256;
         Top:=300;
         Visible := True;
       end;

 info.Showmodal;
 info.free;
 end;

end.

Author

Commented:
btw, edey - i have posted 200 points for you toward your help on this matter.

Commented:
Ok, I had written a nice post that clearly showed why & how, but then EE died & I lost it :(. So here's the abridged version:

you could change:

procedure InfoForm

to:

procedure InfoForm(btnClickHandler : TNotifyEvent);

and change:

OkBttn := TButton.Create(Info);
     with OkBttn do begin
        Parent := Info;
        Font.Name := 'MS Sans Serif';
        Font.Size := 8;
        Height:=20;
        Width:=50;
        Caption:= '&OK';
        Left:=256;
        Top:=300;
        Visible := True;
      end;

to:

OkBttn := TButton.Create(Info);
     with OkBttn do begin
        onClick := btnClickHandler;
        Parent := Info;
        Font.Name := 'MS Sans Serif';
        Font.Size := 8;
        Height:=20;
        Width:=50;
        Caption:= '&OK';
        Left:=256;
        Top:=300;
        Visible := True;
      end;

then you call infForm with the name of a button handler. So, for example, if you called:

infoForm(button1Click);

then when you click the button on the new form you'd get another new form.

GL
Mike

Author

Commented:
Sorry ebey, you lost me on the idea of that one.  

What understand of it all so far is that i have have a form (form1) on which i habe a button.  When someone clicks on the button it creates a new form with a label and button on it (info form).  So how do i get it that when someone presses the OK button it closes and destroys the form?

Commented:
Xperl I don't see in your code how you are telling the button or buttons on the newly created form which event to use. As Mike has pointed out your code needs to point to predisigned enent

TheButton.OnClick := TheEvent;

The errors you mentioned, were they errors reported while compiling or after the app was running?

Author

Commented:
Hi CrazyOne - I got errors when i went to compile due to my trying to work out how to assign an event to the button on the new form.

It is how i actually assign the action/event to the button so i can close it again and destroy it that i am looking for.

Commented:
Oh OK thanks. Hmm I am a bit busy right at the moment but I will look into it this a little later and see what I can come up with. :>)

Author

Commented:
thanks CrazyOne

Commented:
Here is something I just put together real quick so it aint pretty, hehe, and it worked. Give it a try and see what happens. :>)

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons;

type
  TForm1 = class(TForm)
    Button1: TButton;
    BitBtn1: TBitBtn;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    procedure TheClick(Sender: TObject);
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

var
  frmNew: TForm;
  btnNew: TButton;

procedure TForm1.TheClick(Sender: TObject);
begin

  frmNew.Close;

end;
{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin

  frmNew := TForm.CreateNew(nil);
  with frmNew do begin
    BorderIcons := [biSystemMenu];
    BorderStyle := bsSingle;
    Caption := '';
    Position := poScreenCenter;
    Height := 200;
    Width := 200;
    Font.Name := 'Arial';
    Font.Size := 9;
    btnNew := TButton.Create(frmNew);
    btnNew.Parent := frmNew;
    btnNew.Left := 10;
    btnNew.Top := 10;
    btnNew.Height := 25;
    btnNew.Width := 25;
    btnNew.OnClick := Form1.TheClick;
    btnNew.Visible := True;
    btnNew.Refresh;
    ShowModal;
    Free;
  end;


end;

end.

Commented:
CrasyOne's last example should do the trick - just see my previous comment for an explanation of how to get to the event handler from unit1 into unit2

GL
Mike

Author

Commented:
Thanks Edey, your sample worked.

One final question before i close the question - (if i get an answer to this last question i shall add an extra 100 points to whoever gave the best answer)

If i use a second unit (not a form unit just a .pas unit) and move the following routine into it:

 frmNew := TForm.CreateNew(nil);
 with frmNew do begin
   BorderIcons := [biSystemMenu];
   BorderStyle := bsSingle;
   Caption := '';
   Position := poScreenCenter;
   Height := 200;
   Width := 200;
   Font.Name := 'Arial';
   Font.Size := 9;
   btnNew := TButton.Create(frmNew);
   btnNew.Parent := frmNew;
   btnNew.Left := 10;
   btnNew.Top := 10;
   btnNew.Height := 25;
   btnNew.Width := 25;
   btnNew.OnClick := Form1.TheClick;
   btnNew.Visible := True;
   btnNew.Refresh;
   ShowModal;
   Free;
 end;

What would i have to do do have the onclick event close the window?  (ie: perform frmNew.close)

Commented:
Well, I would think the easiest way would be to declare (in this new unit, the "non form" one) another object that looks something like this:

type
 TNotifyEventWrapper = class(TObject)
 public
  procedure doEvent(Sender : TComponent);
 end;

...

var
 ew : TNotifyEventWrapper;

...

procedure TNotifyEventWrapper.doEvent(sender : TComponent);
begin
 frmNew.modalResult := mrOk;
end;

...

if ew = nil then
 ew := TNotifyEventWrapper.create(application);
frmNew := TForm.CreateNew(nil);
with frmNew do begin
 onClick := ew.doEvent;

...


GL
Mike

Author

Commented:
Thanks Edey - I worked about with the code you gave me and adapted it a little and it worked!

I shall increase your points to 300 on the other posting for you and shall give Crazyone the 200 from this posting.

Commented:
Hry, just glad to see you get a solution :) - I remember trying to work out some of this stuff when D2 was all the rage - How i wish I had a place like this then!

GL
Mike

Explore More ContentExplore courses, solutions, and other research materials related to this topic.