* * *

Author Topic: Cloning contents from a tabsheet into another one (not the hard way!)  (Read 1359 times)

Raul_ES

  • New member
  • *
  • Posts: 13
Hello,

Imagine a very complex form with a main control in it, a PageControl with several tabs. See the attached image please.

With toolbars, icons, buttons, comboboxes, charts, notebooks with multiple pages with different contents that expand or contract depending on the dimensions of the main window, a complex stringgrid inside, vertical and horizontal slicebars.... a real complex form, too hard for cloning it's components by hand, the way I allready know how to do it. I am sure there is another handy way to create a new instance of all this elements in a new tab.

The way I do it right now is:

1. when a user clicks the "create new Point of Sale window" calls a procedure in main form to add a new tab
2. copy each control from the pos tab into the empty tab, something like this:

Code: [Select]
procedure TmainScreen.SpeedButton_newTabClick(Sender: TObject);

  var
  k: integer;
  newtab: TTabSheet;
  newlabel: TLabel;

  aPic : TPicture;

  // start duplicating all the components in the POS tabsheet

  // toolbar in the POS tabsheet
  newBevel_toolbar: TBevel;
  newSpeedButton_toolbarFirst: TSpeedButton;
  newSpeedButton_toolbarPrevious: TSpeedButton;

  begin


  k := PageControl.PageCount + 1;
  // we stablish the limit of 15 POS sessions opened at a time
  if k <= 15 then begin
     newtab := TTabSheet.Create(PageControl);
     newtab.PageControl := PageControl;
     newtab.name := 'tab'+inttostr(k);
     newtab.Caption := 'TPV '+inttostr(k);

     //---------------------------------------
     //newlabel := TLabel.Create(newtab);
     //newlabel.name := 'tablabel'+inttostr(k);
     //newlabel.caption := 'Test'+inttostr(k);
     //newlabel.left := 20;
     //newlabel.top := 20;
     //newlabel.visible := true;
     //newlabel.parent := newtab;

     // nueva barra de herramientas
     newBevel_toolbar:= TBevel.Create(newtab);
     newBevel_toolbar.Left := 0;
     newBevel_toolbar.Height := 30;
     newBevel_toolbar.Top := 0;
     newBevel_toolbar.Width := 1765;
     newBevel_toolbar.Align := alTop;
     newBevel_toolbar.Shape := bsBottomLine;
     newBevel_toolbar.visible := true;
     newBevel_toolbar.parent := newtab;
     // botón 1
     newSpeedButton_toolbarFirst := TSpeedButton.Create(newtab);
     newSpeedButton_toolbarFirst.Left := 5;
     newSpeedButton_toolbarFirst.Height := 22;
     newSpeedButton_toolbarFirst.Top := 3;
     newSpeedButton_toolbarFirst.Width := 23;
     newSpeedButton_toolbarFirst.Flat := True;
     aPic := TPicture.Create;
     aPic.LoadFromFile('C:\Users\Raúl\Proyectos\img-general\toolbars\1.png');
     newSpeedButton_toolbarFirst.Glyph.Assign(aPic.Bitmap);
     newSpeedButton_toolbarFirst.Transparent := False;
        //OnClick = SpeedButton_toolbarFirstClick
     newSpeedButton_toolbarFirst.ShowCaption := False;
     newSpeedButton_toolbarFirst.visible := true;
     newSpeedButton_toolbarFirst.parent := newtab;
(...)                                     

Ok, all this for just ONE single button from the toolbar. ONE. Please, tell me there's an easier way to solve this problem because I'm not in the mood to clone the whole thing manually. I've read severall posts on the net but I have a lot of confusion. Some posts are from Delphi websites, other from Lazarus, etc.

TFrames is a way to handle this? RTTI? Can someone provide a simple example please? Maybe it's possible to save the contents of the POS tabsheet into a file and read it into the new tab? Creating TMyPOS subclass of TPanel with the controls inside?

Any idea is welcomed...
Thanks
« Last Edit: July 12, 2017, 05:52:54 pm by Raul_ES »
Raúl
Student of Computer Science - UOC

jc99

  • Hero Member
  • *****
  • Posts: 509
    • My private Site
