Delphi - 在表单之前创建控件Create是否运行?

时间:2009-12-14 11:48:36

标签: delphi forms controls creation

好吧,我的问题如下:

我有一个Delphi 5应用程序,我基本上移植到Delphi 2010(用最新版本替换旧组件,修复不可避免的Ansi / Unicode字符串问题等),我遇到了一些麻烦。

创建我们的某个表单后,会发生访问冲突。在查看之后,我得出结论,原因是因为Create中的一个setter尝试在尚未创建的表单上更改对象的属性。

我把它修剪了一下,但代码基本上是这样的:

在表格声明中:

property EnGrpSndOption:boolean read fEnGrpSndOption write SetGrpSndOption;

在表单的创​​建中:

EnGrpSndOption := false;

实施中:

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

在btGrpSnd.Visible:= Value之前抛出ShowMessage(BooltoStr(Assigned(btGrpSend),true)),我确认问题是还没有创建btGrpSnd。

btGrpSend是一个LMDButton,但我很确定它不太相关,因为它还没有被创建。

虽然我意识到我可能只应在确认控件被分配后才分配一个值,这只会导致创建的值没有设置为实际控件。

所以我想要做的是找到一种方法来确保在我的Create运行之前创建表单上的所有控件。

任何有关这方面的帮助,或有关Delphi如何创建表单的信息将不胜感激。 它在Delphi 5中工作,所以我想在版本之间的变化列表中应该提到它的原因。 Delphi 2010毕竟比Delphi 5更新。

5 个答案:

答案 0 :(得分:3)

就像托比亚斯提到的那样(但主张反对)你可以改变创作顺序(在改变创作顺序时在表格上)。

但您也可以在setter方法中检查表单是否正在创建(csCreating in form.componentstate)。如果是,你必须自己存储该属性值,并在AfterConstruction中处理它。

答案 1 :(得分:2)

根据你的评论,你在设计时放置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来定义它?如果没有,则需要覆盖表单的构造函数并创建控件并手动定义其属性。没有“魔法”会为你初始化它。

答案 2 :(得分:1)

在祖先构造函数之前,始终首先调用Create。这就是构造函数的工作原理。在进行剩余的初始化之前,您应该能够调用继承的构造函数:

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

然而,有一种更好的方式来表明你正在努力实现的目标。您的类正在从DFM资源加载属性。完成后,它将调用名为Loaded的虚拟方法。它通常用于通知所有孩子一切都准备就绪,所以如果他们中的任何一个在表格上持有对其他孩子的引用,他们就知道在那时使用这些参考是安全的。您也可以在表单中覆盖它。

procedure MyForm.Loaded;
begin
  inherited;
  EnGrpSndOption := False;
end;
但是,这通常不会对你的情况产生太大影响。在表单从DFM资源加载自身后,立即从构造函数中调用Loaded。该资源告诉表单它应该为自己创建的所有控件。如果未创建按钮,则可能未在DFM中正确列出。控件可以在DFM中列出,但在类中没有相应的字段。另一方面,如果发布的字段在DFM中没有相应的条目,则IDE应该向您发出警告并提供每次在表单设计器中显示时删除声明。以文本形式查看您的DFM,并确认名为btGrpSnd的控件确实有一个条目。

答案 3 :(得分:0)

我看到两种可能性:在将Value赋给Visible属性之前检查btGrpSnd是否为nil。如果它是零,你可以:

  • 未设置属性
  • 创建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。如果将其设置为值&lt;&gt; nil,也可以在fEnGrpSndOption中设置可见性。

如果不需要在Myform外部设置btGrpSnd,请创建一个创建所有内容的init-procedure。 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; 

这仍然更好,取决于一些可能在将来破坏的更改的init-code-hack。

答案 4 :(得分:0)

这足以让你前进:

如果Assigned(btGrpSnd)和btGrpSnd.HandleAllocated那么       btGrpSnd.Visible:= ...