Lazarus

Using the Lazarus IDE => Designer => Topic started by: Raul_ES on July 05, 2017, 07:46:23 pm

Title: [SOLVED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on July 05, 2017, 07:46:23 pm
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
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: jc99 on July 05, 2017, 10:00:59 pm
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.
 



Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: jc99 on July 05, 2017, 11:31:00 pm
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.* (https://github.com/joecare99/Public/blob/master/Examples/FPC/Prj_CopyComplexFrame.lpr)
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on July 06, 2017, 01:00:25 am
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!



Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on July 06, 2017, 01:09:30 am
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.* (https://github.com/joecare99/Public/blob/master/Examples/FPC/Prj_CopyComplexFrame.lpr)

I am checking it right now :-)
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: ASerge on July 06, 2017, 06:14:01 am
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;
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: CCRDude on July 06, 2017, 08:17:41 am
TFrame would be the way I would choose as well.

Since you asked for an example: CodeCoverage.Browser.pas (https://gitlab.com/ccrdude/laz-codecoverage-helper/blob/master/source/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.
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: jc99 on July 06, 2017, 01:10:35 pm
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 ...
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: taazz on July 06, 2017, 02:27:04 pm
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).
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on July 06, 2017, 10:17:30 pm
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.* (https://github.com/joecare99/Public/blob/master/Examples/FPC/Prj_CopyComplexFrame.lpr)

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.

Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES 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 :-)
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES 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
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: jc99 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 ...
 
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: jc99 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.  
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES 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???
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on July 12, 2017, 05:51:07 pm
I managed to created my very own frames thanks to  you guys  :D

check this example code:

Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: taazz on July 12, 2017, 06:13:31 pm
looks logical. You do know that the name is only useful for the streaming mechanism right? if you do not plan to stream the contents of your frame then leaving the name empty will work as well. There are a number of tricks you can do also, for example

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls,
  9.   StdCtrls, Grids, Unit2;
  10.  
  11. type
  12.  
  13.   { TTabSheetFrame }
  14.  
  15.   TTabSheetFrame = class(TTabSheet)
  16.   private
  17.     FMyFrame: TFrame1;
  18.     procedure SetMyFrame(aValue: TFrame1);
  19.   public
  20.     constructor Create(TheOwner: TComponent); override;
  21.     property MyFrame : TFrame1 read FMyFrame;
  22.   end;
  23.  
  24.   { TForm1 }
  25.  
  26.   TForm1 = class(TForm)
  27.     Button1: TButton;
  28.     Button2: TButton;
  29.     Label2: TLabel;
  30.     PageControl1: TPageControl;
  31.     StringGrid1: TStringGrid;
  32.     procedure Button1Click(Sender: TObject);
  33.     procedure Button2Click(Sender: TObject);
  34.   private
  35.  
  36.   public
  37.  
  38.   end;
  39.  
  40. var
  41.   Form1: TForm1;
  42.  
  43. implementation
  44.  
  45. {$R *.lfm}
  46.  
  47. { TForm1 }
  48.  
  49. procedure TForm1.Button2Click(Sender: TObject);
  50. begin
  51.    Form1.Close;
  52. end;
  53.  
  54. procedure TForm1.Button1Click(Sender: TObject);
  55. var
  56.     k: integer;
  57.     lNewTabSheet: TTabSheet;
  58.     lNewFrame: TFrame1;
  59. begin
  60.     k := PageControl1.PageCount + 1;
  61.     if k <= 5 then
  62.       begin
  63.         lNewTabSheet := TTabSheetFrame.Create(PageControl1);
  64.         lNewTabSheet.PageControl := PageControl1;
  65.         with lNewTabSheet do
  66.           begin
  67.             Name := 'tab' + IntToStr(k);
  68.             Caption := 'Operation #' + IntToStr(k);
  69.           end;
  70. end;
  71.  
  72. { TTabSheetFrame }
  73.  
  74. constructor TTabSheetFrame.Create(TheOwner: TComponent);
  75. begin
  76.   inherited Create(TheOwner);
  77.   FMyFrame:=TFrame1.Create(TheOwner{or Self});
  78.   FMyFrame.Parent := Self;
  79.   FMYFrame.Align := alClient;
  80. end;
  81.  
  82.  
  83. end.
  84.  
  85.  
single creation for both ttabsheet and frame.

And of course the most unexpected.
Code: Pascal  [Select][+][-]
  1. { TTabSheetFrame }
  2.  
  3. constructor TTabSheetFrame.Create(TheOwner: TComponent);
  4. begin
  5.   FMyFrame:=TFrame1.Create(TheOwner);
  6.   inherited Create(FMyFrame);
  7.   FMyFrame.Parent := Self;
  8.   FMYFrame.Align := alClient;
  9. end;
  10.  
Now when you call
Code: Pascal  [Select][+][-]
  1.    TTabSheetFrame(PageControl1.Page[0]).MyFRame.Free;
  2.  
it will automatically destroy the TTabsheet too. Well This one needs testing I'm not comfortable using in production code but it shows how the owner mechanism works.
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on July 12, 2017, 07:35:08 pm
Ok. This is getting more complex...I think I'm going to blow a circuit in my brain. How to manage procedures and properties between the frame and the owner.

Code: Pascal  [Select][+][-]
  1. procedure TFrame1.BitBtn2Click(Sender: TObject);
  2. begin
  3.   // implement close active tabsheet method
  4.  
  5.   // Change the caption of the window (or a label or whatever) to say that
  6.   // tab number x has been deleted.
  7.    Form1.Caption := 'Deleting tab ' + InttoStr(PageControl1.ActivePageIndex);
  8.  
  9.    // delete the tabsheet
  10.    PageControl1.Page[PageControl1.ActivePageIndex].Destroy;
  11.  
  12. end;
  13.  

This code belongs to Unit2, the frame code. It gives the obvious error that Form1 and PageControl1 have not been declared (they belong to Unit1, the Form control). What I am doing wrong?
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on July 12, 2017, 07:37:49 pm
Thanks taazz, I'm going to experiment with your code! Now I think I need a break...
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: taazz on July 12, 2017, 07:51:05 pm
Thanks taazz, I'm going to experiment with your code! Now I think I need a break...
erm let it be for now its a bit ahead still sorry.
Ok. This is getting more complex...I think I'm going to blow a circuit in my brain. How to manage procedures and properties between the frame and the owner.

Code: Pascal  [Select][+][-]
  1. procedure TFrame1.BitBtn2Click(Sender: TObject);
  2. begin
  3.   // implement close active tabsheet method
  4.  
  5.   // Change the caption of the window (or a label or whatever) to say that
  6.   // tab number x has been deleted.
  7.    Form1.Caption := 'Deleting tab ' + InttoStr(PageControl1.ActivePageIndex);
  8.  
  9.    // delete the tabsheet
  10.    PageControl1.Page[PageControl1.ActivePageIndex].Destroy;
  11.  
  12. end;
  13.  

This code belongs to Unit2, the frame code. It gives the obvious error that Form1 and PageControl1 have not been declared (they belong to Unit1, the Form control). What I am doing wrong?
where did you wrote this code? the frame? how about going on the form selecting the button inside the frame you have there and adding a new event for the buttons click (double the button for short).
Then write the code there. Keep in mind that the button's event inside the form will not automatically link when creating the frame dynamically you need to do it manually something along the lines of
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls,
  9.   StdCtrls, Grids, Unit2;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     Button2: TButton;
  18.     Label2: TLabel;
  19.     PageControl1: TPageControl;
  20.     StringGrid1: TStringGrid;
  21.     procedure Button1Click(Sender: TObject);
  22.     procedure Button2Click(Sender: TObject);
  23.   private
  24.  
  25.   public
  26.     procedure FrameButton2Click(Sender:TObject);
  27.  
  28.   end;
  29.  
  30. var
  31.   Form1: TForm1;
  32.  
  33. implementation
  34.  
  35. {$R *.lfm}
  36.  
  37. { TForm1 }
  38.  
  39. procedure TForm1.FrameButton2Click(Sender:TObject);
  40. begin
  41.   Caption := 'Deleting tab ' + InttoStr(PageControl1.ActivePageIndex);
  42.   PageControl1.Page[PageControl1.ActivePageIndex].Destroy;
  43. end;
  44.  
  45. procedure TForm1.Button2Click(Sender: TObject);
  46. begin
  47.    Form1.Close;
  48. end;
  49.  
  50. procedure TForm1.Button1Click(Sender: TObject);
  51. var
  52.     k: integer;
  53.     lNewTabSheet: TTabSheet;
  54.     lNewFrame: TFrame1;
  55. begin
  56.     k := PageControl1.PageCount + 1;
  57.     if k <= 5 then
  58.       begin
  59.         lNewTabSheet := PageControl1.AddTabSheet;
  60.         with lNewTabSheet do
  61.           begin
  62.             Name := 'tab' + IntToStr(k);
  63.             Caption := 'Operation #' + IntToStr(k);
  64.           end;
  65.  
  66.         lNewFrame := TFrame1.Create(Form1);
  67.         with lNewFrame do
  68.           begin
  69.             Name := 'Frame' + IntToStr(k+1);  // <--- Here
  70.             Parent := lNewTabSheet;
  71.             Align := alClient;
  72.             BitBtn2.click := @framebutton2Click;
  73.           end;
  74.       end;
  75. end;
  76.  
  77. end.
  78.  
  79.  
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on July 12, 2017, 11:56:26 pm
Thank you taazz.

Actually, is the Lazarus IDE itself that places the procedure TFrame1.BitBtn2Click(Sender: TObject) inside the frame source code file, not in the Form1, when double-clicking on the designer. So I thought that's the place should be. It would be possible to achieve the same using this approach instead?


And excuse my dummy question, In this piece of code:

Code: Pascal  [Select][+][-]
  1. BitBtn2.click := @framebutton2Click;

What does the @ mean? it has something to do with @override?


thanks!
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: taazz on July 13, 2017, 03:50:47 am
Thank you taazz.

Actually, is the Lazarus IDE itself that places the procedure TFrame1.BitBtn2Click(Sender: TObject) inside the frame source code file, not in the Form1, when double-clicking on the designer. So I thought that's the place should be. It would be possible to achieve the same using this approach instead?
Yes. a frame should be seen as a single entity what ever you write in there should only be about the frame it self,  behavior rules etc that cavern the controls and their interaction with each other nothing more.
And excuse my dummy question, In this piece of code:
Code: Pascal  [Select][+][-]
  1. BitBtn2.click := @framebutton2Click;

What does the @ mean? it has something to do with @override?
it is a way to instruct the compiler that you do not want to execute the method and assign the results but you want to assign the method it self as a value to the event.
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: jc99 on July 15, 2017, 02:06:54 am
And excuse my dummy question, In this piece of code:

Code: Pascal  [Select][+][-]
  1. BitBtn2.click := @framebutton2Click;

What does the @ mean? it has something to do with @override?

The @-operator generally gives you the address/reference of something instead of the object/variable.
The opposite operator is the ^-dereferencing operator that takes a (typed) address(pointer) and gives you the value.
example:
Code: Pascal  [Select][+][-]
  1.  
  2. var i:integer;
  3.      p:^integer; // p is declared as a pointer to integer;
  4.                        // normally pointer-variables are 'p'-prefixed
  5. begin
  6.    i := 5;    // This initializes the variable i
  7.    p := @i;   // This sets p to the address of i
  8.    i := 6;    // Now as a test you change i to 6
  9.    write(p^); // Surprise: This writes 6 and not 5
  10.    p^ := 7;   // Here you set the dereferenced address to 7
  11.    write(i);  // This will output the 7
  12. end;
  13.  
I hope this clarifies something ...
Title: Re: Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on July 19, 2017, 06:05:41 pm
Thanks Joe!
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on August 20, 2017, 05:42:17 am
Hi,

I'm still working in this same small example. See attached files please.

There's something wrong here or maybe there is something I am missing. No clue:

Code: Pascal  [Select][+][-]
  1. BitBtn2.click := @framebutton2Click;

Compiler complains with an error message: Error: Argument cannot be assigned to

 %)
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Handoko on August 20, 2017, 05:47:04 am
I think it should be:
Code: Pascal  [Select][+][-]
  1. BitBtn2.OnClick := @framebutton2Click;
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on August 20, 2017, 04:39:32 pm
Thanks Handoko!

You are right, now it seems to work. I attach the updates an a minor fix.

regards,

Code: Pascal  [Select][+][-]
  1. unit Unit2;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, StdCtrls, ExtCtrls, Buttons;
  9.  
  10. type
  11.  
  12.   { TFrame1 }
  13.  
  14.   TFrame1 = class(TFrame)
  15.     BitBtn1: TBitBtn;
  16.     BitBtn2: TBitBtn;
  17.     BitBtn3: TBitBtn;
  18.     BitBtn4: TBitBtn;
  19.     BitBtn5: TBitBtn;
  20.     BitBtn6: TBitBtn;
  21.     BitBtn7: TBitBtn;
  22.     CheckBox1: TCheckBox;
  23.     Edit1: TEdit;
  24.     Edit2: TEdit;
  25.     Edit3: TEdit;
  26.     Edit4: TEdit;
  27.     GroupBox1: TGroupBox;
  28.     Label1: TLabel;
  29.     Label2: TLabel;
  30.     Label3: TLabel;
  31.     Label4: TLabel;
  32.     Label5: TLabel;
  33.     Panel1: TPanel;
  34.     StaticText1: TStaticText;
  35.     procedure BitBtn1Click(Sender: TObject);
  36.     procedure BitBtn2Click(Sender: TObject); // don't remove
  37.     procedure BitBtn3Click(Sender: TObject);
  38.     procedure BitBtn4Click(Sender: TObject);
  39.     procedure BitBtn5Click(Sender: TObject);
  40.     procedure BitBtn6Click(Sender: TObject);
  41.     procedure BitBtn7Click(Sender: TObject);
  42.   private
  43.  
  44.   public
  45.  
  46.   end;
  47.  
  48. implementation
  49.  
  50. {$R *.lfm}
  51.  
  52. { TFrame1 }
  53.  
  54. procedure TFrame1.BitBtn2Click(Sender: TObject); // don't remove
  55. begin
  56.   // implement close active tabsheet method
  57.  
  58.   //PageControl1.Page[PageControl1.ActivePageIndex].Destroy; ----> implemented in Form1 code
  59.  
  60. end;
  61.  
  62. procedure TFrame1.BitBtn3Click(Sender: TObject);
  63. begin
  64.   Label3.caption := 'Suma';
  65. end;
  66.  
  67. procedure TFrame1.BitBtn4Click(Sender: TObject);
  68. begin
  69.   Label3.caption := 'Resta';
  70. end;
  71.  
  72. procedure TFrame1.BitBtn5Click(Sender: TObject);
  73. begin
  74.   Label3.caption := 'División';
  75. end;
  76.  
  77. procedure TFrame1.BitBtn6Click(Sender: TObject);
  78. begin
  79.   Label3.caption := 'Multiplicación';
  80. end;
  81.  
  82. procedure TFrame1.BitBtn7Click(Sender: TObject);
  83. begin
  84.   // implement save result to log
  85.  
  86. end;
  87.  
  88. procedure TFrame1.BitBtn1Click(Sender: TObject);
  89. begin
  90.   // implement reset input values to zero
  91.  
  92. end;
  93.  
  94. end.

And the Form1 code:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls,
  9.   StdCtrls, Grids, Unit2, Buttons;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     Button2: TButton;
  18.     Label2: TLabel;
  19.     PageControl1: TPageControl;
  20.     StringGrid1: TStringGrid;
  21.     procedure Button1Click(Sender: TObject);
  22.     procedure Button2Click(Sender: TObject);
  23.   private
  24.  
  25.   public
  26.     procedure Frame1BitBtn2Click(Sender: TObject);
  27.   end;
  28.  
  29. var
  30.   Form1: TForm1;
  31.  
  32. implementation
  33.  
  34. {$R *.lfm}
  35.  
  36. { TForm1 }
  37.  
  38. procedure TForm1.Button2Click(Sender: TObject);
  39. begin
  40.    Form1.Close;
  41. end;
  42.  
  43. ///////////////////////
  44.  
  45. procedure TForm1.Frame1BitBtn2Click(Sender: TObject);
  46. begin
  47.    PageControl1.Page[PageControl1.ActivePageIndex].Destroy;
  48. end;
  49.  
  50. ///////////////////////
  51.  
  52. procedure TForm1.Button1Click(Sender: TObject);
  53. var
  54.     k: integer;
  55.     lNewTabSheet: TTabSheet;
  56.     lNewFrame: TFrame1;
  57. begin
  58.     k := PageControl1.PageCount + 1;
  59.     if k <= 5 then
  60.       begin
  61.         lNewTabSheet := PageControl1.AddTabSheet;
  62.         with lNewTabSheet do
  63.           begin
  64.             Name := 'tab' + IntToStr(k);
  65.             Caption := 'Operation #' + IntToStr(k);
  66.           end;
  67.  
  68.         lNewFrame := TFrame1.Create(Form1);
  69.         with lNewFrame do
  70.           begin
  71.             Name := 'Frame' + IntToStr(k+1);  // <--- Here
  72.             Parent := lNewTabSheet;
  73.             Align := alClient;
  74.             //
  75.             BitBtn2.OnClick := @Frame1BitBtn2Click; //
  76.             // BitBtn2.Click ----> Error: Argument cannot be assigned to
  77.           end;
  78.       end;
  79. end;
  80.  
  81. end.
  82.  
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on August 20, 2017, 06:00:14 pm
How can we verify from the Form1 code the value of a variable/checkbox from Frame1? defining a global public variable to store the value of interest? what approach would you recommend?

Check the following example:

In frame1 there's a checkbox that if true indicates that the results has been correctly stored or saved to a file.

In Form1 there's a Quit button that veryfies the status of this checkbox, and if not checked it doesn't allow the user to quit the program and displays a messagebox warning about the reason.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button2Click(Sender: TObject);
  2. begin
  3.  
  4.    if (CheckBox1.Checked = true) then    // this CheckBox1 is in Frame1. What's the best way to access it? a kind of Getter?
  5.    Form1.Close else ShowMessage('Save results before quitting.');
  6. end;
  7.  


Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Handoko on August 20, 2017, 06:25:45 pm
You have to do:
1. Make lNewFrame a global variable 
2. In Button2Click, make sure the variable is assigned
3. Use lNewFrame.CheckBox1.Checked to access the value
4. Remove lNewFrame's variable declaration in Button1Click
5. And don't forget to free lNewFrame when no longer need it

Code: Pascal  [Select][+][-]
  1. // ....
  2. implementation
  3.  
  4. var
  5.   lNewFrame: TFrame1;
  6.  
  7. {$R *.lfm}
  8.  
  9. { TForm1 }
  10.  
  11. procedure TForm1.Button2Click(Sender: TObject);
  12. begin
  13.    if not(Assigned(lNewFrame)) then Exit;
  14.    if (lNewFrame.CheckBox1.Checked = true) then
  15.    Form1.Close else ShowMessage('Save results before quitting.');
  16. end;
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on August 21, 2017, 02:50:48 am
Thank you again Handoko :-)

