The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Email Subscription

    Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 716 other subscribers

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?
Message

---------------------------
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.

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:
[sourcecode language=”delphi”]
{$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.
[/sourcecode]

to this:
[sourcecode language=”delphi”]
{$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.
[/sourcecode]
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:
[sourcecode language=”delphi”]
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
[/sourcecode]

New .dfm fragment:
[sourcecode language=”delphi”]
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
[/sourcecode]

After putting the .dfm file from text view to form view and back, the new .dfm fragment magically transforms into something like this
[sourcecode language=”delphi”]
inherited CustomMyExistingFrameComponent: TCustomMyExistingFrameComponent
Width = 71
Height = 156
ParentFont = False
ExplicitWidth = 71
ExplicitHeight = 156
end
[/sourcecode]

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 .

  1. Change the .pas file so TMyFrame now descends from TBaseFrameComponent in stead of TFrame.
  2. Change the .dfm file so the first object keyword is replaced by inherited.
  3. Rebuild your component package.

That’s it!

–jeroen

15 Responses to “Delphi – Frames as visual Components – changing your inheritance”

  1. 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.

  2. noName's avatar

    noName said

    +1

  3. Dave Leach's avatar

    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

  4. Ed J's avatar

    Ed J said

    Very helpful. Thank you.

  5. […] 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 […]

  6. pult's avatar

    pult said

    All are an unknown types IDE by default interpretes as TForm !

  7. pult's avatar

    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 …

  8. pult's avatar

    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's avatar

      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's avatar

        pult said

        fix2:
        other way – override method DefineProperty and for all unsupported characteristic to miss their reading from DFM

        • jpluimers's avatar

          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

  9. […] "на ровном месте". Ниже привожу попытку перевода статьи на эту […]

  10. […] 출처 : http://wiert.wordpress.com/2009/07/17/delphi-frames-as-visual-components-changing-your-inheritance/ […]

Leave a Comment