Recent

Author Topic: [SOLVED] How to make a TForm descendant  (Read 7090 times)

ggallo

  • New Member
  • *
  • Posts: 13
[SOLVED] How to make a TForm descendant
« on: October 01, 2014, 08:15:41 am »
Hi All!

I made 3 different forms in my application, those share similar or equal methods. I'd like to make a superclass/ancestor for these forms and put in that code (as object oriented programming says). The new code contains only potected methods, so no new published properties, neither controls placed on the ancestor form (the ancestor form is a classic unit, not a form unit itself).

But if I make this ancestor class, IDE shows a message box when I switch to form design view, the ancestor class can't found, neither a resource file, neither by RegisterClass.

So I searched all the Internet, but I can't found how to handle this problem.

Some sample code to show what I mean:

Code: Text  [Select][+][-]
  1. TEditorForm = class(TForm)
  2.   procedure ShowSBMessage(Sender: TObject);
  3.   procedure HideSBMessage(Sender: TObject);
  4. private
  5.   FProblemControlList: TFPHashObjectList;
  6.   procedure FocusChanged(Sender: TObject; LastControl: TControl);
  7. protected
  8.   procedure AddProblemControl(AControl: TWinControl; AMessage: String; ALabel: TCustomLabel = Nil);
  9.   procedure RemoveProblemControl(AControl: TWinControl);
  10.   function CheckStringField(SourceEdit: TWinControl; FieldLabel: TCustomLabel;  var TartgetString: String; ProblemMessage: String): Boolean;
  11. public
  12.   constructor Create(TheOwner: TComponent); override;
  13.   destructor Destroy; override;
  14. end;
  15.  
Code: Text  [Select][+][-]
  1. uses EditorForm
  2. TListEditForm = class(TEditorForm)
  3. protected
  4.   FDataValid: Boolean;
  5.   FClosed: boolean;
  6.   FStatusBar: TStatusBar;
  7.   FIsNew: Boolean;
  8.   FOnItemEditDone: TOnListEditDone;
  9.   procedure SetPosition(const PopupOrigin: TPoint);
  10.   procedure SetBaseParams(AStatusBar: TStatusBar; IsNew: Boolean; AOnItemEditDone: TOnListEditDone);
  11.   procedure Paint; override;
  12.   function CheckSzovegMezo(SourceEdit: TWinControl; TargetLabel: TCustomLabel; var TartgetString: String; ErrMsg: String): Boolean;
  13.   function CheckCegjegyzekszamMezo(SourceEdit: TMaskEdit; TargetLabel: TCustomLabel; var TartgetString: String): Boolean;
  14.   procedure FormCloseHelper(Sender: TObject; var CloseAction: TCloseAction);
  15.   procedure FormKeyDownHelper(Sender: TObject; var Key: Word; Shift: TShiftState);
  16. end;
  17.  
Code: Text  [Select][+][-]
  1. uses ListEditForm;
  2. TfrmCoverDialog = class(TListEditForm) <-- This is the real form I want to edit in design view
  3.   [...]
  4. end;
  5.  

The message I receive when I switch to design mode (F12) on TfrmCoverDialog:

Quote
Unable to find the component class "TListEditForm".
It is not registered via RegisterClass and no lfm was found.
It is needed by unit:
D:\GG\DebitorClient\src\coverdialog.pas
[Do not load component] [Stop loading at all] [Use TForm as ancestor]

The TEditorForm is the ancestor of 2 form types (the other not shown here), TListEditForm is the ancestor of many final forms designed in IDE. I need two level of classes above TForm, so I can't put everything in TListEditForm and delete TEditorForm.
I can edit the form in design mode if I choose the third option in the message box (Use TForm as ancestor). But I want to eleminate all errors.

I didn't made descendants of visual components until now, so I don't know how can I handle this situation in the right way?
Unfortunately I can't use class helper for TForm because I need new private variables, too. As I can't use interfaces because I want to write code once, only one place.
« Last Edit: October 02, 2014, 07:42:53 am by ggallo »
Windows 7 64 bit | openSuSE 12.3 32 bit
Lazarus 1.2.4 | fpc 2.6.4 | Win32/64 | Linux gtk2

wp

  • Hero Member
  • *****
  • Posts: 10286
Re: How to make a TForm descendant
« Reply #1 on: October 01, 2014, 10:45:55 am »
Saw for the first time that there is a template for inheriting forms. But it seems to be faulty, I can confirm your observations.

This is what must be true:
  • Inherited forms have the name of their parent in the form declaration; this seems to be correct from what you post here.
  • The parent form unit must be in the "interface" uses clause - seems to be correct as well.
  • AND: The first word in the lfm file of the form must not be "object", but "inherited" - you don't show the lfm files, so I can't tell... But from my experiments I'd say that this this not true - at least after fixing this my demo project was running: Please open the inherited lfm files (right-click on form in IDE, "View source (.lfm)"), modify the "object" to "inherited", save. (The master file, of course, must have the "object" word here).

ggallo

  • New Member
  • *
  • Posts: 13
Re: How to make a TForm descendant
« Reply #2 on: October 01, 2014, 02:42:30 pm »
Please guide me to the documentation/code you mentioned above about "template for inheriting forms", I didn't find it yet.
It follows from this that the name of my custom form classes unintentional (I choosen those names to comply with my own standards only)  :)

I based one other form class (TDetailForm) on my TEditorForm, but the error message not shown when I switch view on that descendants (real forms), this is strange. I compared the LFM files after this recognition, and found that the non-messagig form's LFM file begins with "inherited" instead of "object" - as you mentioned, too. So I replaced all occurences in affected LFM files form "object" to "inherited". But this didn't changed anything, some forms trigger the message (all forms based on TListEditForm), some forms switch without message (all TDetailForm descendants). So I confused about this.
Windows 7 64 bit | openSuSE 12.3 32 bit
Lazarus 1.2.4 | fpc 2.6.4 | Win32/64 | Linux gtk2

Mike.Cornflake

  • Hero Member
  • *****
  • Posts: 1256
Re: How to make a TForm descendant
« Reply #3 on: October 01, 2014, 06:21:51 pm »
I use both form and frame inheritance quite extensively with Lazarus.

Err, deepest I've gone is 4 levels (for my applications main form).

TForm -> TfrmPersistence -> TfrmMainBase -> TfrmDockingManager -> TfrmApplication

Using the File - New.. - Inherited Item - Inherited Project Item  I've never had any problems.  This is how you're supposed to do things.  Get your ancestor form correct first, then create descendants using this process.

Yeah - design decisions change.  You thought you didn't need inheritance at the start, but now you do.  Whoops - I understand, been there a lot myself and have got quite used to hand/hacking the lfm/pas files.

I've encountered minor issues by hand/hacking existing forms to new ancestors.   I've never managed to get this process to work by hand/hacking from inside Lazarus, but this doesn't surprise me, I never got it to work inside Delphi either.

Most of the steps below it looks to me like you've done already, but I'll go through what I do when hand/hacking.

You'll have to close Lazarus for this to work.  But before you do, ensure both the Form and the Ancestor are in the Project (Project - Project Inspector).  If they're not, this isn't going to work (well, not entirely true, I've got form inheritance working where the ancestor is in a package that's required by the project.  However, for your first few runs, don't add the extra complication of a Package)

Next, close Lazarus entirely.   Open the LFM you want to hand/hack in your editor of choice.  Change the initial object to inherited as you found.  Also do this for each and every existing control that also exists on the ancestor, but only for these.  Keep a list of these objects, you'll be needing them later.. (I'm thinking perhaps you global search/replaced Object for inherited, and have stuck inherited in front of components that don't exist on the ancestor form).

Open the forms .pas file.  Change Class(Tform) to Class(TMyAncestor).   Add the Ancestor unit to the forms .pas Interface Uses section (again, if you missed this step, this might also account for your error).

The final thing I do is to go through the list of controls in the class declaration of the now descended form.  Delete all the controls in that list that you changed object to inherited when you edited the lfm.

And yeah, that's pretty much it - none of these steps differed from the hand/hacking that I used to in the days of D5/D7. 

Open the results in Lazarus.  Should be fine. 

As you'll have developed both forms concurrently, there's a slew of settings that going to differ in the new descended form from the ancestor.  To reset everything in your descended form back to ancestor defaults, open the form in the designer, right click and choose reset.

Let us know if any of your steps differed from the above, and we'll see if we can't narrow down the problem.

Also - if you could zip all the related file here (including lfm) that would help diagnose this one...

Quote
Saw for the first time that there is a template for inheriting forms. But it seems to be faulty, I can confirm your observations.

I'm also intrigued.  By template - did you mean the File - New... - Inherited Item bit?  I've never had problems with that....
« Last Edit: October 01, 2014, 06:28:12 pm by Mike.Cornflake »
Lazarus Trunk/FPC Trunk on Windows [7, 10]
  Have you tried searching this forum or the wiki?:   http://wiki.lazarus.freepascal.org/Alternative_Main_Page
  BOOKS! (Free and otherwise): http://wiki.lazarus.freepascal.org/Pascal_and_Lazarus_Books_and_Magazines

ggallo

  • New Member
  • *
  • Posts: 13
Re: How to make a TForm descendant
« Reply #4 on: October 01, 2014, 09:28:05 pm »
Thank you for your exhaustive answer!

I went through it step-by-step (again), but I did these steps before, exactly as you wrote. I double checked the LFM files, too.

One thing that I missed until now is that I never used File->New...->Inherited option !

I manually typed in a new class in a new unit (not in a new form) as I usually do for non-visible class inheritance, but based on TForm. Then made another one based on the previous (also new unit, not new form) and then made a new form, and changed the class(TForm) to class(TMySecondAncestorForm) manually.
And this method worked for some forms and not for some others...

To test the now-known (for me at least) official method, I quickly created a new form, then File->New...->Inherited a descendant, and the again File->New...->Inherited second level descendant. And that works! No error message shown.
The strange thing is that I compared every file affected, and I didn't find and differences in these and my manually created/modified ones... That's strange.

But I now have the answer and the resolution: I recreate my two ancestor classes as forms and base my real forms on those with File->New...->Inherited.

So, thanks to everyone who helped! I will write the outcoming of this re-implemetation tomorrow.

Just a comment: On the ancestor forms I don't want to place any controls nor change any properties. I just want to add common methods to those, I don't want to re-use visible design.
Windows 7 64 bit | openSuSE 12.3 32 bit
Lazarus 1.2.4 | fpc 2.6.4 | Win32/64 | Linux gtk2

ggallo

  • New Member
  • *
  • Posts: 13
Re: How to make a TForm descendant
« Reply #5 on: October 02, 2014, 07:42:17 am »
Freshly in the morning I did the things mentioned above: made a new form to let is be the base custom class, File->New...->Instance another new form to further customize my final base class, finally changed my real visible form (didn't recreated) to use this second custom form.

It works! No error message shown when I change to design view.

What I learned yesterday: form inheritance only works right (as of now) if I make the new TForm descendant througn File->New...->Inherited.

Thank you all for quick help!

Just a comment: My descendant form names not relating nor comtaining ancestor form's class name: TForm->TClientDetailsForm->TBusinessOrgDialog. So this show me it's not necessary to fully include ancestor class's name in descendant's name.
Windows 7 64 bit | openSuSE 12.3 32 bit
Lazarus 1.2.4 | fpc 2.6.4 | Win32/64 | Linux gtk2

Mike.Cornflake

  • Hero Member
  • *****
  • Posts: 1256
Re: [SOLVED] How to make a TForm descendant
« Reply #6 on: October 02, 2014, 12:26:26 pm »
And this method worked for some forms and not for some others...

Well, it's unoffocial hand/hacking :-)  Glad to know the official way works.

But I can't get let go of why hand/hacking works sometimes but others?

Another thought occurred.  Maybe the failures used the old .lrs Lazarus Resources, and the successes used the new .lfm way.

The following was shamelessly ripped from the mailing lists - Graeme Geldenhuys asked the question, Leledumbo answered it:
 
Quote
> Is there documentation or a wiki page on converting such old
projects/forms to newer resources? Or does the IDE have this ability,
but I simply haven't found it.

- Change resource format in the project options to FPC resources
- Change {$I *.lrs} in initialization section to {$R *.lfm}, optionally
remove the initialization section if it doesn't contain any other code
- Remove LResources from uses clause
- Optionally remove existing .lrs

IF the forms that failed used the old .lrs resources, follow the steps above to switch to the newer .lfm resources...

If my working theory is incorrect, then I've no idea...

Quote
Just a comment: On the ancestor forms I don't want to place any controls nor change any properties. I just want to add common methods to those, I don't want to re-use visible design.

Good man - like the way you think.   I mentioned I used frame and form inheritence a lot.  Any UI reuse I want to do, I use frames (and I don't use frames at design time, I create and load the frames at run time).  75% of my my base forms/frames is logic, not UI (in TfrmMainBase, I introduce the TMainMenu and an About box, and my Docking Manager base in hindsight should have been a frame)
Lazarus Trunk/FPC Trunk on Windows [7, 10]
  Have you tried searching this forum or the wiki?:   http://wiki.lazarus.freepascal.org/Alternative_Main_Page
  BOOKS! (Free and otherwise): http://wiki.lazarus.freepascal.org/Pascal_and_Lazarus_Books_and_Magazines

 

TinyPortal © 2005-2018