Just a point: I thought that the frame was automathicaly freed when closing it's parent control (form1). Am I wrong?


regards

Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Handoko on August 21, 2017, 04:01:49 am
I thought you may be need to free it manually because I saw your code has potential creating it multiple times on runtime:

Code: Pascal  [Select][+][-]
  1.         lNewFrame := TFrame1.Create(Form1);

If you want to create it just once, make sure you check it before creating it. If it is only created a single time when the program running, you do not need to free it manually. So in TForm1.Button1Click, it should be:

Code: Pascal  [Select][+][-]
  1.         // ...
  2.         if not(Assigned(lNewFrame)) then
  3.           lNewFrame := TFrame1.Create(Form1);
  4.         with lNewFrame do
  5.         // ...
  6.  
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on August 22, 2017, 11:58:25 pm
Thanks Handoko,

the idea is:

Put a Frame in a TabSheet of Pagecontrol1 in Form1. (Yes you were right, the idea is to have multiple frames)

I understand that closing Form1 will destroy/free all it's components or that by simply closing a tabsheet, it's corresponding Frame instance will be also closed and therefore freed. Ain't right?
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on August 23, 2017, 12:18:46 am
We've seen how to access a component inside the Frame1 from the code in the Form1. What if I want to access from Frame1 a component in Form1? (opposite direction)

Example:

In Form1 I have StatusBar1.

Whenever I click in Button1 of Frame1 the caption of StatusBar1 has to change:

Code: Pascal  [Select][+][-]
  1. StatusBar1.Caption := 'Button1 has been clicked'

I have a bit of confusion about who can see who in this form-frame model. Form1.StatusBar1.Caption := whatever doesn't seem to work.

thanks!
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: taazz on August 23, 2017, 01:33:07 am
We've seen how to access a component inside the Frame1 from the code in the Form1. What if I want to access from Frame1 a component in Form1? (opposite direction)
from a component point of view, you do not. a Frame might be placed inside form1, form43 or even dialog54. Frame1 code should never have the need to access anything outside it self. Form1 might want to be informed on various events in the frame1 you create those events in frame1 and implement their handler in form1 and in form45 or which ever parent you choose.
Example:

In Form1 I have StatusBar1.

Whenever I click in Button1 of Frame1 the caption of StatusBar1 has to change:

Code: Pascal  [Select][+][-]
  1. StatusBar1.Caption := 'Button1 has been clicked'

I have a bit of confusion about who can see who in this form-frame model. Form1.StatusBar1.Caption := whatever doesn't seem to work.

thanks!
In this case I would say you have write two event handlers one inside the frame1 and one inside the form1. As I said (I think I said it) in one of my previous messages you can override the events of the frame in their parent and you should be able call the inherited event. So after you have finished the code on frame1 you place it on a form you double click the button inside the frame and you write your code that accesses your status bar before or after calling the inherited method.

