* * *

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

Handoko

  • Hero Member
  • *****
  • Posts: 1514
  • My goal: build my own game engine using Lazarus
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.  

Raul_ES

  • New member
  • *
  • Posts: 37
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?
Raúl
Student of Computer Science - UOC

Raul_ES

  • New member
  • *
  • Posts: 37
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!
Raúl
Student of Computer Science - UOC

taazz

  • Hero Member
  • *****
  • Posts: 4262
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.
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: 37
Thanks taazz, I think that I nearly have it but now I'm stuck in the last part.
Raúl
Student of Computer Science - UOC

Raul_ES

  • New member
  • *
  • Posts: 37
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,

Raúl
Student of Computer Science - UOC

taazz

  • Hero Member
  • *****
  • Posts: 4262
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.
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

Handoko

  • Hero Member
  • *****
  • Posts: 1514
  • My goal: build my own game engine using Lazarus
@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.
« Last Edit: August 25, 2017, 07:01:37 pm by Handoko »

taazz

  • Hero Member
  • *****
  • Posts: 4262
@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.
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

Handoko

  • Hero Member
  • *****
  • Posts: 1514
  • My goal: build my own game engine using Lazarus
I found no information about parentform. Where is the documentation?

taazz

  • Hero Member
  • *****
  • Posts: 4262
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.
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: 37

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. :-)
Raúl
Student of Computer Science - UOC

Raul_ES

  • New member
  • *
  • Posts: 37
@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.
Raúl
Student of Computer Science - UOC

Raul_ES

  • New member
  • *
  • Posts: 37
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
Raúl
Student of Computer Science - UOC

Raul_ES

  • New member
  • *
  • Posts: 37
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




« Last Edit: September 05, 2017, 12:09:47 am by Raul_ES »
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