Delphi - Создание элементов управления до запуска формы Create? - PullRequest
2 голосов
/ 14 декабря 2009

Ну, моя проблема заключается в следующем:

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

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

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

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

property EnGrpSndOption:boolean read fEnGrpSndOption write SetGrpSndOption;

В форме создания:

EnGrpSndOption := false;

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

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

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

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

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

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

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

Ответы [ 5 ]

3 голосов
/ 14 декабря 2009

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

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

2 голосов
/ 14 декабря 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 для его определения? Если нет, вам нужно переопределить конструктор формы, создать элементы управления и определить их свойства вручную. Там нет "магии", которая инициализирует его для вас.

1 голос
/ 14 декабря 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.

0 голосов
/ 14 декабря 2009

Достаточно ли этого, чтобы начать:

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

0 голосов
/ 14 декабря 2009

Я вижу 2 возможности: проверить, равен ли btGrpSnd нулю, прежде чем присваивать значение свойству 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, создайте процедуру инициализации, которая создает все. E.g.:

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; 

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...