Recent

Author Topic: Accessing parent form from derived class  (Read 2225 times)

nikel

  • Full Member
  • ***
  • Posts: 240
Accessing parent form from derived class
« on: February 10, 2025, 01:25:37 pm »
Hello, I'm trying to learn and use object oriented programming. I'm working on a project which will be able to update another project. It will read and write download links from several servers using text files. Here's my code so far:

Code: Pascal  [Select][+][-]
  1. { TForm1 }
  2.  
  3. TForm1 = class(TForm)
  4.   Download_Btn: TButton;
  5.   Info_Lbl: TLabel;
  6.   procedure Download_BtnMouseDown(Sender: TObject; Button: TMouseButton;
  7.     Shift: TShiftState; X, Y: Integer);
  8.   procedure FormCreate(Sender: TObject);
  9. private
  10.  
  11. public
  12.  
  13. end;
  14.  
  15. {TVersion}
  16.  
  17. TVersion = class(TForm1)
  18. private
  19.   ProgramName        : shortstring;
  20.   NameSave           : shortstring;
  21. public
  22.   constructor Create(P: shortstring; Ns: shortstring);
  23.   destructor Destroy;
  24.  
  25.   procedure SetProgramName(P: shortstring);
  26.   function GetProgramName(): shortstring;
  27.  
  28.   procedure SetNameSave(Ns: shortstring);
  29.   function GetNameSave(): shortstring;
  30.  
  31.   procedure WriteLabel; virtual;
  32. end;
  33.  
  34. {TDownloadChannel}
  35.  
  36. TDownloadChannel = class(TVersion)
  37. private
  38.   ChannelName   : shortstring;
  39.   IndexLine     : Byte;
  40.   ShortVersion  : Integer;
  41.   FullVersion   : shortstring;
  42.   ShortLink     : shortstring;
  43.   FullLink      : string;
  44.   CharComment   : Char;
  45. public
  46.   constructor Create(P: shortstring); overload;
  47.   constructor Create(Cn: shortstring; Idx: Byte; Sv: Integer;
  48.     Fv: shortstring; Sl: shortstring; Fl: string; Cc: Char; P: shortstring; Ns: shortstring); overload;
  49.   destructor Destroy; overload;
  50.  
  51.   procedure SetChannelName(Cn: shortstring);
  52.   function GetChannelName(): shortstring;
  53.  
  54.   procedure SetIndexLine(Idx: Byte);
  55.   function GetIndexLine(): Byte;
  56.  
  57.   procedure SetShortVersion(Sv: Integer);
  58.   function GetShortVersion(): Integer;
  59.  
  60.   procedure SetFullVersion(Fv: shortstring);
  61.   function GetFullVersion(): shortstring;
  62.  
  63.   procedure SetShortLink(Sl: shortstring);
  64.   function GetShortLink(): shortstring;
  65.  
  66.   procedure SetFullLink(Fl: string);
  67.   function GetFullLink(): string;
  68.  
  69.   procedure SetCharComment(Cc: Char);
  70.   function GetCharComment(): Char;
  71.  
  72.   procedure WriteLabel; override;
  73. end;

I'm getting an error at TDownloadChannel.WriteLabel procedure while trying to set caption of the label of the form.

...actually access violation at
Quote
control.inc
GetTextMethod := TMethod(@Self.GetTextBuf);
line 3612, Lazarus 3.8

How can I fix this error? And am I on the right way?
« Last Edit: February 12, 2025, 03:04:13 am by nikel »

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1485
    • Lebeau Software
Re: Accessing parent form from derived class
« Reply #1 on: February 10, 2025, 05:02:21 pm »
All you've shown is declarations. What does the implementation code and lfm file(s) look like? Can you provide a more complete example that shows the error in action?
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

nikel

  • Full Member
  • ***
  • Posts: 240
Re: Accessing parent form from derived class
« Reply #2 on: February 10, 2025, 05:27:27 pm »
I couldn't exactly understand what you meant by saying implementation code. However this is my implementation block:

Code: Pascal  [Select][+][-]
  1. var
  2.   Form1   : TForm1;
  3.   D1      : TDownloadChannel;
  4.  
  5. implementation
  6.  
  7. {$R *.lfm}
  8.  
  9. { TForm1 }
  10.  
  11. procedure TForm1.FormCreate(Sender: TObject);
  12. var
  13.    ...
  14. begin
  15.   D1:=TDownloadChannel.Create('simple scheduler');
  16.   D1.WriteLabel;

And Unit1.lfm

Code: Pascal  [Select][+][-]
  1. object Form1: TForm1
  2.   Left = 256
  3.   Height = 88
  4.   Top = 145
  5.   Width = 512
  6.   Caption = 'Form1'
  7.   ClientHeight = 88
  8.   ClientWidth = 512
  9.   OnCreate = FormCreate
  10.   LCLVersion = '3.8.0.0'
  11.   object Download_Btn: TButton
  12.     Left = 392
  13.     Height = 25
  14.     Top = 27
  15.     Width = 75
  16.     Caption = 'Download_Btn'
  17.     TabOrder = 0
  18.     OnMouseDown = Download_BtnMouseDown
  19.   end
  20.   object Info_Lbl: TLabel
  21.     Left = 40
  22.     Height = 15
  23.     Top = 32
  24.     Width = 42
  25.     Caption = 'Info_Lbl'
  26.     ParentColor = False
  27.     WordWrap = True
  28.   end
  29. end

Thanks for your time.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1485
    • Lebeau Software
Re: Accessing parent form from derived class
« Reply #3 on: February 10, 2025, 06:51:17 pm »
I couldn't exactly understand what you meant by saying implementation code.

 :o

However this is my implementation block:

What does the code inside of TDownloadChannel.Create() and TDownloadChannel.WriteLabel() look like?  What does the lfm for TDownloadChannel look like?
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

nikel

  • Full Member
  • ***
  • Posts: 240
Re: Accessing parent form from derived class
« Reply #4 on: February 10, 2025, 06:58:18 pm »
My procedure is:

Code: Pascal  [Select][+][-]
  1. procedure TDownloadChannel.WriteLabel;
  2. begin
  3.   Info_Lbl.Caption:=Info_Lbl.Caption + #10 + ProgramName + #10 + NameSave
  4.     + #10 + ChannelName + #10 + IndexLine.ToString + #10 + ShortVersion.ToString
  5.     + #10 + FullVersion + #10 + ShortLink + #10 + FullLink;
  6. end;

I don't have another lfm file. They're not gui objects. I only created 2 classes via code.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1485
    • Lebeau Software
Re: Accessing parent form from derived class
« Reply #5 on: February 11, 2025, 01:44:26 am »
They're not gui objects.

Yes, they are. TDownloadChannel derives from TForm, which is a GUI class. And the Info_Lbl member is a GUI class, too.

The only way I can see your TDownloadChannel.WriteLabel() method crashing is if the Info_Lbl member has not been created.  That is why I also asked you to show the code for your Create() constructors.  Maybe TDownloadChannel is not initializing TForm1 correctly, which owns the Info_Lbl member.  We simply don't know, because you are not showing enough of your code to troubleshoot your problem.  That is why I asked you for a "complete example that shows the error in action".
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

egsuh

  • Hero Member
  • *****
  • Posts: 1563
Re: Accessing parent form from derived class
« Reply #6 on: February 11, 2025, 04:44:46 am »
I made a simple example to show you inhering a form. First defined Form1, and then created a new form, TForm2, and then changed

   TForm2 = class(TForm)   -->   TForm2 = class(TForm1)


Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.    {$IFDEF UNIX}
  7.    cthreads,
  8.    {$ENDIF}
  9.    {$IFDEF HASAMIGA}
  10.    athreads,
  11.    {$ENDIF}
  12.    Interfaces, // this includes the LCL widgetset
  13.    Forms, unit1, unit2
  14.    { you can add units after this };
  15.  
  16. {$R *.res}
  17.  
  18. begin
  19.    RequireDerivedFormResource:=True;
  20.    Application.Title:='project1';
  21.    Application.Scaled:=True;
  22.    Application.Initialize;
  23.    Application.CreateForm(TForm2, Form2);
  24.    Application.Run;
  25. end.
  26.  
  27. /////////////// form 1
  28. unit Unit1;
  29.  
  30. {$mode objfpc}{$H+}
  31.  
  32. interface
  33.  
  34. uses
  35.    Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  36.  
  37. type
  38.  
  39.    TForm1 = class(TForm)
  40.       Button1: TButton;
  41.       Label1: TLabel;
  42.       procedure Button1Click(Sender: TObject);
  43.    protected
  44.       procedure setText; virtual;
  45.    end;
  46.  
  47. var
  48.    Form1: TForm1;
  49.  
  50. implementation
  51.  
  52. {$R *.lfm}
  53.  
  54. { TForm1 }
  55.  
  56. procedure TForm1.Button1Click(Sender: TObject);
  57. begin
  58.    setText;
  59. end;
  60.  
  61. procedure TForm1.setText;
  62. begin
  63.    Label1.Caption := 'From Form 1';
  64. end;
  65.  
  66. end.
  67.  
  68.  
  69. //////////////////// form 2
  70. unit Unit2;
  71.  
  72. {$mode ObjFPC}{$H+}
  73.  
  74. interface
  75.  
  76. uses
  77.    Classes, SysUtils, Forms, Controls, Graphics, Dialogs, unit1;
  78.  
  79. type
  80.    TForm2 = class(TForm1)
  81.    protected
  82.       procedure setText; override;
  83.    end;
  84.  
  85. var
  86.    Form2: TForm2;
  87.  
  88. implementation
  89.  
  90. {$R *.lfm}
  91.  
  92. { TForm2 }
  93.  
  94. procedure TForm2.setText;
  95. begin
  96.    Label1.Caption := 'From Form 2';
  97. end;
  98.  
  99. end.


I do not think this is a good approach. But as you are leaning OOP, I think you may try to figure out this.
And FormCreate event handler is executed "after" form is created. So you don't have to define constructor yourself. But if you want to add other parameters to constructor (i.e. Create), they you may define them yourself.

nikel

  • Full Member
  • ***
  • Posts: 240
Re: Accessing parent form from derived class
« Reply #7 on: February 11, 2025, 05:32:08 pm »
Here's my constructors and destructors:

Code: Pascal  [Select][+][-]
  1. constructor TVersion.Create(P: shortstring; Ns: shortstring);
  2. begin
  3.   ProgramName:=P;
  4.   NameSave:=Ns;
  5. end;
  6.  
  7. destructor TVersion.Destroy;
  8. begin
  9.  
  10. end;
  11. ...
  12. constructor TDownloadChannel.Create(P: shortstring);
  13. begin
  14.   inherited Create(P, ' ');
  15.   ChannelName:=' ';
  16.   IndexLine:=0;
  17.   ShortVersion:=0;
  18.   FullVersion:=' ';
  19.   ShortLink:=' ';
  20.   FullLink:=' ';
  21.   CharComment:='#';
  22.   NameSave:=' ';
  23. end;
  24.  
  25. constructor TDownloadChannel.Create(Cn: shortstring; Idx: Byte; Sv: Integer; Fv: shortstring;
  26.   Sl: shortstring; Fl: string; Cc: Char; P: shortstring; Ns: shortstring);
  27. begin
  28.   inherited Create(P, Ns);
  29.   ChannelName:=Cn;
  30.   IndexLine:=Idx;
  31.   ShortVersion:=Sv;
  32.   FullVersion:=Fv;
  33.   ShortLink:=Sl;
  34.   FullLink:=Fl;
  35.   CharComment:=Cc;
  36.   NameSave:=Ns;
  37. end;

I attached my project. Removed contents of backup and lib folders.

cdbc

  • Hero Member
  • *****
  • Posts: 1968
    • http://www.cdbc.dk
Re: Accessing parent form from derived class
« Reply #8 on: February 11, 2025, 06:05:58 pm »
Hi
1) You inherit your classes from TForm1 ...BUT:
2) You don't include the derived 'TheOwner' parameter in your overridden
    (which you forgot) constructor  >:( How's that ever gonna be free'd?!?
3) You don't call 'inherited Create(TheOwner);' in your overridden constructor,
    that means the entire form-constructor-code gets forgotten  >:(
4) You don't call 'inherited Destroy;' in your destructor, again that forgets the
    entire form-finalization-code  >:(

NO wonder, you get an AV!!!

Methinks, you need to study more.
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

nikel

  • Full Member
  • ***
  • Posts: 240
Re: Accessing parent form from derived class
« Reply #9 on: February 11, 2025, 07:00:05 pm »
I asked to AI, it gave me these examples:

Code: Pascal  [Select][+][-]
  1. type
  2.   TMyForm = class(TForm)
  3.   private
  4.     FTheOwner: TObject;
  5.   public
  6.     constructor Create(AOwner: TComponent; ATheOwner: TObject); reintroduce;
  7.     property TheOwner: TObject read FTheOwner;
  8.   end;

Code: Pascal  [Select][+][-]
  1. constructor TMyForm.Create(AOwner: TComponent; ATheOwner: TObject);
  2. begin
  3.   inherited Create(AOwner);
  4.   FTheOwner := ATheOwner;
  5. end;

Code: Pascal  [Select][+][-]
  1. var
  2.   MyForm: TMyForm;
  3.   AnOwner: TObject;
  4. begin
  5.   AnOwner := TObject.Create;
  6.   MyForm := TMyForm.Create(Self, AnOwner);
  7.   // MyForm kullanımı
  8. end;

May I use these examples in my code?

cdbc

  • Hero Member
  • *****
  • Posts: 1968
    • http://www.cdbc.dk
Re: Accessing parent form from derived class
« Reply #10 on: February 11, 2025, 07:32:15 pm »
Hi
I'd say no!
You first have to get to grips with the mechanism, that is creating a form, keeping it alive, who's painting it and who's freeing it. It all serves a higher purpose and as such, highly skilled put together, by *very* clever people.
You cannot just grab the nearest big hammer and boink something together and expect it to work...
I'd suggest, you go out and find a decent book on beginner's Lazarus, even Delphi will do...
Learn to walk before you start running, if not, you will fall over.
Regards Benny

Added: ...and stop using that pesky AI and posting its *wrong* code here!!!
« Last Edit: February 11, 2025, 07:33:48 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

alpine

  • Hero Member
  • *****
  • Posts: 1374
Re: Accessing parent form from derived class
« Reply #11 on: February 11, 2025, 07:34:03 pm »
I asked to AI, it gave me these examples:
Methinks, you need to study more.
The AI should too. ;)
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

cdbc

  • Hero Member
  • *****
  • Posts: 1968
    • http://www.cdbc.dk
Re: Accessing parent form from derived class
« Reply #12 on: February 11, 2025, 07:38:46 pm »
Hi
@alpine: +1 - True that!
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

alpine

  • Hero Member
  • *****
  • Posts: 1374
Re: Accessing parent form from derived class
« Reply #13 on: February 11, 2025, 07:47:21 pm »
Hi
@alpine: +1 - True that!
Regards Benny
Clarke and Cubrick, circa 1968.  :-X
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1485
    • Lebeau Software
Re: Accessing parent form from derived class
« Reply #14 on: February 11, 2025, 07:57:34 pm »
Here's my constructors and destructors:

