Delphi - создание элементов управления перед запуском формы Create?

Что ж, моя проблема в следующем:

У меня есть приложение Delphi 5, которое я по сути портирую на Delphi 2010 (заменяя старые компоненты их последними версиями, исправляя неизбежные проблемы со строками Ansi / Unicode и т. Д.), И я наткнулся на своего рода заминку.

При создании одной из наших форм происходит нарушение прав доступа. Изучив его, я пришел к выводу, что причина этого в том, что один из установщиков, вызываемых в Create, пытается изменить свойство объекта в форме, которая еще не была создана.

Я немного его урезал, но в основном код выглядит так:

В декларации формы:

property EnGrpSndOption:boolean read fEnGrpSndOption write SetGrpSndOption;

В форме Create:

EnGrpSndOption := false;

В реализации:

procedure Myform.SetGrpSndOption(const Value: boolean);
begin
  fEnGrpSndOption := Value;
  btGrpSnd.Visible := Value;
end;

Вставив ShowMessage (BooltoStr (Assigned (btGrpSend), true)) прямо перед btGrpSnd.Visible: = Value, я подтвердил, что проблема в том, что btGrpSnd еще не создан.

btGrpSend - это LMDButton, но я почти уверен, что это не совсем актуально, поскольку он еще даже не был создан.

Хотя я понимаю, что мне, вероятно, следует назначать значение только после подтверждения того, что элемент управления назначен, это просто приведет к тому, что значение, установленное в create, не будет установлено на фактический элемент управления.

Итак, я хочу найти способ убедиться, что все элементы управления в форме созданы ДО запуска моего Create.

Приветствуется любая помощь в этом или информация о том, как Delphi создает формы. Это работало еще в Delphi 5, поэтому я полагаю, что причину этого следует упомянуть где-нибудь в списках изменений между версиями. В конце концов, Delphi 2010 немного новее, чем Delphi 5.


person Michael Stahre    schedule 14.12.2009    source источник
comment
Забыл упомянуть об этом: я получаю сообщение о нарушении прав доступа при размещении компонента в моей форме во время разработки. Вероятно, это происходит и во время выполнения, но я не могу этого подтвердить, так как не могу его запустить.   -  person Michael Stahre    schedule 14.12.2009
comment
Добро пожаловать в Stack Overflow, Майкл. :)   -  person Mason Wheeler    schedule 14.12.2009
comment
Спасибо ^^ Я искал на сайте другие проблемы, связанные с delphi, которые у меня тоже были, но я обычно обнаруживал, что на большинство из них уже где-то задавали вопросы и на них отвечали;)   -  person Michael Stahre    schedule 14.12.2009
comment
Думаю, мы нашли проблему. См. Изменение моего ответа.   -  person Mason Wheeler    schedule 14.12.2009
comment
Я увидел это и ответил еще двумя комментариями на этот ответ. Мне действительно нужно перестать думать о чем-то еще, что я могу сказать сразу после нажатия кнопки «Добавить комментарий».   -  person Michael Stahre    schedule 14.12.2009
comment
Честно говоря, я не совсем уверен, как это в итоге сработало, но меняю его на наследование от TFrame, заменяя все предыдущие использования ClientHeight и ClientWidth в коде (поскольку они исчезли по очевидным причинам) на высоту и ширину соответственно ... Просто решил это. Переопределение конструктора Create (началось с унаследованного; вызова) ... Я действительно не знаю, почему это не работало раньше. Может быть, потому что я до сих пор не использовал TFrame? Похоже, это не было несоответствий между DFM и типом объекта ... RAD Studio хорошо поработала, пожаловавшись на них (и исправив это почти автоматически)   -  person Michael Stahre    schedule 15.12.2009
comment
Это та часть, где проявляется любопытство - как можно ниже, как создание элементов управления работает в Delphi? Что он делает, в каком порядке и т. Д.? Можно ли найти код, который обрабатывает создание из DFM (я полагаю, оригинальный Create?), Или он встроен в компилятор?   -  person Michael Stahre    schedule 15.12.2009


Ответы (5)


Как упоминал Тобиас (но выступает против), вы можете изменить порядок создания (прямо в форме при изменении порядка создания).

Но вы также можете в методе установки проверить, создается ли форма (csCreating в form.componentstate). И если это так, вы должны сами сохранить это значение свойства и обработать его в AfterConstruction.

person BennyBechDk    schedule 14.12.2009
comment
Я посмотрю на AfterConstruction. Но если такая вещь существует, не всегда ли безопаснее использовать AfterConstruction вместо Create, когда вы пишете компонент, на котором есть элементы управления? - person Michael Stahre; 14.12.2009

Судя по вашему комментарию, вы получаете AV при его размещении во время разработки, это означает, что есть проблема с самим элементом управления, и он не был должным образом перенесен вперед. Чтобы воспроизвести его во время выполнения в контролируемых условиях, вам нужно написать небольшую программу, подобную этой:

