Delphi – Frames as visual Components – changing your inheritance
Posted by jpluimers on 2009/07/22
Did you ever get this error message when creating TFrame descendants?
--------------------------- Error Reading Form --------------------------- Error reading TDioptreFrame.ClientHeight: Property ClientHeight does not exist. Ignore the error and continue? NOTE: Ignoring the error may cause components to be deleted or property values to be lost. --------------------------- Ignore Cancel Ignore All ---------------------------
It is odd: TFrame descendants do not have a ClientHeight property!
If you press Cancel, then you get this error message.
--------------------------- Error --------------------------- Error creating form: Error reading TDioptreFrame.ClientHeight: Property ClientHeight does not exist. --------------------------- OK ---------------------------
I did, and here is a reason why it can happen.
Normally when you create a frame (say TBaseFrameComponent), then you just use your tool palette (or File > New > Other > Delphi Files > Frame).
This creates a new TBaseFrameComponent that descends from TFrame.
When you want to create a frame (like TMyFrameComponent) based on TBaseFrameComponent, then you use your tool palette too (or File > New > Other > Inheritable Items > BaseFrameComponent).
This creates a new TMyFrameComponent that descends from TBaseFrameComponent.
But what if you already have a TMyExistingFrameComponent that descends from TFrame and want to have it to descend from TBaseFrameComponent?
What you do is change your declaration:
{$i Defines.Inc} unit MyExistingFrameComponentUnit; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TCustomMyExistingFrameComponent = class(TFrame) end; TMyExistingFrameComponent = class(TCustomMyExistingFrameComponent) end; implementation {$R *.dfm} end.
to this:
{$i Defines.Inc} unit MyExistingFrameComponentUnit; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, BaseFrameComponentUnit; type TCustomMyExistingFrameComponent = class(TBaseFrameComponent) end; TMyExistingFrameComponent = class(TCustomMyExistingFrameComponent) end; implementation {$R *.dfm} end.
If you leave it like that, sooner or later you will get the above error messages.
Searching for the error messages I found only two relevant web pages:
It was the last one that got me on the right track.
Since only TForm has a published ClientHeight property (these are also published in TForm: ClientWidth, OldCreateOrder, PixelsPerInch, TextHeight), this is odd: a TFrame should never have it!
The issue at hand is that because only the .pas file got changed, now the Delphi IDE thinks this is not a frame any more, but a form!
This is caused by the fact that the IDE sees that TCustomMyExistingFrameComponent is derived from TBaseFrameComponent, which it does not recognize as a designable class like TFrame or TDataModule. So it defaults to TForm, and when saving it will introduce properties like ClientHeight, ClientWidth, OldCreateOrder, PixelsPerInch and TextHeight.
The trick is to not only to change the .pas file, but also the .dfm file: in the .dfm file, you need to replace the keyword object by inherited.
Old .dfm fragment:
object CustomMyExistingFrameComponent: TCustomMyExistingFrameComponent Left = 0 Top = 0 ClientHeight = 71 ClientWidth = 156 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = True PixelsPerInch = 96 TextHeight = 13 end
New .dfm fragment:
inherited CustomMyExistingFrameComponent: TCustomMyExistingFrameComponent Left = 0 Top = 0 ClientHeight = 71 ClientWidth = 156 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = True PixelsPerInch = 96 TextHeight = 13 end
After putting the .dfm file from text view to form view and back, the new .dfm fragment magically transforms into something like this
inherited CustomMyExistingFrameComponent: TCustomMyExistingFrameComponent Width = 71 Height = 156 ParentFont = False ExplicitWidth = 71 ExplicitHeight = 156 end
Now just recompile your components package, then reload the forms/frames that depend on your framed component, and everything should be set.
Finally a small recap:
How to convert a framed component TMyFrame that descends directly from TFrame into something that descends from TBaseFrameComponent .
- Change the .pas file so TMyFrame now descends from TBaseFrameComponent in stead of TFrame.
- Change the .dfm file so the first object keyword is replaced by inherited.
- Rebuild your component package.
That’s it!
–jeroen
Leigh Harrison said
Many thanks. I encountered this issue in Delphi 2010 after changing dependencies within an existing project. In that IDE version your fix works perfectly, although there is a one-time warning when toggling from text to visual layout after making the DFM object/inherited change. I appreciate your logical documentation of the workaround.
jpluimers said
You are most welcome. I’m glad it worked wel for you (:
noName said
+1
Dave Leach said
Thank you very much for sharing this solution! In addition to changing “object” to “inherited,” I also manually deleted out the properties you mentioned above (ClientHeight, ClientWidth, OldCreateOrder, PixelsPerInch, TextHeight). That made things work perfectly. I can’t imagine having found this on my own
jpluimers said
You are most welcome. Buy me a beer if we ever meet in person :)
–jeroen
Ed J said
Very helpful. Thank you.
Delphi – TInterfacedDataModule revisted – use ‘inherited’ in your .dfm files when your datamodules look like forms in the designer « The Wiert Corner – Jeroen Pluimers’ irregular stream of Wiert stuff said
[…] should have been clearer: the solution for frames looking like forms in the designer from my post Delphi – Frames as visual Components – changing your inheritance also applies to datamodules. I hinted on that by writing This is caused by the fact that the IDE […]
pult said
All are an unknown types IDE by default interpretes as TForm !
pult said
for example:
type
TEditBase = TEdit
TEdit = class(TEditBase)
private
FTagObj: TPersistent;
published
property TagObj: TPersistent read FTagObj write FTagObj;
// You may access to property TagObj from code , but do not see it in Obejct Inspector
end;
TForm1 = class(TForm)
Edit1: TEdit;
…
procedute TForm1.Form1Create(…
begin
…
TagObj := Self;
…
Such approach often rescues in problem situations …
pult said
fix:
—
…
type
TCustomMyExistingFrameComponent = class(TBaseFrameComponent)
end;
TFrameBase = TFrame; // if you need access to base class TFrame
TFrame = TCustomMyExistingFrameComponent; // !!! we outwit IDE
TMyExistingFrameComponent = class(TCustomMyExistingFrameComponent)
end;
…
—
Now you may not worry for your DFM.
NB:
The Reason this in that that IDE does not be aware of RTTI TBaseFrameComponent.
Solve it can only if calling RegisterClass/RegisterComponent(TBaseFrameComponent). But for this it is necessary to create the package with TBaseFrameComponent and install it into IDE. Regrettably this not always possible. In VisualStudio (c#) this problem has been solved. But this therefore that c# – interpreted language.
jpluimers said
The problem with outwitting is that it not a structural solution.
This not only holds for outwitting Delphi, but in general: Side-effects caused by outwitting usually will kick you in the but when you do not expect it.
Your outwitting proposal will most likely cause your design-time inheritance to break.
It reminds me of a funny colleague inserting this in one of the core units, thereby breaking all literal references to True and False:
const
True = False;
False = not True;
My post was merely meant to provide a structural fix, not a smart ‘workaround’.
–jeroen
pult said
fix2:
other way – override method DefineProperty and for all unsupported characteristic to miss their reading from DFM
jpluimers said
Which is another outwit that has the drawback of not fixing the design time experience :-)
Of course you can combine both outwits, but it still gives me a clumsy impression.
I’m convinced that fixing the real problem (i.e. changing both the .pas and the .dfm like I postde: this is what the IDE would do when creating a new inherited frame) is the only way to go.
–jeroen
Фреймы, как визуальные компоненты, изменение наследования – Delphi 2010 said
[…] "на ровном месте". Ниже привожу попытку перевода статьи на эту […]
TFrame 상속시 문제 잡기. | Venus Debris' Blog said
[…] 출처 : https://wiert.wordpress.com/2009/07/17/delphi-frames-as-visual-components-changing-your-inheritance/ […]