Delphi-在运行窗体创建之前创建控件?

Delphi-在运行窗体创建之前创建控件?,delphi,forms,controls,creation,Delphi,Forms,Controls,Creation,我的问题如下: 我有一个Delphi 5应用程序,基本上我正在移植到Delphi 2010(用最新版本替换旧组件,修复不可避免的Ansi/Unicode字符串问题,等等),我遇到了一些问题 创建一个表单时,会发生访问冲突。仔细查看后,我得出结论,这是因为在Create中调用的一个setter试图更改表单上尚未创建的对象的属性 我对其进行了一点精简,但代码基本上如下所示: var newButton: TLMDButton; begin newButton := TLMDButton.

我的问题如下:

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

创建一个表单时,会发生访问冲突。仔细查看后,我得出结论,这是因为在Create中调用的一个setter试图更改表单上尚未创建的对象的属性

我对其进行了一点精简,但代码基本上如下所示:

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

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中设置的值未设置为实际控件

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

在此过程中提供的任何帮助,或关于Delphi如何创建表单的信息,都将不胜感激。
它在Delphi5中工作,所以我想应该在版本之间的更改列表中的某个地方提到它的原因。毕竟,Delphi 2010比Delphi 5更新了一点。

我看到了两种可能性:在为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; 
procedure Myform.SetGrpSndOption(常量值:布尔值);
开始
fEnGrpSndOption:=值;
如果btGrpSndnil,则btGrpSnd.Visible:=值;
结束;
和一个附加属性设置btGrpSnd。如果将其设置为值nil,则还应设置fEnGrpSndOption中的安全可见性

如果不需要在Myform之外设置btGrpSnd,请创建一个创建所有内容的init过程。例如:

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; 
构造函数Myform.Create(…)
开始
初始化;
结束;
程序初始化
开始
btGrpSnd:=TButton.Create;
...
结束;
过程Myform.SetGrpSndOption(常量值:布尔值);
开始
fEnGrpSndOption:=值;
如果为btGrpSndnil,则为init;
btGrpSnd.Visible:=值;
结束;
这比依赖于将来可能出现的某些更改的init代码黑客更好。

如Tobias所述(但主张反对),您可以更改创建顺序(就在更改创建顺序的表单上)


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

从您的评论中可以看出,在设计时放置该属性时,您将获得一个AV,这意味着控件本身存在问题,并且未正确地向前移植。要在受控环境下在运行时复制它,您需要编写如下的小程序:

var
   newButton: TLMDButton;
begin
   newButton := TLMDButton.Create(self);
   newButton.Parent := self;
   //assign any other properties you'd like here
end;
使用单个表单创建一个新的VCL应用程序。在表单上放置一个T按钮。在按钮的OnClick上,执行以下操作:

var
   newButton: TLMDButton;
begin
   newButton := TLMDButton.Create(self);
   newButton.Parent := self;
   //assign any other properties you'd like here
end;
在构造函数上放置断点并跟踪它,直到找到导致访问冲突的原因

编辑:好的,通过查看评论,我想我们发现了您的问题


通过读取DFM文件初始化窗体的子控件。当您将控件更改为TCustomForm时,是否提供了新的DFM来定义它?如果没有,则需要重写窗体的构造函数,创建控件并手动定义其属性。没有什么“魔法”可以为您初始化它。

这足以让您继续:

如果已分配(btGrpSnd)和btGrpSnd.HandleAllocated,则
btGrpSnd.Visible:=…

您的
创建总是在祖先构造函数之前首先调用。这就是构造器的工作方式。在执行其余初始化之前,您应该能够调用继承的构造函数:

constructor MyForm.Create(Owner: TComponent);
begin
  inherited;
  EnGrpSndOption := False;
end;
然而,有一种更好的方式来表明你想要实现什么。您的类正在从DFM资源加载属性。完成后,它将调用名为的虚拟方法。它通常用来通知所有的孩子一切都准备好了,所以如果他们中的任何一个在表单上持有对其他孩子的引用,他们知道在那个时候使用这些引用是安全的。您也可以在表单中重写它

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

不过,在你的情况下,这通常不会有太大区别
Loaded
在表单完成从DFM资源加载自身后立即从构造函数调用。该资源告诉表单它应该为自己创建的所有控件。如果未创建按钮,则可能未在DFM中正确列出。可以在DFM中列出类中没有相应字段的控件。另一方面,如果有一个已发布的字段在DFM中没有相应的条目,IDE应该警告您,并在您每次在表单设计器中调用该声明时提供删除该声明。以文本形式查看DFM,并确认确实有一个名为
btGrpSnd

的控件条目忘了提及这一点:在设计期间将组件放置在表单上时,我遇到了有问题的访问冲突。我