Создайте новое приложение VCL с единой формой. Поместите TButton в форму. На кнопке OnClick сделайте что-то вроде этого:

var
   newButton: TLMDButton;
begin
   newButton := TLMDButton.Create(self);
   newButton.Parent := self;
   //assign any other properties you'd like here
end;

Поместите точку останова в конструктор и проследите за ним, пока не найдете причину нарушения прав доступа.

РЕДАКТИРОВАТЬ: Хорошо, глядя на комментарии, я думаю, что мы нашли вашу проблему!

Субэлементы управления формы инициализируются чтением файла DFM. Когда вы изменили свой элемент управления на TCustomForm, вы предоставили новый DFM для его определения? Если нет, вам нужно переопределить конструктор формы и создать элементы управления и определить их свойства вручную. Нет никакой «магии», которая инициализирует его за вас.

person Mason Wheeler    schedule 14.12.2009
comment
Я сделал это только сейчас, и это привело меня к той же строке кода, которую я нашел, просмотрев сообщение о нарушении прав доступа, полученное во время разработки. Вероятно, это то же самое, что я описал в своем исходном вопросе, поскольку именно в этой строке кода происходит нарушение прав доступа. Это означает, что это происходит как во время разработки, так и во время выполнения одинаково. Любая идея, почему элементы управления в компоненте, который я размещаю, не создаются до вызова создания компонента, когда он, по-видимому, вернулся в Delphi 5? - person Michael Stahre; 14.12.2009
comment
Опять же, забыл сказать: в любом случае, это очень полезный трюк, который я обязательно запомню. Я удивлен, что еще не подумал об этом. Спасибо :) - person Michael Stahre; 14.12.2009
comment
Его надо создать. Если вы отметите галочку, все остальные объекты в форме к тому времени уже будут созданы. Может, что-то не так при десериализации компонента? Трудно сказать без дополнительной информации. Кстати, это кнопка прямо на вашей форме или это подкомпонент более крупного компонента, который вы разместили в своей форме? - person Mason Wheeler; 14.12.2009
comment
Это выглядит так: TRadioPanel - это форма VCL, на которой расположены различные компоненты (этот LMDButton является одним из них.) В моем основном приложении мы размещаем несколько компонентов TRadioPanel в главной форме и делаем их видимыми, если пользователь нажимает их соответствующие кнопки. Думаю, это сделало бы его подкомпонентом. - person Michael Stahre; 14.12.2009
comment
TRadioPanel - это форма VCL, которая размещается на других формах? Вы имеете в виду рамку? - person Mason Wheeler; 14.12.2009
comment
Ну, изначально это был TVisualComponent, полученный из набора инструментов CDK. Поскольку это больше не доступно (и в любом случае не требуется), я изменил его на TCustomForm. Я пробовал TForm, TWinControl, TFrame и тому подобное. TVisualComponent был основан на TWinControl, так что можно подумать, что это должно работать ... Но, увы, никакой разницы. - person Michael Stahre; 14.12.2009
comment
Вероятно, это не имеет ничего общего с созданием формы, а все, что связано с инициализацией элемента управления внутри вашего настраиваемого элемента управления. Не форма. Контроль. Управление с субуправлением. Вероятно, потребуется пройти через ваш настраиваемый элемент управления строка за строкой. И было бы здорово, если бы вы могли опубликовать рабочий образец, демонстрирующий эту проблему. - person Warren P; 14.12.2009
comment
Но разве различие больше, чем просто семантика? Я думаю, это правильно называть это контролем. В любом случае, разве субэлементы управления в элементе управления не инициализируются автоматически так же, как элементы управления в форме? Я предполагаю, что это может быть та часть, где что-то изменилось со времен Delphi 5. Я посмотрю, как это воспроизвести. - person Michael Stahre; 14.12.2009
comment
Ах. Я не нашел в DFM никакой информации, относящейся к таким вещам, поэтому я просто предположил, что Delphi просматривает объекты, записанные в DFM, те, которые определены в файле .pas, и выясняет детали создания оттуда. ... Итак, остается вопрос - как мне создать новый DFM, содержащий эти данные? Единственный способ, которым я когда-либо («когда-либо» может быть немного преувеличенным словом, поскольку у меня всего три или около того опыта работы с Delphi) нашел для создания файлов DFM, - это File- ›New-› Form (или VCL Application) - person Michael Stahre; 14.12.2009
comment
Стоит отметить, что существует DFM, который при просмотре в виде текста в RAD Studio содержит записи для рассматриваемых кнопок. Правильно ли я понимаю, что DFM содержит определения, на которые вы ссылаетесь в своем редактировании данных, которые не отображаются, когда вы используете представление как форму? Открытие файла в Notepad2 обнаруживает множество данных, не являющихся открытым текстом. Некоторые из них полностью скрыты от пользователя в RAD Studio? - person Michael Stahre; 14.12.2009