Have you tried a Component-Stream ?
[Edit]
Suggestion post an example-Program.
BTW. How Do you want to access the components of the cloned tab ?
Is the Form fixed or is it dynamically created ?
Can you put the content of the first Tab in a frame? Then you only have to generate  a new tab and add a new instance of the frame, Done.
 



« Last Edit: July 05, 2017, 10:11:36 pm by jc99 »
OS: Win XP x64, Win 7, Win 7 x64, Win 10, Win 10 x64, Suse Linux 13.2
Laz: 1.2 - 1.6.4, 1.8rc3
https://github.com/joecare99/public
'~|    /''
,_|oe \_,are


Raul_ES

  • New member
  • *
  • Posts: 13
Have you tried a Component-Stream ?
[Edit]
Suggestion post an example-Program.
BTW. How Do you want to access the components of the cloned tab ?
Is the Form fixed or is it dynamically created ?
Can you put the content of the first Tab in a frame? Then you only have to generate  a new tab and add a new instance of the frame, Done.

Hello Joe, thank you for your reply.

I'm giving my first steps with Object-Pascal and OOP. I know nothing about component-streaming, I'll do some research to see what can I learn.

It's a fixed form. Except one of the tabs (and maybe another one like the product info tab so you can open more than one product at a time), the one the contains the Point-of-Sale GUI that it's intended to be created dinamically each time that a user press the "+" icon in the main toolbar. The first POS tab is fixed, so there's always at least one of them open. If you need more POS tabs the user can add them.

The POS components will retrieve data from a pharmacy database (prices, indications, composition, etc) and will compute total price, taxes, etc. When you recieve the payment the user will close the sell and the tab will close.

I am not really sure of this. All the POS tabs shall connect to the same methods but have to work with their respective data... For example, closePOStab() shall make all computations for prices and taxes before closing and storing the operation data in the database, but with the corresponding data of the tab instance.

Quote
Can you put the content of the first Tab in a frame? Then you only have to generate  a new tab and add a new instance of the frame, Done.

As far as I've read, I think that's the kind of solution I need. But the TFrames doc it's SOO confusing for a newbe.
Please, can you provide a link to a clear & concise tutorial or maybe even a clean and simple example of exactly this? It would be very clarifying.


thanks!



« Last Edit: July 06, 2017, 01:08:16 am by Raul_ES »
Raúl
Student of Computer Science - UOC


ASerge

  • Sr. Member
  • ****
  • Posts: 340
Code: Pascal  [Select]
  1. procedure CopyProperties(FromControl, ToControl: TControl);
  2. var
  3.   TempMem: TMemoryStream;
  4.   FromName: string;
  5. begin
  6.   FromName := FromControl.Name;
  7.   FromControl.Name := '';
  8.   try
  9.     TempMem := TMemoryStream.Create;
  10.     try
  11.       TempMem.WriteComponent(FromControl);
  12.       TempMem.Position := 0;
  13.       TempMem.ReadComponent(ToControl);
  14.     finally
  15.       TempMem.Free;
  16.     end;
  17.   finally
  18.     FromControl.Name := FromName;
  19.   end;
  20. end;
  21.  
  22. function CloneControl(FromControl: TControl): TControl;
  23. var
  24.   C: TControl;
  25. begin
  26.   Result := TControlClass(FromControl.ClassType).Create(FromControl.Owner);
  27.   if FromControl.Name <> '' then
  28.     Result.Name := FromControl.Name + '_';
  29.   CopyProperties(FromControl, Result);
  30.   if FromControl is TWinControl then
  31.     for C in TWinControl(FromControl).GetEnumeratorControls do
  32.       CloneControl(C).Parent := TWinControl(Result);
  33. end;
  34.  
  35. procedure CloneActivePage(PageControl: TPageControl);
  36. var
  37.   Page: TTabSheet;
  38. begin
  39.   if PageControl.ActivePage <> nil then
  40.   begin
  41.     Page := CloneControl(PageControl.ActivePage) as TTabSheet;
  42.     Page.PageControl := PageControl;
  43.     PageControl.ActivePage := Page;
  44.   end;
  45. end;
  46.  
  47. // Use:
  48. procedure TForm1.Button1Click(Sender: TObject);
  49. begin
  50.   CloneActivePage(PageControl1);
  51. end;

