* * *

Author Topic: How to access "distant" components in GUI  (Read 2279 times)

Raul_ES

  • Jr. Member
  • **
  • Posts: 62
  • My interests: Healthcare & Computers
    • My Linkedin Profile, you can add me to stay in contact.
How to access "distant" components in GUI
« on: February 22, 2018, 05:49:20 pm »
Hello,

I am struggling to understand how to do it correctly. The case is the following:

Imagine a button inside a frame or may be another form, but in this particular situation is a frame. Now imagine a label inside it's parent form. When you click button, the caption of the label is modified.

Form uses Frame's unit but not otherwise to avoid circular referencing, so the Frame cannot see the Label1 and something like Form1.Label1.Caption := "text something" is not possible because Label1 is not recognised as a valid identifier.

Our fellow programmer @Handoko proposed a clever but tricky solution (in his case to access an statusbar) in this post:

https://forum.lazarus.freepascal.org/index.php/topic,37456.msg257730.html#msg257730

but I haven't been able to replicate the solution in this case.

I found the following references:
http://delphi.xcjc.net/viewthread.php?tid=48799

and

http://forum.lazarus.freepascal.org/index.php?topic=19459.0

Why I can't do the following?
Code: Pascal  [Select]
  1. ParentForm := GetParentForm(Self);
  2.   ParentForm.(FindComponent('Label1') as TLabel).Caption := 'you pressed button whatever...';

regards
Pharmacy + Chemistry + Biology + Healthcare + Computing

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

Raul_ES

  • Jr. Member
  • **
  • Posts: 62
  • My interests: Healthcare & Computers
    • My Linkedin Profile, you can add me to stay in contact.
Re: How to access "distant" components in GUI
« Reply #1 on: February 22, 2018, 05:50:41 pm »
The error message I get is:

unit_login_frame.pas(67,14) Fatal: Syntax error, "identifier" expected but "(" found
Pharmacy + Chemistry + Biology + Healthcare + Computing

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

ASerge

  • Hero Member
  • *****
  • Posts: 900
Re: How to access "distant" components in GUI
« Reply #2 on: February 22, 2018, 06:10:45 pm »
Usually containers do not control the objects of their parent. But if you really need, then you can set the link explicitly:
Code: Pascal  [Select]
  1. unit Unit2;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, StdCtrls;
  9.  
  10. type
  11.   TFrame1 = class(TFrame)
  12.     Button1: TButton;
  13.     procedure Button1Click(Sender: TObject);
  14.   private
  15.   public
  16.     LabelForButton1: TLabel;
  17.   end;
  18.  
  19. implementation
  20.  
  21. {$R *.lfm}
  22.  
  23. procedure TFrame1.Button1Click(Sender: TObject);
  24. begin
  25.   if Assigned(LabelForButton1) then
  26.     LabelForButton1.Caption := 'Test';
  27. end;
  28.  
  29. end.
Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, StdCtrls, Unit2;
  9.  
  10. type
  11.   TForm1 = class(TForm)
  12.     Frame1_1: TFrame1;
  13.     Label1: TLabel;
  14.     procedure FormCreate(Sender: TObject);
  15.   private
  16.   public
  17.   end;
  18.  
  19. var
  20.   Form1: TForm1;
  21.  
  22. implementation
  23.  
  24. {$R *.lfm}
  25.  
  26. procedure TForm1.FormCreate(Sender: TObject);
  27. begin
  28.   Frame1_1.LabelForButton1 := Label1;
  29. end;
  30.  
  31. end.

Or assign event handlers not in Frame, but in the Form itself:
Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, StdCtrls, Unit2;
  9.  
  10. type
  11.   TForm1 = class(TForm)
  12.     Frame1_1: TFrame1;
  13.     Label1: TLabel;
  14.     procedure Button1Click(Sender: TObject);
  15.   private
  16.   public
  17.   end;
  18.  
  19. var
  20.   Form1: TForm1;
  21.  
  22. implementation
  23.  
  24. {$R *.lfm}
  25.  
  26. procedure TForm1.Button1Click(Sender: TObject);
  27. begin
  28.   Label1.Caption := 'Test';
  29. end;
  30.  
  31. end.
