How do I use or resolve issues with visual form inheritance in Delphi?
Solution 1
First, for those who don't know how to inherit a form visually, you create the ancestor form as usual. Then go to File > New > Other. Select the tab with the name of the current project, and choose the form you want to inherit from. If you want to inherit from a form that's not part of the current project, open that form, right click it, and choose Add to Repository. Then you will be able to go to File > New > Other and select that form from the appropriate tab.
Given that, I came across issues because some of the descendant forms were already created, so I couldn't follow the process above. Also, I made some changes to forms from the standard code Delphi creates. I was able to resolve all issues with visual form inheritance using the following guidelines:
- The .pas file of the descendant form must have the form's class inherit from the correct ancestor class, e.g.:
type TMyForm = class(TAncestorForm)
- The first line in the .dfm of the descendant form must have the word
inherited
instead ofobject
, e.g.:
inherited MyForm: TMyForm
- EDIT: After double checking, the following is NOT required: The .pas file of the ancestor form must have the standard global variable that Delphi creates, e.g.:
var AncestorForm: TAncestorForm;
- The
uses
section of the .dpr file of the project must have that same global variable as a comment after the unit's file name, e.g.:
unAncestor in 'unAncestor.pas' {AncestorForm}
Notes/Tips:
- Both the ancestor form and the descendant form are allowed to be non-auto created if you want (Set in Project > Options > Forms > Auto-create forms).
- To revert a property on a descendant form to the ancestor form's value, right click on the property in the Object Inspector, and choose Revert to inherited.
- To revert all property values of a component to the ancestor's values, right click the component and choose Revert to inherited.
Solution 2
The DPR seems a little bit trickier than that. In my case, I created an ancestor derived from TFrame. I then derived multiple frames from TAncestorFrame. My DPR's uses clause then looked like:
uses
Forms,
ancestorFrame in 'ancestorFrame.pas' {AncestorFrame : TFrame},
frame1Unit in 'frame1Unit.pas' {frame1:TFrame},
frame2Unit in 'frame2Unit .pas' {frame2:TFrame},
The DPROJ file should look like:
<DCCReference include="frame1Unit.pas">
<Form>frame1</Form>
<DesignClass>TFrame</DesignClass>
</DCCReference>
Derived Frames should look like:
TFrame1 = class(TAncestorFrame)
And Derived Frames .DFM files should say:
inherited Frame1:TFrame1
Comments
-
Rob about 2 years
I've been working on a project in Delphi 7 where I wanted to have forms inherit components from other forms. I was able to get this working, but came across the following issues (and I'm going to post the solutions to hopefully help others in the future):
- In the .pas file of a form, I would change the form to inherit from some other form, but it wouldn't get the components from the ancestor form.
- For certain descendant forms, I would get the following error message when opening the form at design time: "Error creating form: Ancestor for 'TAncestorForm' not found." I would have to first manually open the ancestor form, and then I could open the descendant form.
-
Erick Sasse over 15 yearsThe global var is not required.
-
Jason 'Bug' Fenter over 15 yearsThe global variable is only required if you let Delphi "Auto-create" the form (you can set this in the Project Options dialog).
-
Rob over 15 yearsYou guys are right. I just double checked that out. I'll edit the post, thanks.
-
Jeroen Wiert Pluimers over 14 yearsSee also these two blog posts that explain Frame and DataModule inheritance: wiert.wordpress.com/2009/07/22/… and wiert.wordpress.com/2009/08/20/… Those articles contain pictures of good and bad situations.
-
raju over 13 yearsFor the C++Builder people you can pretty much follow the same instructions. The only caveat I have is that TAncestorForm cannot be within its own namespace (i.e. must be in global namespace). At least this was my finding.
-
Bizmarck over 7 yearsI ran into the same problem, but in my case both the descendant and the ancestor already existed. In the project, however, the ancestor's DFM was not included in the project files. I had to remove the ancestor, then right click the project and Add > , the ancestor again. This time the DFM was included.
-
Arioch 'The almost 6 yearsI wonder if what is needed to make it work would be just mentioning the unit in the DPR file or adding the form/frame comment too. Because having those parent forms in BPLs, putting "in" clause into DPR would be cheating at best. There is another workaround though, github.com/the-Arioch/XE2_AutoOpenUnit
-
Arioch 'The almost 6 yearsSo, I tried
unAncestor {AncestorForm},
withoutin 'unAncestor.pas'
in my DPR and my Delphi XE2 acts so weirdly... It does try to auto-open something, but... Well, it throws an exception that it can open file DPR-PATH\AncestorForm ! So without in-clause it takes comment for the file name relative to DPR folder and then tries to open it without checking if the file exists! Weird -
Arioch 'The almost 6 yearsFor removing in-clause in DPR for BPL-contained units there are two abstract and one practical reasons: 1) it is anti-documentation. You document in DPR that the unit is compiled into EXE, while in reality it is compiled into BPL the EXE uses; 2) violation of single responsibility: the path to unit should be specified in one place: DPK 3) consequently I faced a situation when I had several versions of the unit in different folders. DPK and DPR chanced to point at different ones. So the real code used DPK-provided unit, while IDE pointed at DPR-documented (falsely) one. Confusing a lot.
-
Server Overflow over 3 yearsInteresting.... Which of the steps above are automatically done by the IDE and which ones you need to do manually? Also, do you need to open the ancestor form BEFORE you open the inherited form?