Lazarus

Programming => General => Topic started by: rickielynn on November 06, 2017, 12:36:30 am

Title: Multiple forms
Post by: rickielynn on November 06, 2017, 12:36:30 am
I created a program with two forms; Form1 and Form2.  At startup, Form1 appears. A button on Form1 makes Form2 appear by calling Form2.Show.  Form1 is still visible in the background.  When I click back on Form1, Form2 disappears.  I want Form2 to remain visible; ie: be able to click back and forth between them.  I do not want Form2 to be modal.  How can I do this?
Title: Re: Multiple forms
Post by: taazz on November 06, 2017, 12:44:59 am
that is how show works. Either the form2 wend behind form1 when you clicked on form1 or you have extra code in form2 to hide it when it looses focus.
Title: Re: Multiple forms
Post by: soerensen3 on November 06, 2017, 12:57:18 am
You can make it stay on top though if you set FormStyle to fsStayOnTop.
Title: Re: Multiple forms
Post by: rickielynn on November 06, 2017, 01:04:35 am
There is no extra code to hide Form2.  When I click back on Form1, Form2 seems to get minimized.  I want it to remain as it was.  I tried making Form1 a MDIForm, and Form2 a MDIChild, but it didn't seem to help.

I want Form1 to be the main form with menu, etc, and then be able to display other forms on top of it.  I was able to do this in Visual Basic by making Form1 a MDIForm and the other forms MDIChilds.

Any suggestions?
Title: Re: Multiple forms
Post by: rickielynn on November 06, 2017, 01:18:48 am
Yes, soerensen3, making Form2 StayOnTop and Form1 Normal seems to work.  Also made Form2.Visible:=False upon Form2.create so that it does not appear until called upon by Form1.

Thanks!
Title: Re: Multiple forms
Post by: taazz on November 06, 2017, 01:34:25 am
1) make sure that form1 is not maximized after you click on it move it around do you see form2 somewhere behind it? if yes then changing focus brings a form to the top this is how things work.
2) mdi is not working for the win32 widget set for some reason the lcl team did not see any value to implement it.
3) if stay on top solves your problem then try setting the popupParent of form2 to form1 that should solve it too and I prefer it from stay on top forms that will remain on top of other applications as well.
Title: Re: Multiple forms
Post by: RAW on November 06, 2017, 02:58:56 am
Play around with this... (PopupParent, PopupMode as @taazz already mentioned...).
If you don't want that Form2 can be maximized or minimized then use BorderStyle... or play around with Form2.Constraints.MaxHeight and so on...
Title: Re: Multiple forms
Post by: RAW on November 06, 2017, 01:08:26 pm
Code: Pascal  [Select][+][-]
  1. Procedure TForm1.btnShowClick(Sender: TObject);
  2.  Begin
  3.   If Not Assigned(Form2)
  4.   Then
  5.    Begin
  6.     Form2                      := TForm.Create(Self);
  7.     Form2.PopupMode            := pmExplicit;
  8.     Form2.PopupParent          := Self;
  9.     Form2.Caption              := 'Hi.. I am Form2';
  10.     //Form2.ShowInTaskBar        := stAlways; // only interesting with a normal
  11.                                             // window (bsSizeable, bsSingle)...
  12.     Form2.BorderStyle          := bsSizeToolWin;
  13.     //Form2.BorderIcons          := []; // On Windows bsSizeable...
  14.     Form2.Constraints.MaxHeight:= Self.Height-20;
  15.     Form2.Constraints.MinHeight:= 200;
  16.     Form2.Constraints.MinWidth := 150;
  17.     Form2.Constraints.MaxWidth := Self.Width;
  18.     Form2.SetBounds            (Left+50, Top+50, Width Div 2, Height);
  19.     Form2.Show;
  20.    End
  21.   Else Form2.Show;
  22.  End;
  23.  
  24.  
  25. Procedure TForm1.btnCloseClick(Sender: TObject);
  26.  Begin
  27.   If Assigned(Form2)
  28.   Then
  29.    Begin
  30.     Form2.Release;
  31.     Form2:= Nil;
  32.    End;
  33.  End;
Title: Re: Multiple forms
Post by: rickielynn on November 06, 2017, 08:28:08 pm
I found an implementation of MDI forms for Lazarus at http://wiki.lazarus.freepascal.org/LMDI (http://wiki.lazarus.freepascal.org/LMDI).  This looks like what I am trying to do with multiple forms.
Title: Re: Multiple forms
Post by: boz on November 06, 2017, 11:32:11 pm
Hi