CCRDude

  • Sr. Member
  • ****
  • Posts: 331
TFrame would be the way I would choose as well.

Since you asked for an example: CodeCoverage.Browser.pas shows hab tabs with embedded frame are created on the fly. During design-time, you handle frames pretty much like forms, except that you've got to move anything you would want in FormCreate and FormDestroy into overwritten constructors/destructors.

jc99

  • Hero Member
  • *****
  • Posts: 509
    • My private Site
As far as I've read, I think that's the kind of solution I need. But the TFrames doc it's SOO confusing for a newbe.
Please, can you provide a link to a clear & concise tutorial or maybe even a clean and simple example of exactly this? It would be very clarifying.


thanks!
My solution is done with frames, just use my source as a template ...
OS: Win XP x64, Win 7, Win 7 x64, Win 10, Win 10 x64, Suse Linux 13.2
Laz: 1.2 - 1.6.4, 1.8rc3
https://github.com/joecare99/public
'~|    /''
,_|oe \_,are

taazz

  • Hero Member
  • *****
  • Posts: 3974
As far as I've read, I think that's the kind of solution I need. But the TFrames doc it's SOO confusing for a newbe.
Please, can you provide a link to a clear & concise tutorial or maybe even a clean and simple example of exactly this? It would be very clarifying.


I do not want to work today for some reason, lucky for you I guess, here is a small how to about frames.

Imagine if you like a single container that can host other controls and group them together as a single control. What are the basic requirement for such a control.
1)It can be designed the simplest designer is the form designer lets use that.
2)It can be created and destroyed as a single control.
3) it should give access to the internal controls properties and events and safe guard them from destructive actions (delete etc)
4)it should be able to use inheritance for easy extension.

and that is all a tframe does, its not simple to implement such a control though and by design has no life outside the designer of its own it requires an other host (also known as parent in lcl) to be able to function properly.
So enough with the theory lets see it work.

1) start a new project I would rather not mesh anything of yours at this point.
2) save it in a new folder
3) press file\new.
4) on the dialog that opens select Frame under the module brunch.
5) press ok.

Now you see what looks like a form but it is a frame place any number of controls in it write a couple of events (click, mouse enter/leave, labels changing color/caption etc) and save it.

Now you have a frame. In order for the frame to work properly it needs to be placed inside a host/parent. Select the main form and find in the component bar the frame control it is inside the standard bar the second from the right.
click on it and then click on the form the select frame dialog appears select your unit2 in the list box under the search editor and press ok. Congratulation you just created your first frame. Play a bit with it try to move its controls override the events you have written calling the inherited event as well. when you have your fun delete from the form and create it dynamically through code.

And thats all there is to know about frames(not really but it is everything you need to know to start using them).
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

Raul_ES

  • New member
  • *
  • Posts: 13
Do you mean something like this:
https://github.com/joecare99/Public/blob/master/Projects/bin/x86_64-win64/Prj_CopyComplexFrame.exe
Source:
https://github.com/joecare99/Public/tree/master/Examples/Source/CopyComplexFrame
https://github.com/joecare99/Public/tree/master/Examples/FPC Prj_CopyComplexFrame.*

Thanks for the example Joe,

It gives two problems:


1- The program crashes when adding more than one tab:

Duplicate name: A component named "Frame1" already exists.
ok, Abort


and the new tabsheet appears empty.

I'm trying to see where's the problem.

2- when exiting the program gives the followin error message:

Error:
Heap dumb by heaptrc unit
101838 memory blocks allocated: numbers...
101383 memory blocks freed:  the same numbers...
0 unfreed memory block: 0
True heap size: 819200
True free heap: 819040
Should be: 819200

Accept.

Raúl
Student of Computer Science - UOC

Raul_ES

  • New member
  • *
  • Posts: 13
Re: Cloning contents from a tabsheet into another one (not the hard way!)
« Reply #10 on: July 06, 2017, 10:21:39 pm »
Thank you very much ASerge, I'm also going to try you propossal, looks clear.