Code: Pascal  [Select]
  1. object Form1: TForm1
  2.   Left = 259
  3.   Height = 295
  4.   Top = 126
  5.   Width = 369
  6.   Caption = 'Form1'
  7.   ClientHeight = 295
  8.   ClientWidth = 369
  9.   LCLVersion = '1.8.0.6'
  10.   object Label1: TLabel
  11.     Left = 39
  12.     Height = 15
  13.     Top = 109
  14.     Width = 34
  15.     Caption = 'Label1'
  16.     ParentColor = False
  17.   end
  18.   inline Frame1_1: TFrame1
  19.     Left = 24
  20.     Top = 31
  21.     inherited Button1: TButton
  22.       OnClick = Button1Click
  23.     end
  24.   end
  25. end

taazz

  • Hero Member
  • *****
  • Posts: 5336
Re: How to access "distant" components in GUI
« Reply #3 on: February 22, 2018, 07:16:18 pm »
Hello,

I am struggling to understand how to do it correctly. The case is the following:

Imagine a button inside a frame or may be another form, but in this particular situation is a frame. Now imagine a label inside it's parent form. When you click button, the caption of the label is modified.

Form uses Frame's unit but not otherwise to avoid circular referencing, so the Frame cannot see the Label1 and something like Form1.Label1.Caption := "text something" is not possible because Label1 is not recognised as a valid identifier.

Our fellow programmer @Handoko proposed a clever but tricky solution (in his case to access an statusbar) in this post:

https://forum.lazarus.freepascal.org/index.php/topic,37456.msg257730.html#msg257730

but I haven't been able to replicate the solution in this case.

I found the following references:
http://delphi.xcjc.net/viewthread.php?tid=48799

and

http://forum.lazarus.freepascal.org/index.php?topic=19459.0

Why I can't do the following?
Code: Pascal  [Select]
  1. ParentForm := GetParentForm(Self);
  2.   ParentForm.(FindComponent('Label1') as TLabel).Caption := 'you pressed button whatever...';

regards
Code: Pascal  [Select]
  1.  
  2.   ParentForm := GetParentForm(Self);
  3.   (ParentForm.FindComponent('Label1') as TLabel).Caption := 'you pressed button whatever...';
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

  • Jr. Member
  • **
  • Posts: 62
  • My interests: Healthcare & Computers
    • My Linkedin Profile, you can add me to stay in contact.
Re: How to access "distant" components in GUI
« Reply #4 on: February 23, 2018, 01:25:57 am »
Thank you taazz,

Please, can you explain me the logic behind positioning the brackets the way you do? I don't get it. Or if you know the part or what shall I read in the fpc reference manual that actually treats this basic matter it would be very helpfull. Thanks again.
Pharmacy + Chemistry + Biology + Healthcare + Computing

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

Raul_ES

  • Jr. Member
  • **
  • Posts: 62
  • My interests: Healthcare & Computers
    • My Linkedin Profile, you can add me to stay in contact.
Re: How to access "distant" components in GUI
« Reply #5 on: February 23, 2018, 01:32:32 am »
Thank you very much ASerge.

For some reason I find myself very often with this situation, having to access a parent component from it's container. It's a kind of common problem in the way I use to design gui's. I'll study your example to refine my design skills. Regards.
Pharmacy + Chemistry + Biology + Healthcare + Computing

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

taazz

  • Hero Member
  • *****
  • Posts: 5336
Re: How to access "distant" components in GUI
« Reply #6 on: February 23, 2018, 01:56:25 am »
Thank you taazz,

Please, can you explain me the logic behind positioning the brackets the way you do? I don't get it. Or if you know the part or what shall I read in the fpc reference manual that actually treats this basic matter it would be very helpfull. Thanks again.
the language operator, as, is used to type cast a type in to an other and the syntax is, SourceObject as TargetType, the brackets around the expression is to instruct the compiler to use the Sourceobject as the memory address for what follows the dot instead of the targetclass/type, in your case the SourceObject is the result of the method ParentForm.FindComponent that can not be splited on the dot, findcomponent does not exists with out parentform, and parentform outside the brackets can be translated as a hard cast from the compiler.
« Last Edit: February 23, 2018, 02:01:00 am by taazz »
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

Remy Lebeau

  • Sr. Member
  • ****
  • Posts: 446
    • Lebeau Software
Re: How to access "distant" components in GUI
« Reply #7 on: February 23, 2018, 03:11:05 am »
Imagine a button inside a frame or may be another form, but in this particular situation is a frame. Now imagine a label inside it's parent form. When you click button, the caption of the label is modified.

