Фреймы, как визуальные компоненты, изменение наследования

Источник: delphi2010
Александр Божко

Фреймы в Delphi  - интересная штука, которая может создать проблемы "на ровном месте".

Вам случалось получать подобное сообщение об ошибке при создании потомка класса TFrame?

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

И что странно: потомки  TFrame не имеют свойства ClientHeight.

Если вы нажмете Cancel, то получите следующее сообщение об ошибке:

---------------------------
Error
---------------------------
Error creating form: Error reading TDioptreFrame.ClientHeight: Property ClientHeight does not exist.
---------------------------
OK
---------------------------

У меня такое случалось, и здесь я объясню причину, из-за которой это может происходить.

Обычно, когда вы создаете фрейм (скажем, TBaseFrameComponent), вы используете панель инструментов (или меню File > New > Other > Delphi Files > Frame).

При этом создается новый TbaseFrameComponent, который наследуется от TFrame.

Когда вы хотите создать фрейм (допустим, TMyFrameComponent), основанный на TBaseFrameComponent, вы также используете панель инструментов, (или File > New > Other > Inheritable Items > BaseFrameComponent). Так создается новый TMyFrameComponent, который наследуется от TBaseFrameComponent.

Но если у вас уже имеется TMyExistingFrameComponent, унаследованный от TFrame и вы хотите сделать его наследником TBaseFrameComponent?

Вы меняете эту декларацию:

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

На эту:

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

Если вы оставите такой код без изменений, то рано или поздно получите приведенную выше ошибку.
Поискав информацию о данной ошибке, я нашел только две релевантные Web-страницы:
TFrame descendant strange behavior
TFrame descendand dfm corrupted
Это и натолкнуло меня на верный путь.
Поскольку только TForm имеет published свойство ClientHeight (а еще TForm имеет published свойства: ClientWidth, OldCreateOrder, PixelsPerInch, TextHeight), странно, что TFrame не имеет его.
Проблема в том, что как только .pas файл был изменен, IDE думает, что это не фрейм или что-то еще, а именно форма!
Фактически это происходит потому, что IDE видит, что TCustomMyExistingFrameComponent наследуется от TBaseFrameComponent, который не распознается как визуально модифицируемый (designable) класс, такой как TFrame или TDataModule . По умолчанию он TForm, и при сохранении он будет вводить такие свойства  как ClientHeight, ClientWidth, OldCreateOrder, PixelsPerInch и TextHeight.
Фокус состоит в том, что бы, не только изменить .pas файл, но и еще изменить .dfm файл, поменять ключевое слово object на inherited.
Старый фрагмент .dfm:

 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

Новый фрагмент .dfm:

 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

После перевода отображения .dfm файла из текстового вида в вид формы и обратно, новый .dfm фрагмент чудесным образом преобразуется в что-то подобное:

   inherited CustomMyExistingFrameComponent: TCustomMyExistingFrameComponent
    Width = 71
    Height = 156
    ParentFont = False
    ExplicitWidth = 71
    ExplicitHeight = 156
   end

Теперь, как только вы перекомпилируете пакет компонентов, формы/фреймы перезагрузятся с учетом компонентов, размещенных на фрейме, и все будет нормально размещено.

В заключение маленькое резюме.

Как конвертировать фрейм -компонент TMyFrame который наследуется непосредственно от TFrame в аналогичный, наследуемый от TBaseFrameComponent.
1.    Измените .pas файл так, что бы TMyFrame наследовался не от TFrame, а от TBaseFrameComponent.
2.    Измените .dfm файл так, что бы ключевое слово object было заменено на inherited.
3.    Сделайте ребилд пакета.
Вот и все!


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=21644