MDI is quite old fashioned looking IMHO

For larger projects I embed the form in a panel and have a selection of buttons on the left to show which one I want. this also allows you to split up larger projects into multiple units quite easily not quite MDI but you can also have multiple forms showing at one time like a form embedded in another form and then embedded in another form and example is here => http://rodyne.com/wp-content/uploads/2017/11/catch.png  on this the left hand buttons embed a form in the RHS

so in the above example it you create a project like this => http://rodyne.com/wp-content/uploads/2017/11/catch-ide.png

The code you use to embed the form is like this:

Code: Pascal  [Select][+][-]
  1. var
  2.    RHS:Tform;
  3.  
  4. procedure TMainForm.ShowRHS(FormName:Tform); // Embed named form into RHS Panel and show it. Allows us to split program into different forms
  5. begin
  6.   if RHS<>nil then RHS.Close;
  7.   RHS:=formname;
  8.   RHS.Align := alClient;
  9.   RHS.BorderStyle := bsNone;
  10.   RHS.parent := RHSPanel;
  11.   RHS.show;
  12.   RHSPanel.caption:='';
  13.   cfg.activeform:=string(formname.Name);
  14.   RHS.BorderWidth:=2;
  15. end;      
  16.  
  17. procedure TMainForm.b1Click(Sender: TObject); // all buttons on left hand side have there onclick event go here and we embed the correct form..
  18. begin
  19.   if sender=b1 then ShowRHS(HomeForm) else b1.Font.color:=cldefault;
  20.   if sender=b2 then ShowRHS(SalesOrdersForm) else b2.Font.color:=cldefault;
  21.   if sender=b3 then ShowRHS(DryStockForm) else b3.Font.color:=cldefault;
  22.   if sender=b5 then ShowRHS(SemiFinishedStockForm) else b5.Font.color:=cldefault;
  23.   if sender=b6 then ShowRHS(DemandForm) else b6.Font.color:=cldefault;
  24.   if sender=b7 then ShowRHS(ReportsForm) else b7.Font.color:=cldefault;
  25.   if sender=b8 then ShowRHS(DispatchForm) else b8.Font.color:=cldefault;
  26.   if sender=b9 then ShowRHS(PurchaseOrderForm) else b9.Font.color:=cldefault;
  27.   if sender=b10 then ShowRHS(StockControlForm) else b10.Font.Color:=cldefault;
  28.   if sender=b11 then ShowRHS(AccountingForm) else b11.Font.color:=cldefault;
  29.   if sender=b12 then ShowRHS(WetStockForm) else b12.Font.color:=cldefault;
  30.   TBitBtn(sender).Font.color:=clblue;
  31.   cfg.user.LastForm:=TBitBtn(sender).tag; // so I can save which form the user was looking at and restore it on open
  32. end;
  33.  

Hopefully its obvious and helpful.
Title: Re: Multiple forms
Post by: rwebb616 on January 23, 2021, 02:58:38 am
Boz,

I notice in your code you have a cfg object - how do you handle that aspect of your apps?  Do you make a singleton or something and load at startup? I'm a beginner and trying to get my head around the concepts of classes and user interfaces.  I really like your UI for this app how you embed forms in the RHS. 

What is the font color thing about?

Rich
Title: Re: Multiple forms
Post by: rwebb616 on January 23, 2021, 04:44:31 pm
Yes, I am aware.  I responded because the thread referenced some code I was interested in.  I have no idea if "boz" is even still active - just figured if he is he might get notified and see my question.  I wasn't able to find a way to send a private message (at least that I could find) so I just responded to the thread.

Rich

Edit:  Now that I say that I see it's right under his name ... duh!
Title: Re: Multiple forms
Post by: boz on January 24, 2021, 12:37:24 am
Hi Rich

I'm Still active, just not a frequent visitor.