I would have the Frame expose an event that the Form assigns a handler to when it creates the Frame object.  The Frame's button fires the event, the event handler updates the Form's Label.  This way, the Frame doesn't know or care about the Label, the Form doesn't know or care about who is firing the event or why, and the Form decides what it wants to do with the data provided by the event.

For example:

Code: [Select]
type
  TMyFrameTextEvent = procedure(Sender: TObject; const Text: String) of object;

  TFrame1 = class(TFrame)
    Button1: TButton;
    procedure ButtonClick(Sender: TObject);
  public
    OnDisplayText: TMyFrameTextEvent;
  end;

procedure TFrame1.ButtonClick(Sender: TObject);
begin
  if Assigned(OnDisplayText) then
    OnDisplayText(Self, 'you pressed button whatever...');
end;

Code: [Select]
type
  TForm1 = class(TForm)
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
  private
    Frame: TFrame1;
    procedure DisplayFrameText(Sender: TObject; const Text: string);
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Frame := TFrame1.Create(Self);
  Frame.Parent := Self;
  ...
  Frame.OnDisplayText := DisplayFrameText;
end;

procedure TForm1.DisplayFrameText(Sender: TObject; const Text: string);
begin
  Label1.Caption := Text;
end;

Why I can't do the following?
Code: Pascal  [Select]
  1. ParentForm := GetParentForm(Self);
  2.   ParentForm.(FindComponent('Label1') as TLabel).Caption := 'you pressed button whatever...';

Because it is not valid syntax.  FindComponent() is a method of TForm, you can't separate the two identifiers with a '(' character.  You want to type-cast the object that FindComponent() returns, so you have to call FindComponent() first normally, then type-cast the return value:

Code: [Select]
(ParentForm.FindComponent('Label1') as TLabel).Caption := 'you pressed button whatever...';
Which is semantically the same as doing this:

Code: [Select]
var
  Temp_Comp: TComponent;
  Temp_Label: TLabel;
...
Temp_Comp := ParentForm.FindComponent('Label1');
Temp_Label := Temp_Comp as TLabel;
Temp_Label.Caption := 'you pressed button whatever...';
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Raul_ES

  • Jr. Member
  • **
  • Posts: 62
  • My interests: Healthcare & Computers
    • My Linkedin Profile, you can add me to stay in contact.
Re: How to access "distant" components in GUI
« Reply #8 on: March 04, 2018, 02:43:24 am »
Thanks Remy & Taazz for your comments. :)
Pharmacy + Chemistry + Biology + Healthcare + Computing

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

jamie

  • Hero Member
  • *****
  • Posts: 809
Re: How to access "distant" components in GUI
« Reply #9 on: March 04, 2018, 09:18:17 pm »
You could always post/send a custom message to the parent form....

So if the parent form is to expect this message at some point, you can implement it and when
ever it receives it, it then will process it.

 The message can have parameters so it can point to a string.

In parent form..

procedure HandleFrameMessages(Var msg:Tmessage); message LM_USER+1;

--implementation --
Procedure Tform1.HandleFrameMessages(Var msg:Tmessage);
Var
  P:PChar;
Begin
  Caption := PChar(Msg.LParam)^;
  Msg.Result := ?; /// set a return value
End;

-- In FrameCode ---
 SomeResults := SendMessage(Frame1.Parent.handle, LM_USER+1, 0, Pchar(@SomeString[1]);

ect//
If the Message Handler does not exists in the form, it'll just return without action..

Hope this gives you some ideas...


Raul_ES

  • Jr. Member
  • **
  • Posts: 62
  • My interests: Healthcare & Computers
    • My Linkedin Profile, you can add me to stay in contact.
Re: How to access "distant" components in GUI
« Reply #10 on: March 14, 2018, 03:26:16 am »
Thank you jamie
Pharmacy + Chemistry + Biology + Healthcare + Computing

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

jamie

  • Hero Member
  • *****
  • Posts: 809
Re: How to access "distant" components in GUI
« Reply #11 on: March 14, 2018, 10:53:22 pm »
Wanted to make an additional note

 With frames you could always have the parent form assign one of the events.

 Like the OnClick even of the frame, something you don't use other wise. cover the frame with a
TPanel so that the frame background never gets clicked..
 
 When ever you want to signal the form...

 IF Assigned(OnClick) then OnClick(Sender);

 and in the Form handler for this you can then

read back to the frame some information contained in it..

 

Recent

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