There is an other case though, the code inside the button is a loop and you want to update the parent with its progress. In this case you define a new event type and declare an event property on the frame, inside the loop you call what ever method that event variable points with the progress. In form1 you write an event handler with the same signature as the event you defined in the previous step and assign it to the event property of the frame.

That way your frame remains decoupled from any parent and can be used on different parents with out problems.
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on August 25, 2017, 05:23:05 pm
Thanks taazz, I think that I nearly have it but now I'm stuck in the last part.
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on August 25, 2017, 05:42:48 pm
Hi friends, I am missing something. I am not really sure about how to proceed to destroy and free the components.

It happens the following and I think that is because I'm not freeing correctly the frames or tabsheets. I don't want to destroy the page control, I will just make it unvisible if it's empty and visible again when a new tab is added.

Example:

Frame1 is in tabshheet1 of Pagecontrol.
Frame2 is in tabshheet2
...

Each time I click "add new tab" button a new tab is added
and a new frame is inserted in the tab. Everything fine.

Let's continue. When I click the "close active tab" button in Form1, the active tab closes. Fine. But if I try to add a new tab:

Duplicate name: a component named "Frame4" already exists. Ok to ignore and risk data corruption. Abort to kill the program.

If I remove all the tabs (in theory I empty the pagecontrol)
and I try to add a new tab I will get the same message refering to "Frame1".