Ваш Create всегда вызывается первым, перед конструктором-предком. Так работают конструкторы. Вы должны иметь возможность вызвать унаследованный конструктор, прежде чем выполнять остальную часть инициализации:

constructor MyForm.Create(Owner: TComponent);
begin
  inherited;
  EnGrpSndOption := False;
end;

Однако есть лучший способ указать, что именно вы пытаетесь сделать. Ваш класс загружает свойства из ресурса DFM. По завершении он вызовет виртуальный метод с именем Loaded. Обычно он используется для уведомления всех дочерних элементов о том, что все готово, поэтому, если кто-либо из них содержит ссылки на других дочерних элементов в форме, они знают, что использовать эти ссылки в этот момент безопасно. Вы также можете переопределить его в форме.

procedure MyForm.Loaded;
begin
  inherited;
  EnGrpSndOption := False;
end;

Однако в вашем случае это, как правило, не должно иметь большого значения. Loaded вызывается из конструктора сразу после завершения загрузки формы из ресурса DFM. Этот ресурс сообщает форме все элементы управления, которые она должна создать для себя. Если ваша кнопка не создается, возможно, она неправильно указана в DFM. В DFM могут быть перечислены элементы управления, не имеющие соответствующих полей в классе. С другой стороны, если есть опубликованное поле, для которого нет соответствующей записи в DFM, среда IDE должна предупреждать вас об этом и предлагать удалять объявление каждый раз, когда вы вызываете его в конструкторе форм. Просмотрите DFM как текст и убедитесь, что действительно существует запись для элемента управления с именем btGrpSnd.

person Rob Kennedy    schedule 14.12.2009
comment
Что ж, там, к сожалению, есть такие записи. Если DFM не содержит данных, полностью скрытых от кодировщика, которые также должны быть правильными, это не похоже на проблему. Надеюсь, что новый DFM может решить эту проблему ... Спасибо за ваше описание Loaded. Я определенно хотел знать, существует ли такая процедура. - person Michael Stahre; 14.12.2009

Достаточно ли этого хорошо, чтобы вы начали:

если назначено (btGrpSnd) и btGrpSnd.HandleAllocated, то btGrpSnd.Visible: = ...

person Warren P    schedule 14.12.2009
comment
Что ж, проблема в том, что это оставит значения без изменений, в первую очередь убрав точку установки их в Create. Кто-то уже предлагал использовать AfterConstruction для присвоения значений, которые нельзя было присвоить во время Create, но я еще не потратил на это время, потому что я действительно не хочу обдумывать проблему. Кроме того, мне очень любопытно, как автоматическое создание в delphi обрабатывает элементы управления в формах. Не удалось найти ничего конкретного об этом. Так что я мог бы обойти это способом, похожим на то, что вы предлагаете, но я бы предпочел не делать этого. Тем не менее, спасибо. - person Michael Stahre; 14.12.2009

Я вижу 2 возможности: проверьте, равно ли btGrpSnd нулю, прежде чем присваивать Value свойству Visible. Если это ноль, вы можете:

  • не устанавливать свойство
  • создать btGrpSnd

Я бы не стал возиться с порядком создания. Это более сложно и может сломаться при дальнейших изменениях.


из вашего комментария: вы можете проверить, находитесь ли вы в режиме разработки или в режиме выполнения. Перед настройкой видимости проверьте, находитесь ли вы в расчетном режиме.

if not (csDesigning in Componentstate) then
begin
  btGrpSnd:=Value;
end;

Ответ на ваш комментарий:

пойти на это:

procedure Myform.SetGrpSndOption(const Value: boolean); 
begin 
  fEnGrpSndOption := Value; 
  if btGrpSnd<>nil then btGrpSnd.Visible := Value; 
end; 

и один дополнительный параметр свойства btGrpSnd. Если установлено значение ‹> nil, также установите видимость, сохраненную в fEnGrpSndOption.

Если нет необходимости устанавливать btGrpSnd вне Myform, создайте процедуру инициализации, которая создает все. Например.:

constructor Myform.Create(...)
begin
  init;
end;

procedure init
begin
  btGrpSnd:=TButton.Create;
  ...
end;

procedure Myform.SetGrpSndOption(const Value: boolean); 
begin 
 fEnGrpSndOption := Value; 
 if btGrpSnd<>nil then init;
 btGrpSnd.Visible := Value; 
end; 

Это все еще лучше, чем в зависимости от некоторого измененного кода инициализации, который может сломаться в будущем.

person Tobias Langner    schedule 14.12.2009
comment
Однако нам нужно, чтобы это выполнялось во время разработки. Например, свойство, упомянутое в моем вопросе, должно быть возможно установить во время разработки. - person Michael Stahre; 14.12.2009