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