Creating multiple (maybe hundreds) of almost-identical TForms lazarus / 10-Designer ---------------------------------------------------------------------- torbente · 2014-09-02 10:38 I have a server-client app already working very well and stable (using indy) Now i want add a new feature to it, but i need create dinamical forms in the server code, almost all them identical to one already designed with the ide. What it means? I added a new form in the lazarus IDE, lets say it is called 'unitStandard'; it have it own buttons, labels, etc, but at runtime i need a lot of identical forms with just some diferent values to their controls variables (but containing the same controls). Can it be done? If, so, how? Thanks in advance ---------------------------------------------------------------------- taazz · 2014-09-02 10:42 Code: [Select]uses uMyForm; procedure DomyStuff1; var   vFrm :TMyForm; begin   vFrm := TMyForm.Create(application);   vFrm.MyButton.Caption := 'This is Stuff1';   vfrm.Show; end; procedure DomyStuff2; var   vFrm :TMyForm; begin   vFrm := TMyForm.Create(application);   vFrm.MyButton.Caption := 'This is Stuff 2';   vfrm.Show; end; Keep in mind that it is impossible to add code to the form at runtime but you can set any property or control's property that they are already on the form. ---------------------------------------------------------------------- torbente · 2014-09-02 11:12 If i assign aplication as owner, those new forms will remain open until the main form is closed, even if they are called from a different form? How i can assign different values to their position at creation, relative to the mainform? Lets say mainform.left+10, mainform.y+10 ? Thanks a lot ---------------------------------------------------------------------- wp · 2014-09-02 11:17 Quoteit is impossible to add code to the form at runtime@taazz: Are you sure that this statement is true? What's wrong with the next code? Code: [Select]procedure DomyStuff1; var   vFrm :TMyForm;   vEdit: TEdit; begin   vFrm := TMyForm.Create(application);   vFrm.MyButton.Caption := 'This is Stuff1';   vEdit := TEdit.Create(vFrm);   vEdit.Parent := vFrm;   vEdit.Left := 10;   vEdit.Text := 10;   vEdit.Text := 'Something';   vfrm.Show; end; ---------------------------------------------------------------------- taazz · 2014-09-02 11:37 Quote from: wp on September 02, 2014, 01:17:40 pmQuoteit is impossible to add code to the form at runtime@taazz: Are you sure that this statement is true? What's wrong with the next code? Code: [Select]procedure DomyStuff1; var   vFrm :TMyForm;   vEdit: TEdit; begin   vFrm := TMyForm.Create(application);   vFrm.MyButton.Caption := 'This is Stuff1';   vEdit := TEdit.Create(vFrm);   vEdit.Parent := vFrm;   vEdit.Left := 10;   vEdit.Text := 10;   vEdit.Text := 'Something';   vfrm.Show; end; I do not consider creation of a control as "code addition" you dynamically create something that the code is already in the executable. When I said  "code addition" I was refferring to the ability that all dynamic (scripting really) languages (like python, javascript etc) have to be able to build the code to be executed at runtime and use that as variable to an event. If I had to make a distinction between the two I would say that what you describe is "code insertion" in any case I would rather avoid introducing any new acronyms we already have way to many of them. ---------------------------------------------------------------------- howardpc · 2014-09-02 12:50 Quote from: torbente on September 02, 2014, 01:12:47 pmHow i can assign different values to their position at creation, relative to the mainform? Lets say mainform.left+10, mainform.y+10 ? The following unit shows one way to do this. Code: [Select]unit mainIdenticalForms; {$mode objfpc}{$H+} interface uses   SysUtils, Forms, Controls, StdCtrls, Classes; type   { TbaseForm }   TbaseForm = class(TForm)     Button1: TButton;     procedure Button1Click(Sender: TObject);     procedure FormCreate(Sender: TObject);   end;   procedure CreateBaseFormCopies(nCopies: integer); var   baseForm: TbaseForm; implementation uses Dialogs; procedure CreateBaseFormCopies(nCopies: integer); var   bf: TbaseForm;   button: TButton;   i, j: Integer; begin   for j:=0 to nCopies-1 do begin     bf:=TbaseForm.CreateNew(Application, i);     i:=j+1;     bf.Caption:=Format('Form number %d',[i]);     bf.Left:=baseForm.Left+(i*30);     bf.Top:=baseForm.Top+(i*30);     button:=TButton.Create(bf);     button.Caption:=Format('Button#%d',[i]);     button.Autosize:=True;     button.Left:=10;     button.Top:=10;     button.Tag:=i;     button.OnClick:=@baseForm.Button1Click;     button.Parent:=bf;     bf.Show;   end; end; {$R *.lfm} { TbaseForm } procedure TbaseForm.Button1Click(Sender: TObject); begin   ShowMessageFmt('You clicked Button#%d',[(Sender as TButton).Tag]); end; procedure TbaseForm.FormCreate(Sender: TObject); begin   CreateBaseFormCopies(5); end; end. ---------------------------------------------------------------------- torbente · 2014-09-02 13:33 Great, i already solved this perfectly!! Thanks a lot!! And how i could acces each new form? How i could identify each one so i could acces it separately from the main form? ---------------------------------------------------------------------- eny · 2014-09-02 15:29 Collect them in a TList and set the Tag property to each form with a unique value (form 1: tag = 1; form 2: tag = 2 etc. Main form: Tag remains 0). ---------------------------------------------------------------------- minesadorada · 2014-09-02 15:42 ..or declare an open array of TForm and use SetLength - would that work? ---------------------------------------------------------------------- torbente · 2014-09-03 19:09 An array could be a good workaround, nice suggestion, thanks. I just coded a demo form, and my app opened 5 almost-identical forms. Since it is needed to include some values into each form, i use a couple of TEdits on it, so... CopyForm. := DemoForm.create (application); CopyForm.TEditDefaultValue1 := '20'; CopyForm.Visible := true; TEditDefaultValue1 is different for every copy, and it worked perfect. But all the other variables in unit DemoForm changes everytime each of the copyforms use it, and my app hang out soon. Is there anyway to assign a group of variables to be used just in the form, but the unit have plenty access to it separately? I need that everyform works as a different unit once created. ---------------------------------------------------------------------- Mike.Cornflake · 2014-09-03 19:28 QuoteIs there anyway to assign a group of variables to be used just in the form, but the unit have plenty access to it separately? I need that everyform works as a different unit once created Code: [Select]  TbaseForm = class(TForm)     Button1: TButton;     procedure Button1Click(Sender: TObject);     procedure FormCreate(Sender: TObject);   Private     FVariable: String;  // <-- Declare all your variables in Private.                                  // Each Form will have their own copy...   end; If you're asking what I think you're asking (and I think you are), then the above is the way to declare variables you want to be specific to each instance of the form. FVariable will only be accessible from methods and functions that are part of the form.  Use FormCreate to initialise them. ---------------------------------------------------------------------- torbente · 2014-09-04 01:46 Yes, it is what i was looking for. Every new form created have its own set of variables! I really did not know what the private section was for until now and i have some question in case someone could help me (or point me to a good url where i could  read more) - Are the components also exclusive of each form instance? (components like tStringGrid, TTimer and so on) - I already try, but variables declared in the private section can not be set  when the form is created? - Every form created with the IDE must include one unit? Is there any way to desgin a form without a new unit? Thanks a lot ---------------------------------------------------------------------- engkin · 2014-09-04 02:08 Quote from: torbente on September 04, 2014, 03:46:37 am- Are the components also exclusive of each form instance? (components like tStringGrid, TTimer and so on) Yes. Quote from: torbente on September 04, 2014, 03:46:37 am- I already try, but variables declared in the private section can not be set  when the form is created? They are private to the form class. Member methods of the from can change their values. If you need to change their values from outside the form class methods, then declare them in a public section: Code: [Select]  TSomeForm = class(TForm) ...   Public     SomeVariable: String; ... Quote from: torbente on September 04, 2014, 03:46:37 am- Every form created with the IDE must include one unit? Is there any way to desgin a form without a new unit? Yes. You can add controls to any form at run-time. But why do you need it?. Anyway, try this: Code: [Select]procedure TForm1.Button1Click(Sender: TObject); var   aForm: TForm;   aLabel: TLabel;   aEdit: TEdit; begin   //Create an empty form   aForm := TForm.Create(nil);   //Create a label   aLabel := TLabel.Create(aForm);   aLabel.Left := 10;   aLabel.Top := 10;   aLabel.Caption := 'Hello World';   aLabel.Parent := aForm;   //Create an edit   aEdit := TEdit.Create(aForm);   aEdit.Left := aLabel.Left + aLabel.Width + 10;   aEdit.Top := aLabel.Top;   aEdit.Parent := aForm;   //Show the form   aForm.Show; end; ---------------------------------------------------------------------- taazz · 2014-09-04 02:16 Quote from: torbente on September 04, 2014, 03:46:37 amYes, it is what i was looking for. Every new form created have its own set of variables! I really did not know what the private section was for until now and i have some question in case someone could help me (or point me to a good url where i could  read more) - Are the components also exclusive of each form instance? (components like tStringGrid, TTimer and so on) - I already try, but variables declared in the private section can not be set  when the form is created? - Every form created with the IDE must include one unit? Is there any way to desgin a form without a new unit? Thanks a lot every variable declared in the form can have different value for every form instance regardless of where they are declared (ee private, protected etc). take a look on this starter http://wiki.freepascal.org/Object_Oriented _Programming_with_Free_Pascal_and_Lazarus. 1) each form instance has its own component instances too there is no overlapping between the instances. 2) The private section is what the word says private and doesn't allow access from the outside world, you can create a public method that you can call to set the variables from outside. 3) yes every and each form has a single line {$R *.lfm}, this line instracts the ide to load the lfm file of the form where all your settings are saved with out it you have to write everything by hand no object inspector, no form designer, no visual aid at all. Why what is the problem of having one file per form? you are after all creating them dynamically. ---------------------------------------------------------------------- torbente · 2014-09-08 16:42 Ok, i think i have almost everything ready. One more thing. How i can bringtofront a specific form that already exist? copyform[number].show; copyform[number].bringtofront; copyform[number].visible := true; All  give me errors SIGSEGV. ---------------------------------------------------------------------- taazz · 2014-09-08 16:45 Quote from: torbente on September 08, 2014, 06:42:28 pmOk, i think i have almost everything ready. One more thing. How i can bringtofront a specific form that already exist? copyform[number].show; copyform[number].bringtofront; copyform[number].visible := true; All  give me errors SIGSEGV.  That means that the specific cell is empty or points to an invalid address ---------------------------------------------------------------------- minesadorada · 2014-09-08 18:28 For a dynamic form without resources Myform[index].CreateNew(nil) would be better? ---------------------------------------------------------------------- Fred vS · 2014-09-08 19:39 QuoteAll  give me errors SIGSEGV.  => Code: [Select] if assigned(copyform[number]) then   begin copyform[number].show; copyform[number].bringtofront; copyform[number].visible := true; end; ---------------------------------------------------------------------- torbente · 2014-09-08 20:52 Quote from: taazz on September 08, 2014, 06:45:55 pmQuote from: torbente on September 08, 2014, 06:42:28 pmOk, i think i have almost everything ready. One more thing. How i can bringtofront a specific form that already exist? copyform[number].show; copyform[number].bringtofront; copyform[number].visible := true; All  give me errors SIGSEGV.  That means that the specific cell is empty or points to an invalid address You were rigth, i was using a bad counter to check it. Fixed. ---------------------------------------------------------------------- bylaardt · 2014-09-13 03:46 Forget arrays!!! use a parental control to all this forms, something like thisCode: [Select]var    MyParent:TComponent; begin   MyParent:=TComponent.create(application);   CopyForm.create(MyParent);   CopyForm.create(MyParent);   CopyForm.create(MyParent);     CopyForm(MyParent.Components[0]).caption:='First Form';   Shomessage('I Have '+inttostr(MyParent.componentCount)+' copyforms actives') ;end; ---------------------------------------------------------------------- howardpc · 2014-09-13 07:26 Your naming here is confusing. MyParent is not the parent of the created forms, but their owner. In fact, MyParent is not needed. Your scheme would be simpler if you dispensed with "MyParent" altogether and made the already exisiting Application instance the owner of each form, using the Application.Components[] array to track which forms are visible, how many there are etc. ---------------------------------------------------------------------- cpalx · 2014-09-13 17:39 Why dont you use Tframes todo make the pattern form? Maybe pascal scripts can help you to add code dynamically