2

好吧,我的问题如下:

我有一个 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 中设置的值未设置为实际控件。

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

对此的任何帮助或有关 Delphi 如何创建表单的信息将不胜感激。它可以在 Delphi 5 中使用,所以我想应该在版本之间的更改列表中的某个地方提到它的原因。毕竟,Delphi 2010 比 Delphi 5 更新不少。

4

5 回答 5

3

就像 Tobias 提到的(但反对),您可以更改创建顺序(就在更改创建顺序的表格中)。

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

于 2009-12-14T12:20:22.477 回答
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 来定义它?如果没有,您需要覆盖表单的构造函数并创建控件并手动定义它们的属性。没有“魔法”可以为您初始化它。

于 2009-12-14T13:08:59.010 回答
1

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.

于 2009-12-14T16:06:29.853 回答
0

我看到了 2 种可能性:在将 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。如果将其设置为值 <> nil,则还要在 fEnGrpSndOption 中设置安全可见性。

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

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。

于 2009-12-14T12:06:12.770 回答
0

这是否足以让您继续前进:

如果已分配(btGrpSnd)和 btGrpSnd.HandleAllocated 然后 btGrpSnd.Visible := ...

于 2009-12-14T15:19:04.893 回答