It's like destroying the active tab does not destroy and free it's dependant frame  %)

regards,

Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: taazz on August 25, 2017, 06:10:22 pm
Hi friends, I am missing something. I am not really sure about how to proceed to destroy and free the components.

It happens the following and I think that is because I'm not freeing correctly the frames or tabsheets. I don't want to destroy the page control, I will just make it unvisible if it's empty and visible again when a new tab is added.

Example:

Frame1 is in tabshheet1 of Pagecontrol.
Frame2 is in tabshheet2
...

Each time I click "add new tab" button a new tab is added
and a new frame is inserted in the tab. Everything fine.

Let's continue. When I click the "close active tab" button in Form1, the active tab closes. Fine. But if I try to add a new tab:

Duplicate name: a component named "Frame4" already exists. Ok to ignore and risk data corruption. Abort to kill the program.

If I remove all the tabs (in theory I empty the pagecontrol)
and I try to add a new tab I will get the same message refering to "Frame1".

It's like destroying the active tab does not destroy and free it's dependant frame  %)

regards,
show us some code. From what you describe I'm guessing that you do not have the proper owner hierarchy set, when creating the tabsheets and frames. when you create the frame you must pass the tabsheet as the owner on the create call.
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Handoko on August 25, 2017, 06:29:43 pm
@Raul_ES

I have modified your code to let the frame to be able to call form's status bar. Try to click the 'add', 'subtraction', 'division', 'multiplication' buttons.

