好吧,我的问题如下:
我有一个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更新。
答案 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。如果它是零,你可以:
我不会搞乱创作顺序。它更复杂,可能会因进一步的变化而中断。
来自您的评论:您可以检查您是处于设计还是运行时模式。在设置可见性之前,请检查您的设计时间。
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:= ...