Nothing high tech here, cfg is not an object, its just a record where I put misc global variables. (I'm quite old fashioned and Easier to just type cfg. in the IDE and the editor will then show all my global variables using autocomplete so I dont have to remember them!)

Code: Pascal  [Select][+][-]
  1. type
  2.   Tcfg=record
  3.     ActiveForm,CompanyName,TempDir,UserDir,PCName,version,build,ReportPrinter,LabelPrinter,PageSize:string;
  4.     LastBackup:TdateTime;
  5.     MsgTimeout,ActivityTimeout,Session:integer;
  6.     IsProduction:boolean;
  7.   end;
  8.  
  9.  var
  10.    cfg:Tcfg;


So basically cfg.activeform is just a string where I store the last form the user selected so when the user starts the program next time it can open the last form they used

Code: Pascal  [Select][+][-]
  1. for i:=1 to screen.FormCount-1 do
  2.   if screen.Forms[i].Name=string(cfg.activeform) then SHOWRHS(screen.Forms[i],f);
  3.  
Title: Re: Multiple forms
Post by: boz on January 24, 2021, 01:06:24 am
My Delphi winery management program uses the exact same principles but has collapsible panels on the left hand side and 60+ different screens it embeds on the right hand side.  https://rodyne.com/wp-content/uploads/2014/04/bulls-i-winemaking.png  It works well for very large projects that would otherwise become unmanageable as you can swap out forms easily when they depreciate or need a rewrite. I'm generally an open source company but both these projects are not open due to the clients wishes so I cannot give you the source but as I said it should be an easy concept to grasp.

Also I'm an electronics engineer who knows just enough programming to be dangerous so there will be other and probably better ways out there :)

Good luck with your project
Title: Re: Multiple forms
Post by: rwebb616 on January 24, 2021, 01:08:39 am
Ok that make sense.  Do you store these in an INI file or the database?  What about database connection information?

I've been searching around trying to find information on kind of the shell of the application and the way to "set up shop" so to speak.  Looking at your screenshot it looks like this database might also do some sort of user authentication too?

Rich
Title: Re: Multiple forms
Post by: boz on January 26, 2021, 03:07:20 am
Your program probably should be aware of multiple users using the same PC and the users using PC's on different sites or at home through a VPN or your clients will hate you.

I store the program and connection file as normal on the C drive and all users get these files as part of the install. The connection file is basically just an encrypted text file which stores several connection strings (for live systems, master systems, test system, development system, logging system, update server, other sites etc.. its amazing how many you end up with once your program/client gets big), I create a single connection file and encrypt it on my development pc and deploy it to all users pcs on install with the program. There are plenty of examples out there to do encryption/decryption of text strings.... best not to store the connection string in your exe!

on multi-site configurations I store which connection the user opens as default in a one byte file in the LOCALAPPDATA folder so it is user specific (if multiple users use this PC) - I use the

Code: Pascal  [Select][+][-]
  1. GetEnvironmentVariable('LOCALAPPDATA') function

to get the folder where it is stored - otherwise if it is single site I just connect to the first connection, if you use an INI file to store the users config and dont use the registry like me this folder would be the best place to store it, however like you deduced I generally store the configuration for the site/user on the database in readable text strings (like an ini) in tables in the database so I or the DBA can see, modify, delete them if required

We only have one user account on the database server and all authentication of users is done through a user table.

When the program starts I read the user table from the master server and match username in user table to the pc username. In the simplest case it could be as simple as the pascal/sql pseudo code below

Code: Pascal  [Select][+][-]
  1.       SELECT UserName FROM account WHERE LoginID="'+GetEnvironmentvariable('username')+'"' then
  2.          raise exception.create('Access is denied to '+GetEnvironmentvariable('username'));
  3.  

I then check any qualifications tables load configuration related to the user/PC to start the app. on multi-site configurations I store some config settings (such as label printers) on the site server.

I also generally have a central updates server and as part of the startup it compares the MD5 value of all the install files on local disk to the MD5 value and version on the server and begins a background update thread if they are out of date. Ditto I have a logging server so I can track what they are doing and what queries are getting executed.

Its worked well for the last 16 years (touch wood) though there are definitely better ways



Title: Re: Multiple forms
Post by: rwebb616 on February 13, 2021, 05:07:41 am
Out of curiosity, what database backends do you typically use?  I'm trying to work with Postgres and running into walls quite often.

Also do you use the graphical components with a data module or do you program it all in code?

I can go through the database tutorials with the components on the main form just fine but when I try to do the same project with a datamodule I get mixed issues - I can't get the dbgrid to populate - I get access violations, I get error messages indicating "database not assigned" .. but nothing like this if I don't use the datamodule.

Thinking about trying to code it but not really sure where to start - probably putting it all in it's own unit.

Rich
Title: Re: Multiple forms
Post by: boz on February 19, 2021, 06:55:52 pm
Database Depends on the client but I use mysql mostly. I dont even know what a data layer is I just have queries linked to dbgrids and open them after I have added the appropriate sql and where clause, writing I just build the update/insert/delete query and execute it (in a transaction if required) , it all works very reliably and is not complex but it wont be best practice
TinyPortal © 2005-2018