As stated earlier, your TVersion constructor is not calling the inherited TForm1 constructor, so none of the UI controls on your TDownloadChannel Form are being created from the LFM.  Always call inherited constructors!

Also, you cannot overload a destructor, you must override it instead.

Try this:

Code: Pascal  [Select][+][-]
  1. type
  2.   { TForm1 }
  3.      
  4.   TForm1 = class(TForm)
  5.     Download_Btn: TButton;
  6.     Info_Lbl: TLabel;
  7.     procedure Download_BtnMouseDown(Sender: TObject; Button: TMouseButton;
  8.       Shift: TShiftState; X, Y: Integer);
  9.     procedure FormCreate(Sender: TObject);
  10.   private
  11.      
  12.   public
  13.      
  14.   end;
  15.      
  16.   {TVersion}
  17.      
  18.   TVersion = class(TForm1)
  19.   private
  20.     ...
  21.   public
  22.     constructor Create(AOwner: TComponent; P: shortstring; Ns: shortstring); reintroduce;
  23.     destructor Destroy; override;
  24.     ...    
  25.   end;
  26.      
  27.   {TDownloadChannel}
  28.      
  29.   TDownloadChannel = class(TVersion)
  30.   private
  31.     ...
  32.   public
  33.     constructor Create(AOwner: TComponent; P: shortstring); reintroduce; overload;
  34.     constructor Create(AOwner: TComponent; Cn: shortstring; Idx: Byte; Sv: Integer;
  35.       Fv: shortstring; Sl: shortstring; Fl: string; Cc: Char; P: shortstring; Ns: shortstring); overload;
  36.     destructor Destroy; override;
  37.     ...    
  38.   end;

Code: Pascal  [Select][+][-]
  1. constructor TVersion.Create(AOwner: TComponent; P: shortstring; Ns: shortstring);
  2. begin
  3.   inherited Create(AOwner);
  4.   ...
  5. end;
  6.  
  7. destructor TVersion.Destroy;
  8. begin
  9.   ...
  10.   inherited Destroy;
  11. end;
  12.  
  13. ...
  14.  
  15. constructor TDownloadChannel.Create(AOwner: TComponent; P: shortstring);
  16. begin
  17.   Create(AOwner, ' ', 0, 0, ' ', ' ', ' ', '#', P, ' ');
  18. end;
  19.  
  20. constructor TDownloadChannel.Create(AOwner: TComponent; Cn: shortstring; Idx: Byte; Sv: Integer; Fv: shortstring;
  21.   Sl: shortstring; Fl: string; Cc: Char; P: shortstring; Ns: shortstring);
  22. begin
  23.   inherited Create(AOwner, P, Ns);
  24.   ...
  25. end;

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. var
  3.   ...
  4. begin
  5.   D1 := TDownloadChannel.Create(Self{or nil}, 'simple scheduler');
  6.   D1.WriteLabel;
  7.   ...
  8. end;

Now, that being said, I just noticed another problem - TDownloadChannel derives from TForm1, and TForm1 has an OnCreate event handler that creates a new instance of TDownloadChannel.  This means you have an endless recursion loop!  Creating a new TForm1 will create a new TDownloadChannel, which will initialize its TForm1 ancestor, which will create a new TDownloadChannel, ... and so on, forever.

Your UI design is flawed.  Either TForm1 should not have an OnCreate event handler that creates TDownloadChannel, or TDownloadChannel should not derive from TForm1.  I lean towards the latter - a download task should not itself BE a UI, but it can update a RELATED UI if needed.  So, you should decouple your download logic from your UI logic.  In this case, make TVersion/TDownloadChannel be a standalone class with no UI ancestor, and then give it a pointer to a separate TForm1 object whose UI it should access.
« Last Edit: February 11, 2025, 09:54:53 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

 

TinyPortal © 2005-2018