Code: Pascal  [Select][+][-]
  1. procedure TFrame1.BitBtn3Click(Sender: TObject);
  2. var
  3.   StatusBar: TStatusBar;
  4. begin
  5.   Label3.caption := 'Add';
  6.   StatusBar := GetParentStatusBar;
  7.   if (StatusBar = nil) then Exit;
  8.   StatusBar.SimpleText := 'You have pressed addition button.';
  9. end;
  10.  
  11. procedure TFrame1.BitBtn4Click(Sender: TObject);
  12. var
  13.   StatusBar: TStatusBar;
  14. begin
  15.   Label3.caption := 'Substraction';
  16.   StatusBar := GetParentStatusBar;
  17.   if (StatusBar = nil) then Exit;
  18.   StatusBar.SimpleText := 'You have pressed subtraction button.';
  19. end;
  20.  
  21. procedure TFrame1.BitBtn5Click(Sender: TObject);
  22. var
  23.   StatusBar: TStatusBar;
  24. begin
  25.   Label3.caption := 'Division';
  26.   StatusBar := GetParentStatusBar;
  27.   if (StatusBar = nil) then Exit;
  28.   StatusBar.SimpleText := 'You have pressed division button.';
  29. end;
  30.  
  31. procedure TFrame1.BitBtn6Click(Sender: TObject);
  32. var
  33.   StatusBar: TStatusBar;
  34. begin
  35.   Label3.caption := 'Multiplication';
  36.   StatusBar := GetParentStatusBar;
  37.   if (StatusBar = nil) then Exit;
  38.   StatusBar.SimpleText := 'You have pressed multiplication button.';
  39. end;
  40.  
  41. function TFrame1.GetParentStatusBar: TStatusBar;
  42. var
  43.   Caller:    TWinControl;
  44.   StatusBar: TComponent;
  45. begin
  46.   Result := nil;
  47.   Caller := Parent.Parent.Parent; // tab1 > PageControl1 > Form
  48.   if not(Caller is TForm) then Exit;
  49.   StatusBar := Caller.FindComponent('StatusBar1');
  50.   Result := (StatusBar as TStatusBar);
  51. end;

---edit---
I forget to set result = nil at the beginning of the function.
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: taazz on August 25, 2017, 06:43:05 pm
@Raul_ES

I have modified your code to let the frame to be able to call form's status bar. Try to click the 'add', 'subtraction', 'division', 'multiplication' buttons.
that code gave me various ticks while reading it.
Two comments.
1) there is a method called parentform use it.
2) it would be easier to define an event that is called and assign it a value.
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Handoko on August 25, 2017, 07:02:10 pm
I found no information about parentform. Where is the documentation?
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: taazz on August 25, 2017, 07:08:36 pm
I found no information about parentform. Where is the documentation?
sorry my bad, its a function in the forms unit called GetParentForm. I have no knowledge of any documentation other than the source code that is.
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on August 31, 2017, 08:05:57 pm

It's like destroying the active tab does not destroy and free it's dependant frame  %)

regards,
show us some code. From what you describe I'm guessing that you do not have the proper owner hierarchy set, when creating the tabsheets and frames. when you create the frame you must pass the tabsheet as the owner on the create call.

Thanks taazz, you  were absolutely right. I had a mess with the ownership of the frames, they had the wrong parent. Now works smooth. :-)
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on August 31, 2017, 08:08:35 pm
@Raul_ES

I have modified your code to let the frame to be able to call form's status bar. Try to click the 'add', 'subtraction', 'division', 'multiplication' buttons.

Code: Pascal  [Select][+][-]
  1. procedure TFrame1.BitBtn3Click(Sender: TObject);
  2. var
  3.   StatusBar: TStatusBar;
  4. begin
  5.   Label3.caption := 'Add';
  6.   StatusBar := GetParentStatusBar;
  7.   if (StatusBar = nil) then Exit;
  8.   StatusBar.SimpleText := 'You have pressed addition button.';
  9. end;
  10.  
  11. procedure TFrame1.BitBtn4Click(Sender: TObject);
  12. var
  13.   StatusBar: TStatusBar;
  14. begin
  15.   Label3.caption := 'Substraction';
  16.   StatusBar := GetParentStatusBar;
  17.   if (StatusBar = nil) then Exit;
  18.   StatusBar.SimpleText := 'You have pressed subtraction button.';
  19. end;
  20.  
  21. procedure TFrame1.BitBtn5Click(Sender: TObject);
  22. var
  23.   StatusBar: TStatusBar;
  24. begin
  25.   Label3.caption := 'Division';
  26.   StatusBar := GetParentStatusBar;
  27.   if (StatusBar = nil) then Exit;
  28.   StatusBar.SimpleText := 'You have pressed division button.';
  29. end;
  30.  
  31. procedure TFrame1.BitBtn6Click(Sender: TObject);
  32. var
  33.   StatusBar: TStatusBar;
  34. begin
  35.   Label3.caption := 'Multiplication';
  36.   StatusBar := GetParentStatusBar;
  37.   if (StatusBar = nil) then Exit;
  38.   StatusBar.SimpleText := 'You have pressed multiplication button.';
  39. end;
  40.  
  41. function TFrame1.GetParentStatusBar: TStatusBar;
  42. var
  43.   Caller:    TWinControl;
  44.   StatusBar: TComponent;
  45. begin
  46.   Result := nil;
  47.   Caller := Parent.Parent.Parent; // tab1 > PageControl1 > Form
  48.   if not(Caller is TForm) then Exit;
  49.   StatusBar := Caller.FindComponent('StatusBar1');
  50.   Result := (StatusBar as TStatusBar);
  51. end;

