Recent

Author Topic: Forms  (Read 5353 times)

ltstrau

  • Newbie
  • Posts: 4
Forms
« on: September 16, 2014, 02:47:49 pm »
So I'm starting an application project, created a couple of forms that represents different interfaces to be used in the application... but how can I link them together?

I mean, let's say, when I apply 'Form2.Show', when the application is being run, a new window for Form2 pops up, with the window of Form1 still existing... The question is How can I make all interfaces created in different forms to exist in ONE SINGLE WINDOW from beginning to end? Is it something like 'clrscr' in program projects that has to be used?

thx for ur attention and sorry for my poor english, i know it might be an idiot question but plz forgive that i'm just a beginner of programming...

« Last Edit: September 16, 2014, 02:51:31 pm by ltstrau »

Mike.Cornflake

  • Hero Member
  • *****
  • Posts: 1260
Re: Forms
« Reply #1 on: September 16, 2014, 03:10:13 pm »
I think you're asking for "Multiple Document Interface" or MDI.  I don't think there is a current ability in Lazarus to achieve this.  There's two known implementations - AnchorDocking and, err, the other one.  But both have known issues.
Personally I've implemented a series of PageControls, and I "dock" each TForm into a TabSheet.  I have mechanisms that allow the user to change the order of TabSheets.  If you're interested in this method I can post some code showing the broad steps to follow.

A few links you may want to read/try
http://wiki.lazarus.freepascal.org/MultiDoc
http://wiki.lazarus.freepascal.org/Anchor_Docking
http://wiki.lazarus.freepascal.org/EasyDockingManager (this is the other implementation I was talking about)
http://wiki.lazarus.freepascal.org/Category:Docking

Microsoft Excel is an example of an MDI interface...
« Last Edit: September 16, 2014, 03:22:16 pm by Mike.Cornflake »
Lazarus Trunk/FPC Trunk on Windows [7, 10]
  Have you tried searching this forum or the wiki?:   http://wiki.lazarus.freepascal.org/Alternative_Main_Page
  BOOKS! (Free and otherwise): http://wiki.lazarus.freepascal.org/Pascal_and_Lazarus_Books_and_Magazines

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Forms
« Reply #2 on: September 16, 2014, 05:37:05 pm »
I think that MDI is supported on windows and QT by the lcl you simple set the main form's style to fsMDIForm and all the others to fsMDIChild. The main form must not have any control or logic on it, it is simple a container for the rest of the mdichildrent forms.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

janasoft

  • New Member
  • *
  • Posts: 30
Re: Forms
« Reply #3 on: September 17, 2014, 09:35:59 am »
If you're interested in this method I can post some code showing the broad steps to follow.

Hello Mike.

I think that a lot of people (including myself) would like to see your method. I think is very good to learn new ways of doing things, especially for newbies  :)
Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-win64-win32/win64

Mike.Cornflake

  • Hero Member
  • *****
  • Posts: 1260
Re: Forms
« Reply #4 on: September 17, 2014, 05:16:45 pm »
Please see attached a simple framework for an application that can have multiple forms docked into a single PageControl.

Well, this was intended to be simple, but I got carried away.  It's pretty much a fully fledged implementation, which possibly overcomplicates the main features...

If you don't want to use the provided framework (don't blame you), then any form you want can be docked into a PageControl simply using
Code: [Select]
MyForm.ManualDock(MyPageControl);To remove your form from a PageControl you do:
Code: [Select]
MyForm.Free;
MyFormsTabsheet.Free;

That's essentially it.  You don't have to read the rest of this post :-)

For those who got this far the attached project is a simple Notepad++ style implementation.  There's one Form only with the notepad functionality, the rest is used to manage the numerous instances of each notepad.

In my opinion, this isn't an MDI implementation.  Only one docked form is able to viewed at a time - I have no provision for side by side views or arrangement of windows etc.  If you need this, then use @Taazz's suggestion above, or one of the docking manager implementations I've listed in my first post...

So, what the example project provides is a mechanism for allowing multiple different forms to be docked into a PageControl.  The only Notepad functionality itself is in the form to be docked.  The rest of the app doesn't have a clue what each docked form actually does.  The example project takes care of reading these docks to and from an inifile (inifile for simplicity).  To change the preferred save mechanism, just change LoadSettings & SaveSettings.

Different Docks can be created using Lazarus Form Inheritance, and creating new docks, each descended from the provided dock base.

There's comments scattered throughout the code, most notably at the top of FormDockBase and DockNotepad.

Overview of attached example:

There are three units. 
* FormMain contains the PageControl and the framework for handling Docked forms.
-   FDocks: TList;   // This is the array that stores each of the created Docks
-   All the dock handling code is hidden away in a series of routines.  Rather than call FDocks directly, code elsewhere in FormMain should call any one of these handling routines:
Code: [Select]
Procedure AddDock(oDock: TdckBase);
Procedure DeleteDock(oDock: TdckBase);
Function DockIndex(oDock: TdckBase): Integer;

Property Dock[iIndex: Integer]: TdckBase;
Property ActiveDock: TdckBase;

* FormDockBase contains the base template of each form that will be docked. 
-   TdckBase should not have any UI elements on it.  Think of it as an abstract base class (though it isn't, it's just a base class)
-   TdckBase knows it's going to be docked into a PageControl, and has assorted routines to help deal with this
-   Any docked forms in use should be descended from TdckBase

* DockNotepad contains a simple example of a Form to be docked.  This descends from TdckBase.  All the UI controls and user functionality are embedded at this level

With all that in mind - here is the key code:
To create and dock a form:
Code: [Select]
oDock := TdckNotepad.Create(nil);  // Nil as owner as we'll handle freeing this ourself
AddDock(oDock);

The important bits of AddDock() are:
Code: [Select]
    oDock.ManualDock(pcMain);
    oDock.TabSheet := TTabSheet(oDock.Parent);
    oDock.TabSheet.Caption := oDock.Caption;
    oDock.TabSheet.TabVisible := True;
    oDock.TabSheet.PageIndex := pcMain.PageCount - 1;
    pcMain.ActivePage := oDock.TabSheet;
    oDock.Show;

    FDocks.Add(oDock);

Here you see our array FDocks is being populated.  pcMain is the name of the PageControl, and adding the form to the pagecontrol is actually quite simply using a TForm routine (not mine) called ManualDock();

There's issues with the attached project due to the attempted simplicity of the design:
* I don't like using inifiles.  Used them for simplicity

* Watch the GDI Object count in Task Explorer as you add multiple forms :-)  The TImageList shouldn't be stored in TdckNotepad, that was me being lazy here.  Really I should have a single TImageList in say a Data Module somewhere, and dynamically link my Notepad toolbar to this TImageList.  As you create more complex docks, I would recommend going down this path as Windows can't have a GDI Object count of >10000 per app (and Windows crashes spectacularly when an app hits this limit). (my own app is reasonably complex and gets to >3000 easily.  I *really* must move my TImageLists away from the docks...)

* A Factory is really required if you have multiple types of docked forms.  I've implemented a decent factory in my own app, but decided it added an unecassry level of complication to the attached example.  Look in TfrmMain.LoadSettings & TfrmMain.SaveSettings for how I've implemented a simple version using TObject.ClassName...

* I dislike the fact that MainForm contains both the Docking Management code and general MainForm stuff.   In my own app TfrmMain descends from TfrmDockingManager, and all the Docking management is positioned in this ancestor form.  I could also have implemented a separate TFrame for all the Docking Management and used that.  Again, the MainForm here was created with an attempt at simplicity, though as I said - it got complicated...

* A wierd issue is I'm getting is a GDB debugger STOP issue in the IDE after application close if I use the TOpenDialog.  I've been seeing this issue intermittently in other apps, this is the first time I've been able to isolate this to the TOpenDialog.  I'll chase that down independently now I know where it is.  I don't think there is anything wrong with the code
Code: [Select]
procedure TdckNotepad.btnLoadClick(Sender: TObject);
Var
  dlgOpen: TOpenDialog;
Begin
  dlgOpen := TOpenDialog.Create(Self);
  Try
    dlgOpen.DefaultExt := '.txt';
    dlgOpen.Filter := 'Text Files|*.txt';
    dlgOpen.FileName := FFilename;
    If dlgOpen.Execute Then
    Begin
      FFilename := dlgOpen.FileName;

      memNotepad.Lines.LoadFromFile(FFilename);
      FDirty := False;
      RefreshUI;
    End;
  Finally
    dlgOpen.Free;
  End;
End; 
« Last Edit: September 17, 2014, 05:40:38 pm by Mike.Cornflake »
Lazarus Trunk/FPC Trunk on Windows [7, 10]
  Have you tried searching this forum or the wiki?:   http://wiki.lazarus.freepascal.org/Alternative_Main_Page
  BOOKS! (Free and otherwise): http://wiki.lazarus.freepascal.org/Pascal_and_Lazarus_Books_and_Magazines

janasoft

  • New Member
  • *
  • Posts: 30
Re: Forms
« Reply #5 on: September 25, 2014, 10:38:35 am »
Mike, thank you very much for sharing your code and  your ideas.

Regards
Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-win64-win32/win64

 

TinyPortal © 2005-2018