And thanks also to CCRDude :-)
Raúl
Student of Computer Science - UOC

Raul_ES

  • New member
  • *
  • Posts: 13
Re: Cloning contents from a tabsheet into another one (not the hard way!)
« Reply #11 on: July 06, 2017, 10:25:25 pm »
Thanks taazz, a simple step-by-step guide it's always appreciated! :-) Now I'm going to try a simple exercice to see what I can do with frames, it appears to be a powerfull coding resource.

thanks to all,
regards
Raúl
Student of Computer Science - UOC

jc99

  • Hero Member
  • *****
  • Posts: 509
    • My private Site
Re: Cloning contents from a tabsheet into another one (not the hard way!)
« Reply #12 on: July 06, 2017, 11:24:18 pm »
Thanks for the example Joe,

It gives two problems:


1- The program crashes when adding more than one tab:

Duplicate name: A component named "Frame1" already exists.
ok, Abort


and the new tabsheet appears empty.

I'm trying to see where's the problem.

2- when exiting the program gives the followin error message:

Error:
Heap dumb by heaptrc unit
101838 memory blocks allocated: numbers...
101383 memory blocks freed:  the same numbers...
0 unfreed memory block: 0
True heap size: 819200
True free heap: 819040
Should be: 819200

Accept.
Sorry for the first one, If adding more than one frame you/I have to name the components, Duplicate names are not allowed,
... Gona fix that.

The second one is a message from heaptrc, Saying there are no unfreed blocks after the program ends. So no memleak ...
 
OS: Win XP x64, Win 7, Win 7 x64, Win 10, Win 10 x64, Suse Linux 13.2
Laz: 1.2 - 1.6.4, 1.8rc3
https://github.com/joecare99/public
'~|    /''
,_|oe \_,are

jc99

  • Hero Member
  • *****
  • Posts: 509
    • My private Site
Re: Cloning contents from a tabsheet into another one (not the hard way!)
« Reply #13 on: July 06, 2017, 11:29:34 pm »
Done:
Code: Pascal  [Select]
  1. procedure TfrmComplexFrameMain.btnCreateNewSaleClick(Sender: TObject);
  2. var
  3.     k: integer;
  4.     lNewTabSheet: TTabSheet;
  5.     lNewFrame: TfraComplexFrame;
  6. begin
  7.     k := PageControl1.PageCount + 1;
  8.     if k <= 15 then
  9.       begin
  10.         lNewTabSheet := PageControl1.AddTabSheet;
  11.         with lNewTabSheet do
  12.           begin
  13.             Name := 'tab' + IntToStr(k);
  14.             Caption := 'TPV ' + IntToStr(k);
  15.           end;
  16.  
  17.         lNewFrame := TfraComplexFrame.Create(frmComplexFrameMain);
  18.         with lNewFrame do
  19.           begin
  20.             Name := 'cplxframe' + IntToStr(k);  // <--- Here
  21.             Parent := lNewTabSheet;
  22.             Align := alClient;
  23.           end;
  24.       end;
  25. end;  
  26.  
OS: Win XP x64, Win 7, Win 7 x64, Win 10, Win 10 x64, Suse Linux 13.2
Laz: 1.2 - 1.6.4, 1.8rc3
https://github.com/joecare99/public
'~|    /''
,_|oe \_,are

Raul_ES

  • New member
  • *
  • Posts: 13
Re: Cloning contents from a tabsheet into another one (not the hard way!)
« Reply #14 on: July 12, 2017, 05:34:10 pm »
Joe, I'm trying your last correction but it still gives the problem:
Duplicate name: A component named "Frame1" already exists.
ok, Abort

The solution I've found is adding 1 to k:
Code: Pascal  [Select]
  1. lNewFrame := TFrame1.Create(Form1);
  2.         with lNewFrame do
  3.           begin
  4.             Name := 'Frame' + IntToStr(k+1);  // <--- Here
  5.             Parent := lNewTabSheet;
  6.             Align := alClient;
  7.           end;
  8.  

Honestly, I don't understand why???
Raúl
Student of Computer Science - UOC

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus