error while creating form in delphi - delphi

Hi I am having delphi application which uses more than 100 forms. There is one form call Form B which derived from the Form A.
Unit B
interace
uses A;
Type
Form B = Class(Form A)
End;
Now, when i try to open Form B on the IDE i m getting the error, "Error cerating form: Ancestor for TFormA not found". But when i open Form A and then try to form B then i am able to open form without any error.
I am not able to find why its happening. Am i missed something?

Two items to keep in mind when using form inheritance. The dfm file needs to have the declaration "inherited TFormB" instead of "object TFormB" The other item is Delphi needs to know where TFormA is located before it can create TFormB. It's been a while since I have done this and if I remember correctly, it works better when the base form has been added to the repository
Since you point out you did it manually make sure that the declaration in the dfm is using the word "inherited" instead of "object" as I described above. To make the change yourself do the following
1) open both forms.
2) Then view TFormB as text
3) Change it to inherited like described below
inherited FormB: TFormB
Caption = 'FormB'
PixelsPerInch = 96
TextHeight = 13
end
// not
object FormB: TFormB
Caption = 'FormB'
PixelsPerInch = 96
TextHeight = 13
end

You should use visual form inheritance provided by Delphi IDE; I have no Delphi 5, in Delphi XE it is accessed by File->New->Other...->Inheritable Items. I am sure it is available in Delphi 5 too, but probably from a different menu item

I had the same problem despite everything being "inherited" in the DFM file.
What fixed my problem was adding the ancestor file to the project by right-clicking in the project manager -> add and selecting the ancestor file.

Related

How to make an old form inherit from another?

I have an existing form and i want this form now, to inherit from another form.
For a new form i know how to do this, just go on File > New > Other > Inheritable items and select the father form. But what about an already existent form ?
Here is what i tried, change this :
type
TFrmMyForm = class(TForm)
To this :
type
TFrmMyForm = class(TFrmFatherForm)
But it doesn't seem to work, as FrmMyForm isn't importing FrmFatherForm components.
Is there a way to achieve this ?
Thanks
Open dfm file in some other text editor and replace object with inherited
object FrmMyForm : TFrmMyForm
to
inherited FrmMyForm : TFrmMyForm
However, Delphi has issues with opening such forms if they don't belong to the same project. For instance, if you have base form declared in a package and you are using it to inherit forms in application or another package.
If you have problem opening such forms, make sure that you first open base form and then inherited.

How to add a property to TTabSheet such that it can be used at design time with TPageControl

I would like to add "MyProperty" to TTabSheet and work with it at design time. However if I subclass it I fear I will need to also subclass TPageControl, since it internally creates/manages the TTabSheets.
I believe this would require duplicating the TTabSheet management code to reference TMyTabSheet instead of TTabSheet, since it will need to invoke TMyTabSheet.create. This feels like something I will regret when a new version of Delphi updates TPageControl and I forget to update TMyPageControl.
I am also researching "class helpers" as another option.
Does anyone have advice on how to best accomplish this?
Thanks!
Type Helpers are merely compile-time sugar, they would cause no effect over the already compiled code, that stock VCL is together with TPageControl and TForm. That is a dead-end.
However you have a slight misconception here that TPageControl... internally creates... the TTabSheets. Indeed, when you create the sheets by the means of TPageControl itself, like right-clicking it and selecting "New Tab" that si what happens. But when you create the living form object out of DFM file (or DFM resource in your compiled EXE) that is TForm itself that creates ALL the components, including both page control and its tabsheets.
Just see my answer at How to efficiently let a `ParentFont = False` child control to use same font name as parent? - that would show you how far it is about the owner - TForm, not about TPageControl or other parent components when it comes about selecting specific classes for the actual tabs or other elements..
So you are free to pursue subclassing tabsheets only.
Create the TMyTabsheet = class(TTabSheet) component
Create and install into IDE the design-time package that would introduce new subclassed tab sheet to Delphi Form Editor
In your form put the stock TPageControl and create all the needed tabs regular way
in IDE form Editor right-click over the form free space and in the menu do the "View As Text Alt-F12" command - you would see the text content of your form's DFM file
in those DFM sources find your tab sheets and change their stock TTabSheet class to be your derived sub-class
right-click the text editor and choose "View As Form Alt-F12" command
If all was done correct then Delphi would recreate the form with your new-class sheets now. Find your new properties in Object Inspector and change them.
Now switch to .Pas sources of your form and find the declarations of those tab sheets and change their type too. That is only required if you would have to access your new properties from the Delphi sources. If not you can leave their declaration as TTabSheet in pas-file as your class is direct descendant from it. You may leave those declarations as they were - but then you would have to add RegisterClass(TMyTabSheet); call into the very initialization section at the bottom of your unit, so when the form would construct itself out of the DFM it would be able to find the class implementation by the name. If you would change the declaration (at least one of those) then your form would automagically call all needed RegisterClass before streaming out of DFM. Choose any option you like.
Optionally and later, extend your design-time package to find and hijack IDE Form Editor's right-click menu for TPageControl and add "New My Subclassed Tab" command there. Just to avoid manual post-factum DFM editing. if you would do it often
This feels like something I will regret when a new version of Delphi updates TPageControl
After you created and tuned the form and saved it into DFM - it would be TForm that creates all the components out of the saved DFM-data, and that includes your tabs too. Unless very improbable event EMBT would kill the whole VCL streaming (made back in Delphi 1 in 1995) and redesign it from scratch (killing all the compatibility with existing Delphi sources at once), there should be no problem with forward compatibility with specifying your class in DFM. It is just the standard way VCL is designed - to get specific component types from the DFM.

How to debug or fix "Module has open descendants or linked modules" error?

I've had this long time problem that I can't view as text the main form for a project I inherited. Even if no other forms are open.
How can I debug the cause of this error message? What options do I have to fix it?
I found one related newsgroup post http://embarcadero.newsgroups.archived.at/public.delphi.ide/200906/0906193960.html but this only addresses the form inheritance cause, and doesn't explain anything about linked modules. I don't believe I'm using form inheritance.
I do have a DM (data module) for the project, and the form does load a couple of images from the dm through properties of a TTreeView on the form--does having a data module automatically mean I can never view as text a form in Delphi (aside from viewing the form as text in notepad)? It doesn't seem to matter whether my DM is open or closed in the IDE.
I also found one SO question with a related title (Module %s has open descendants or linked modules. can not reload) but the question itself and it's answer is not particularly relevant.
This is sometimes caused by a form that inherits from another form in your project (or the gallery) (known as Visual Form Inheritance in the documentation, IIRC). The IDE doesn't know how to find the base class for the form; it needs that opened before the descendant form. For instance, this can cause the same error if the unit containing TMyBaseForm isn't opened first in the IDE, particularly if the base (ancestor) unit is not included in the project first:
unit SpecialForm;
interface
uses
Forms, { all the other usual stuff }, BaseForm;
type
TMySpecialForm = class(TMyBaseForm)
private
public
end;
You can tell if this is the case by looking at your form's class declaration - if it descends from anything other than TForm, this is probably the cause of the error.
(Another instance of it happening is often when using a datamodule, because the base TDataModule .DFM isn't available. Attempting to view the datamodule .DFM as text will cause this error every time; the solution is to close your project and use an external editor such as Notepad or Notepad++ to edit the .dfm for your datamodule.)
I've had this issue occasionally, perhaps when I've used Frames, but my latest instance didn't involve Frames nor Data Modules nor inherited forms.
After an enormous amount of work creating a copy of the form (which copy didn't have the problem) and renaming the original unit and the form itself (which initially seemed to solve the problem), it turned out to be a live binding between forms.
Specifically, in my FMX application Form A has an options page with a TSpinBox that allows the user to set the minimum value for a TTrackBar on Form B (which was the form giving me grief). So the TSpinBox.Value was set to update the TTrackBar.Min field by means of a live binding. Closing Form A, or removing that live binding (and replacing it with an event handler to do the same thing) solved the problem.
I'd like to call upon the answer of Phillip J. Rayment and ADD that you don't have to have live binding to have this problem occur. It's sufficient to have custom control (class) of which you have an instance in another form. Then the RLink32 problem can appear and won't be solved until you close the form where you have the instance. The problem I experienced gave the following messages:
-RLink32 (during building)
-Access violation in module designide160.bpl` (if I made a modification to the problematic form)
-The module has open descendants or linked modules” error (if I tried to 'View as Form')

When, the code that create the components on the form and the code that set their properties, is called?

If I put a component on the form I don't see any code like MyComp:=TMyComponent.Create in unit code. I think the component is created automatically, but when ? And the same happends with the properties that I configured after I put the component on the form. When they are applied at runtime ?
The properties for a form and all the design time components that live on it are streamed in by the framework during the construction of the form. That process is triggered from the form's constructor, in TCustomForm.Create. The pertinent code in there looks like this:
Include(FFormState, fsCreating);
try
if not InitInheritedComponent(Self, TForm) then
raise EResNotFound.CreateFmt(SResNotFound, [ClassName]);
finally
Exclude(FFormState, fsCreating);
end;
The key is the call to InitInheritedComponent. That is a function defined in the Classes unit that does the heavy lifting. In a very broad overview it does the following:
Finds the name of the form class and looks for an RT_RCDATA resource of that name. That resource is the .dfm file.
Once the .dfm resource has been located it is parsed.
The .dfm parsing handles the assignment of properties that you set at design-time in the Object Inspector. For instance the parsing might encounter a line like this: Caption = 'My main form' and it turns that into an assignment of the string 'My main form' to the form's property Caption.
The .dfm file is hierarchical. It contains the properties for the various components and controls that are defined at design-time.
As well as setting the properties of the form's design-time components, the .dfm streaming process also instantiates those components.
In order for all of this to work, the streaming framework relies on RTTI. It knows nothing at all at compile time of your classes and components. Hence the need for RTTI. The streaming framework uses the old style RTTI and in fact that is the reason for the existence of old style RTTI. If ever you wonder about why old style RTTI is the way it is, try to view it from the perspective of having been designed to support streaming.
Information about controls and components as well as their properties that you edit while designing in IDE will be stored in your form .dfm file. Creation of that form in run-time will trigger the process of automatic loading of that .dfm file and all controls and components will be initialized at that time.
This is rather simple explanation of what exactly happens, you can start debugger at form creation line and follow what is happening there, but it is quite longish process and you can easily get lost if you are still learning.
You can find form creation code that Delphi automatically writes in .dpr file of your project.
Application.CreateForm(TForm1, Form1);

Delphi: 'Property ClientHeight does Not Exist'

My Delphi program builds and compiles fine, however as soon as it is run in the debug mode, I get the following error;
Property ClientHeight does Not Exist
After looking through all of the .DFM file sources, in every form the code is there which is;
ClientHeight = 111
I'm not understanding where I go wrong here?
Your forms would have been saved with a newer version of Delphi. Unfortunately you will need to open each form in the IDE and save it again to clear the newer properties. There is a tool that can help you called DFMCheck (http://andy.jgknet.de/blog/ide-tools/dfmcheck/). This is an add on that will go through all of your forms and tell you about any problems with the forms that will only show up at runtime.
The reason why you are seeing the problem is this. Delphi saves the forms with all of the properties. It uses streaming to load the forms at runtime. When it tries to load a form with properties that don't exist then you will get an error like this as the streaming system is trying to set a property on a component when the property doesn't exist.
I know this is old thread, but hopefully this will help others that has this problem.
In cases like this where your class inheriteds from other and you know the properties are there, just re-publish them .Add a published section and add them again for example:
published
property ClientWidth;
property ClientHeight;
This then forces the compiler to compile these typeinfo for parts where the parents classes might have forward declarations and thus , resolve your issue.
Hope it helps somebody, took me 3 days to get to the solution eventually.
Same bug happens in modern Delphi (e.g. Rio 10.3) with FMX frames. After some investigation it revealed to be caused by tweaking TFrame inheritance. Example below:
type
// Declaration of custom type
TFrameEx = class(TFrame) .. {here I override a couple of methods} end;
// Causes a bug (described below)
TMyFrame = class(TFrameEx)
// Works fine
TMyFrame = class(TFrame)
Explanation:
Due to changed type, Delphi was unable to correctly choose TMyFrame type between FMX and VCL. So when the TMyFrame was opened in IDE it would ask to strip out FMX properties (non-existent in VCL, e.g. Size.Width) and add VCL properties (e.g. ClientWidth). When saved, that would make the TMyFrame buggy - it would show the "Property ClientHeight does Not Exist" error in runtime upon init.
Had similar bug. First you need a dfm file for your frame.
When you inherit a frame, the dfm file must starts with "inherited MyFrame: TFRameEx" and NOT "object MyFrame: TFrameEx". Without the inherited, when I did it, it was adding TForm properties and in the editor the frame had TForm events, in Delphi 10.3. So delphi really needs the dfm to find the right type. If you use the ide menu, it will be done automatically. New->Others->inheritables it will create the dfm with the inherited line, create a file with {$R *.dfm} in it and a line in the project source "unitname in '......pas' {MyFrame TFrame};" Or you can do it by hand.
As for the possibility of having multiple frames in the same unit, havent tested it myself but since the line is {$R *.dfm} it might be doable.
wanted it to be a comment for the solution of kromster but cant comment apparently.
In my case, I was inheriting TFrame that was save in Delphi 7, and I change the .dfm to resolve.
The first line: "object" frmMain: TfrmMain
I changed to "inherited", like this: inherited frmMain: TfrmMain

Resources