---edit---
I forget to set result = nil at the beginning of the function.

Thank you very much for your time Handoko, I'm going to test it right now.
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on August 31, 2017, 08:12:17 pm
I am trying to glue up together all the knowledge of your contributions into a brand new example, that will hopefully became more clear. Please feel free to modify and contribute at will. I'll post it later tonight .

regards
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on September 04, 2017, 04:54:11 pm
Hello,

In this example we have multiple frames and multiple forms. We have also a Pagecontrol and we must manage tabsheets correctly.

We have to be able to create tabs, destroy them, modify the order (drag'n drop?), minimize, restore, sort, scramble...

We have to manage also with two kind of frames and their event handlers. We have to be able to connect the forms between them. They have to access the contents from the frame and frames have to access contents outside them and even in other forms. I think it's an interesting exercise.
Feel free to modify, adapt, correct, what ever you think to improve it an make it as didactive as possible. Please check the code, I will post updates.

regards




Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on September 04, 2017, 05:10:19 pm
How do I call an event handler from different controls?

I mean that I want to, in example, attach two different controls to the same event:

Button1 ------> procedure TForm1.BitBtn1Click(Sender: TObject);
MenuItem  ---------> has to connect with the same procedure BitBtn1Click(Sender: TObject);

thanks
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: cpicanco on September 04, 2017, 05:24:23 pm
Do you mean something like this:

Code: Pascal  [Select][+][-]
  1. Button2.OnClick := @Button2Click;
  2. Button1.OnClick := @SharedClickEvent;
  3. MenuItem.OnClick := @SharedClickEvent;
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Handoko on September 04, 2017, 05:59:03 pm
@Raul_ES

I can't open your project. I got this error:
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on September 05, 2017, 12:13:12 am
Thanks, you can remove the uses entry that makes reference to this package, and also the control in the frame2. I am using CodeTyphon,so probably it's one of it's packages.

I have updated the 7zip file in the post.
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on September 05, 2017, 12:16:18 am
Do you mean something like this:

Code: Pascal  [Select][+][-]
  1. Button2.OnClick := @Button2Click;
  2. Button1.OnClick := @SharedClickEvent;
  3. MenuItem.OnClick := @SharedClickEvent;

Thanks cpicanco, I think that yes but excuse my dummy question: where do you place this code?
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: cpicanco on September 05, 2017, 12:56:03 am
Ok, the minimum requirement is a class. I am not sure if it should be private, public, published, but this way it will work and will be available in the Events tab of the object inspector (OnClick):

Code: Pascal  [Select][+][-]
  1. TForm1 = class(TForm)
  2. Button1 : TButton;
  3. Button2: TButton;
  4. MenuItem1: TMenuItem;
  5. procedure SharedClickEvent(Sender : TObject);
  6. private
  7.  
  8. public
  9.  
  10. end;  
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Handoko on September 06, 2017, 10:05:37 am
@Raul_ES

Your screenshot looks good. But I'm afraid you will disappointed to know it doesn't look good running on Linux (Ubuntu Mate 16.10).

I saw you asked question "Best practices for Multiplatform GUI portability". I think the most important thing to do is having different machines with different OSes for testing. If you can't afford them, you should at least have VirtualBox. I use Linux 64-bit, it has Wine for performing basic tests for Windows applications and I have an old pentium laptop with WinXP installed.

I guess your building an awesome program. May I ask what is the program you're currently developing?
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on September 07, 2017, 12:59:21 am
Thanks Handoko,

with your permission I'll use your screenshot in my other post. I run CodeTyphon under Windows, Linux, FreeBSD and OpenSolaris, on real machines. I wanna try also Raspberry Pi  :) when possible.

Porting GUIs between these systems is a pain in the neck. It's not Lazarus fault obviously, it's my lack of knowledge and experience. Even when changing from BSD/Gtk to Linux/GTK or Solaris/Gtk the results use to be painfull. I need to improve
my habilities in design. In this second example I developed the gui quickly under Windows without any considerations to portablility, so it's not really a surpraise what
you told me.


I currently develop 2 projects:

One is a molecule-analysis-design program. The other project is a pharmacy management software (complex indeed) intended for independent pharmacies and corporations. I am pharmacist, and I am currently graduating in computer science, so I would like to specialise in healthcare-scientifical IT, I think it's a very interestig field!
Right now it's just a hobby to fill my spare time but who knows if some day I will be able to run my own business.

cheers,
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on September 07, 2017, 01:17:10 am
Hi friends,

new problem.

I have a button.

It's event handler button.onClick() calls a procedure with a variable as argument:

createNewPOSFrame( type_of_frame : Integer)


Depending on it's value: 1, 2 ,3 ,4....

the procedure creates the frame with one skin/theme or another. (every Skin is a frame) But here comes the problem:

Code: Pascal  [Select][+][-]
  1. procedure TmainScreen.createNewFramePOS(tabsheetPOStheme: Integer);
  2.  
  3. const
  4.   POS_A = 1; // first skin, first type of frame
  5.   POS_B = 2;
  6.   POS_C = 3;
  7.   POS_D = 4;
  8.   POS_ ....... what ever;
  9.  
  10. var
  11.  
  12. tabsheetCounter: integer;
  13. newtabsheet: TTabSheet;
  14.  
  15. // I cannot do this!
  16. newframePOS: TFrame_POS_C;
  17. newframePOS: TFrame_POS_B;
  18. newframePOS: TFrame_POS_A;
  19.  
  20. // I can't also do: newframePOS: nil;
  21.  
  22. begin
  23. tabsheetCounter := PageControl_mainScreen.PageCount + 1;
  24. if (tabsheetCounter <= MAX_POS_TABS) then {can add a new tabsheet to pagecontrol}
  25. begin
  26.  
  27.     newtabsheet := PageControl_mainScreen.AddTabSheet;
  28.         with newtabsheet do
  29.           begin
  30.             Name := 'tabsheetPOS' + IntToStr(tabsheetCounter);
  31.             Caption := 'POS [' + IntToStr(tabsheetCounter)+']';
  32.           end;
  33.  
  34.         // I need to do something like this, but I can't:
  35.         if (tabsheetPOStheme = POS_A) then
  36.             newframePOS := TFrame_POS_A.Create(newtabsheet);
  37.         if (tabsheetKind = POS_B) then
  38.             newframePOS := TFrame_POS_B.Create(newtabsheet);
  39.         if (tabsheetKind = POS_C) then
  40.             newframePOS := TFrame_POS_C.Create(newtabsheet);
  41.  
  42.         with newframePOS do
  43.           begin
  44.             Name := 'frame' + IntToStr(tabsheetCounter);
  45.             Parent := newtabsheet;
  46.             Align := alClient;
  47.           end;
  48.         // Set the new tabsheet as the active one
  49.         PageControl_mainScreen.ActivePage := newtabsheet;
  50.  
  51. .... more stuff
  52. end;
  53. end;
  54.  

How can you manage this kind of problem? Probably is a basic one but I don't seem
to see it.


thank you,




Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Handoko on September 07, 2017, 05:35:26 am
TFrame_POS_C, TFrame_POS_B and TFrame_POS_A are descendants of TFrame right? Is yes, then you should do:

Code: Pascal  [Select][+][-]
  1. var
  2.   newframePOS: TFrame;
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on September 12, 2017, 05:41:09 pm
Thank you very much Handoko.



Just another point: Why the following doesn't work?

newframePOS : TObject;  (something more generic, let's say that I don't want to specify what
kind of control I will use)


What if I want a variable to be for some reason one or another kind of object depending on
some other option? Example.


newinput: somegenericalobject;


newinput := TComboBox.Create(....

or

newinput := TEdit.Create(... whatever


thanks
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Handoko on September 12, 2017, 07:38:45 pm
Just another point: Why the following doesn't work?

newframePOS : TObject;  (something more generic, let's say that I don't want to specify what
kind of control I will use)

Honestly, I'm not an expert of Object Oriented Programming. I know a bit more than you just because I ever wrote some components using the OOP way.

You asked why it doesn't work. Actually it can compile (although I haven't tried) and it should work. But that newframePOS cannot used as a 'normal' frame. Because on the TObject level, it does not have any features or to be exactly, it does not offer properties and methods for a frame (example: Width, Height, BorderStyle, Font, Color, etc).

If you use TObject for newframePOS and you want to use it as a TFrame, you can use type casting.
http://etutorials.org/Programming/mastering+delphi+7/Part+I+Foundations/Chapter+2+The+Delphi+Programming+Language/Type-Safe+Down-Casting/ (http://etutorials.org/Programming/mastering+delphi+7/Part+I+Foundations/Chapter+2+The+Delphi+Programming+Language/Type-Safe+Down-Casting/)

newinput: somegenericalobject;

newinput := TComboBox.Create(....
or
newinput := TEdit.Create(... whatever

For this case, you should:
Code: Pascal  [Select][+][-]
  1. var
  2.   newinput: TWinControl;
Title: Re: [REOPENED] Cloning contents from a tabsheet into another one (not the hard way!)
Post by: Raul_ES on September 26, 2017, 04:44:42 am
Using TObject it actually compiles but the program crashes (without a single message!) when running the procedure because I weren't downcasting correctly the object.
I need to work this harder. Thanks for the tutorial Handoko :-)

Anyway, as you pruposed in first place, it's more logical to use Tframe instead of TObject.
regards
TinyPortal © 2005-2018