views:

1580

answers:

5

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):

  1. 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.
  2. 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.
+7  A: 

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 of object, 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.
Liron Yahdav
The global var is not required.
Erick Sasse
The global variable is only required if you let Delphi "Auto-create" the form (you can set this in the Project Options dialog).
onnodb
You guys are right. I just double checked that out. I'll edit the post, thanks.
Liron Yahdav
See also these two blog posts that explain Frame and DataModule inheritance: http://wiert.wordpress.com/2009/07/22/delphi-frames-as-visual-components-changing-your-inheritance/ and http://wiert.wordpress.com/2009/08/20/delphi-tinterfaceddatamodule-revisted-inherited-in-your-dfm-files-when-your-datamodules-look-like-forms-in-the-designer/ Those articles contain pictures of good and bad situations.
Jeroen Pluimers
+2  A: 

Liron

I think your third point re. needing the standard global variable is wrong.

Not sure if Delphi 7 did this differently but in D2006 this is certainly not a requirement. I have several projects which use visual inheritance but I have deleted all the global variables of all my forms (except for frmMain).

Marius
You are right, thanks. I edited the post.
Liron Yahdav
A: 

For your point 2, make sure the ancestor form is actually part of the project. Also, remember a form is made up of both pas and dfm file. So both must inherit from the ancestor.

Steve
A: 

Actually the ancestor doesn't have to be part to the project itself. It can be part of a bpl-package, which is included in the project group.

A: 

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
Unknown Coder