Фреймы в 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. Сделайте ребилд пакета.
Вот и все!
Такая хрень часто случается, если наследовать визуальные компоненты не через IDE. В Delphi 6, например, довольно забавно начинают себя вести наследники TDataModule в Design-time. Eсли поменять им предка только в Pas файле, они начинают рисоваться серым цветом с разметкой, точь-в-точь как формы. =)
У постоянно именно с фреймами проблема. Я их в принципе за это и не люблю, и редко использую.
Если правите руками – правьте полностью. В первом случае просто недоделали. Если тоже сделать с формами – то тоже получите чудеса, да ещё похлеще чем с фреймами.
object на inherited должно быть заменено всегда когда наследование идёт не от TForm, TFrame или TDataModule, а их наследников