Recent

Author Topic: [SOLVED] How to know the form that launched another one?  (Read 1599 times)

Raul_ES

  • Full Member
  • ***
  • Posts: 122
  • My interests: Healthcare & Computers
    • My Linkedin Profile, you can add me to stay in contact.
[SOLVED] How to know the form that launched another one?
« on: February 26, 2020, 05:49:11 pm »
Hi all,

Quick question. How do I get to know the Parent(or owner??) of my actual running form so I can access its properties?

I mean:

*I have one mainForm (not sure if I shall call it Owner or Parent).

*Another "child" form is shown with ShowModal when clicked a certain button from mainForm (i.e.)

*The (child) secondForm is a reusable object used in multiple projects, so I need to decouple it from any parent form code as far as possible.

*Hence I cannot use anything like "mainForm.Color" from inside secondForm. I tried something like GetParentForm.Self.Name but it doesn't work the way I thought :-/ There is no GetOwnerForm by the way.

thanks,
have a nice day.
« Last Edit: March 04, 2020, 06:13:05 pm by Raul_ES »
Pharmacy + Chemistry + Biology + Healthcare + Computing

If you have any interest or project related with these areas feel free to contact me!

Handoko

  • Hero Member
  • *****
  • Posts: 3440
  • My goal: build my own game engine using Lazarus
Re: How to know the form that launched another one?
« Reply #1 on: February 26, 2020, 05:59:49 pm »
I will try to do this:

1. Create a global variable in the modal form, like var CallerForm: TForm;
2. Any (main)form must set the ModalForm.CallerForm := self; before calling the modal form
3. On the modal form you can use CallerForm.Color := something;

If you prefer OOP style, for #1 create a public method (you can name it) SetCaller to store the caller form.
« Last Edit: February 26, 2020, 06:15:01 pm by Handoko »

lucamar

  • Hero Member
  • *****
  • Posts: 2591
Re: How to know the form that launched another one?
« Reply #2 on: February 26, 2020, 06:30:17 pm »
Wouldn't it be better to overload ShowModal (or add an Execute method) to make it acept a parameter like, say: CallingForm: TCustomForm? That method would set a field in the modal dialog containing the caling form, if any.

This way there would be no need to modify anything in the calling forms but the call to Execute/ShowModal.

BTW, Handoko, I wouldn't use a global to store the callee; in certain circumstances several instances of the "modal form" may be called to show themselves at the same time, which would overwrite the global var.
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.6/FPC 3.0.4 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Handoko

  • Hero Member
  • *****
  • Posts: 3440
  • My goal: build my own game engine using Lazarus
Re: How to know the form that launched another one?
« Reply #3 on: February 26, 2020, 06:34:49 pm »
BTW, Handoko, I wouldn't use a global to store the callee; in certain circumstances several instances of the "modal form" may be called to show themselves at the same time, which would overwrite the global var.

Maybe you're right, I never knew about it. The OOP style is better but a bit harder. I think if the OP know how to write OOP code he should already has the knowledge to solve this issue by himself.
« Last Edit: February 26, 2020, 06:46:08 pm by Handoko »

GetMem

  • Hero Member
  • *****
  • Posts: 3631
Re: How to know the form that launched another one?
« Reply #4 on: February 26, 2020, 06:51:15 pm »
@lucamar is right. You should pass the caller form to the constructor, like this:
Code: Pascal  [Select]
  1. type
  2.   TForm1 = class(TForm)
  3.   private
  4.     FCallerForm: TForm;
  5.   public
  6.     constructor Create(TheOwner: TComponent; ACallerForm: TForm); reintroduce;
  7.   public
  8.   end;
  9.  
  10. var
  11.   Form1: TForm1;
  12.  
  13. implementation
  14.  
  15. constructor TForm1.Create(TheOwner: TComponent; ACallerForm: TForm);
  16. begin
  17.   inherited Create(TheOwner);
  18.   FCallerForm := ACallerForm;
  19. end;

winni

  • Hero Member
  • *****
  • Posts: 1158
Re: How to know the form that launched another one?
« Reply #5 on: February 26, 2020, 07:23:28 pm »
Hi!

fpc allows this restricted way of recursion between units/forms:

In unit1/Form1:
Code: Pascal  [Select]
  1. Interface
  2. uses ..... unit2;
  3. ...

And in unit2/Form2:

Code: Pascal  [Select]
  1. Interface
  2. .....
  3. Implementation
  4. uses .....,unit1;
  5.  

So you can access in unit2 the properties of Form1.

Winni




MarkMLl

  • Hero Member
  • *****
  • Posts: 711
Re: How to know the form that launched another one?
« Reply #6 on: February 26, 2020, 10:43:14 pm »
BTW, Handoko, I wouldn't use a global to store the callee; in certain circumstances several instances of the "modal form" may be called to show themselves at the same time, which would overwrite the global var.

Which is certainly odd behaviour for something which is intended to be modal :-/

I'd suggest that the best way is for (the object corresponding to) the form to publish something like

procedure TChildForm.ShowMe(caller: TForm);

so that the caller becomes part of the state (variables on the stack) of the function which sets things up and tears them down afterwards.

MarkMLl
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

kupferstecher

  • Sr. Member
  • ****
  • Posts: 365
Re: How to know the form that launched another one?
« Reply #7 on: February 26, 2020, 10:45:25 pm »
*The (child) secondForm is a reusable object used in multiple projects, so I need to decouple it from any parent form code as far as possible
Wouldn't be a Callback more suitable for decoupling the units? So the actual form changes of the parent form is done within itself.

Code: Pascal  [Select]
  1. TModalForm =class...
  2.  public
  3.   FormColorChange: Procedure(aColor:TColor) of object;
  4. end;
  5.  
  6. IMPLEMENTATION
  7.  
  8. ...
  9. if assigned(FormColorChange) then FormColorChange(clRed);
  10.  

jamie

  • Hero Member
  • *****
  • Posts: 2515
Re: How to know the form that launched another one?
« Reply #8 on: February 26, 2020, 10:48:14 pm »
Create a proxy function that brings up the Modal form. That function should set the TAG property to that of the caller..

Example
Function CallFOrm(Caller:TFORM);
 begin
   MyModalForm.Tag :=Caller.Tag; // each caller has a different ID.
   MyModalForm.Show …..
 End;

Then in your show form you can enumerate the forms owned by the APPLICATION.. and search for the form that is calling it via its TAG value

Number 1 at blue screen app creations!

lucamar

  • Hero Member
  • *****
  • Posts: 2591
Re: How to know the form that launched another one?
« Reply #9 on: February 26, 2020, 11:03:17 pm »
Create a proxy function that brings up the Modal form. That function should set the TAG property to that of the caller..

Example
Function CallFOrm(Caller:TFORM);
 begin
   MyModalForm.Tag :=Caller.Tag; // each caller has a different ID.
   MyModalForm.Show …..
 End;

Then in your show form you can enumerate the forms owned by the APPLICATION.. and search for the form that is calling it via its TAG value

Why over-complicate things? You already know which form called; you have it there, in Caller:TForm, so save that reference in a field of the form for future reference, if needed. For example:

Code: Text  [Select]
  1. function TReusable.Execute(Caller: TForm = Nil): Boolean;
  2. begin
  3.   FCaller := Caller; {"FCaller: TForm" is a field of TReusable}
  4.   Result := {Self.}ShowModal = mrYes; {or whatever is the "good" result}
  5. end;

Now you have a permanent reference to the caller which you can use elsewhere by doing:

Code: Pascal  [Select]
  1.   if Assigned(FCaller) then begin
  2.     {access whatever you want of the caller form}
  3.   end;

Of course, you now have to set that field to Nil (or to the Owner) in the constructor or, for "normal" forms, in the OnCreate handler, just as one would do for any other added field/property.
« Last Edit: February 26, 2020, 11:12:32 pm by lucamar »
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus 2.0.6/FPC 3.0.4 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Raul_ES

  • Full Member
  • ***
  • Posts: 122
  • My interests: Healthcare & Computers
    • My Linkedin Profile, you can add me to stay in contact.
Re: How to know the form that launched another one?
« Reply #10 on: February 27, 2020, 03:31:25 am »
Hi!

fpc allows this restricted way of recursion between units/forms:

In unit1/Form1:
Code: Pascal  [Select]
  1. Interface
  2. uses ..... unit2;
  3. ...


And in unit2/Form2:

Code: Pascal  [Select]
  1. Interface
  2. .....
  3. Implementation
  4. uses .....,unit1;
  5.  

So you can access in unit2 the properties of Form1.

Winni

Thanks Winni, but that's not exactly what I think I need. Your code solves a concrete case where you know the name of the unit and the parent form. Actually, what I need is to make Form2's code self-aware of whoever is calling it. We may even have multiple callers from different executables. I intend to share Form2 among a few programs that belong to the same project. And in future projects it will be reused again.

By the way, I guess that in your solution  access properties as follows:
Code: Pascal  [Select]
  1. procedure TForm2.FormCreate(Sender: TObject);
  2. begin
  3.   Form2.Color := Form1.Color;
  4. end;  
It actually works, but you need to code the name of the parent form and you may have several parent forms  %) It will eventually become difficult to mantein and reuse.

By the way, I am not sure about the convinience of accessing foreign properties this manner (Component.Property). I believe it is a violation of one of the OOP principles called "encapsulation" or ocultation (I don't remember right now).  It's more common to manipulate properties with "getters" and "setters", at least in other OOP languages, I dont know if this is correct in Object Pascal/Delphi.

I attach a screenshot of your solution tested. Thanks.
« Last Edit: February 27, 2020, 03:38:38 am by Raul_ES »
Pharmacy + Chemistry + Biology + Healthcare + Computing

If you have any interest or project related with these areas feel free to contact me!

Raul_ES

  • Full Member
  • ***
  • Posts: 122
  • My interests: Healthcare & Computers
    • My Linkedin Profile, you can add me to stay in contact.
Re: How to know the form that launched another one?
« Reply #11 on: February 27, 2020, 03:46:28 am »
Thanks all for your comments,

I attach the excercise code. I will try again tomorrow.
Pharmacy + Chemistry + Biology + Healthcare + Computing

If you have any interest or project related with these areas feel free to contact me!

Raul_ES

  • Full Member
  • ***
  • Posts: 122
  • My interests: Healthcare & Computers
    • My Linkedin Profile, you can add me to stay in contact.
Re: How to know the form that launched another one?
« Reply #12 on: February 27, 2020, 03:59:11 am »
Thanks @Handoko, at first I was thinking in a similar way using some sort of public global variable and passing it to child forms, but after reading a few things it seems not the best option.

Code: Pascal  [Select]
  1. On the modal form you can use [color=green]CallerForm.Color := something;[/color]

Any way, would the component.property method be an OOP rule or principle violation? I've been struggling with this doubt for a time.

Pharmacy + Chemistry + Biology + Healthcare + Computing

If you have any interest or project related with these areas feel free to contact me!

eljo

  • Full Member
  • ***
  • Posts: 183
Re: How to know the form that launched another one?
« Reply #13 on: February 27, 2020, 04:13:31 am »
Thanks @Handoko, at first I was thinking in a similar way using some sort of public global variable and passing it to child forms, but after reading a few things it seems not the best option.

Code: Pascal  [Select]
  1. On the modal form you can use [color=green]CallerForm.Color := something;[/color]

Any way, would the component.property method be an OOP rule or principle violation? I've been struggling with this doubt for a time.
1) create a class to hold settings do not rely on any of the existing components/forms, the single responsibility concept.
2) No Component.property does not violate any OOP princibles any property can have a get/set pair of methods that are called automatically for you, it violates OOP principles if you access directly the field for that property.

Handoko

  • Hero Member
  • *****
  • Posts: 3440
  • My goal: build my own game engine using Lazarus
Re: How to know the form that launched another one?
« Reply #14 on: February 27, 2020, 06:36:54 am »
Any way, would the component.property method be an OOP rule or principle violation?

I don't know why you think using component.property will break the principle.

Using OOP style is considered the 'better' solution. It's all depend on the programmer. For simple cases I usually use whatever is simple and works. Some hardcore programmers will tell you OOP is the best. No I don't follow that belief. I prefer procedural mode for simple cases, I use OOP style only if I know, I want and I will 'grow' that module to something more complex in the future.

Using global variable is not a 'sin', as long as you know what you're doing. But if you're working in a team, you should avoid it because others may 'misuse' it. If you want to write the code using component property, just as @eljo said you need to write its getter/setter. Or you can try @lucamar or @GetMem or @MarkMLI solutions. @Jamie's tag solution should work too, although some will tell to avoid using tag because it is as dangerous as using global variable. A callback, just as what @kupferstecher said is a brilliant solution too, if you know what he meant. I used such trick sometimes.

Well, there are indeed many ways to do the same thing. Just pick the one you feel most convenient. But for learning purpose, I suggest you to try them all.

My 2 Ȼents.
« Last Edit: February 27, 2020, 07:06:47 am by Handoko »