The Wiert Corner – irregular stream of stuff

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

  • My work

  • My badges

  • Twitter Updates

  • My Flickr Stream

    20140508-Delphi-2007--Project-Options--Cannot-Edit-Application-Title-HelpFile-Icon-Theming

    20140430-Fiddler-Filter-Actions-Button-Run-Filterset-now

    20140424-Windows-7-free-disk-space

    More Photos
  • Pages

  • All categories

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

    Join 1,308 other followers

Delphi – Frames as visual Components – changing your inheritance

Posted by Jeroen Pluimers 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:

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

  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. [...] 출처 : http://wiert.wordpress.com/2009/07/17/delphi-frames-as-visual-components-changing-your-inheritance/ [...]

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

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

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

  5. pult said

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

  6. [...] 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 [...]

  7. Ed J said

    Very helpful. Thank you.

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

  9. noName said

    +1

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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

Join 1,308 other followers

%d bloggers like this: