【问题标题】:How to tell Delphi to not include non-published properties in DFM?如何告诉 Delphi 不要在 DFM 中包含未发布的属性?
【发布时间】:2017-01-22 17:32:20
【问题描述】:

我有一个自定义控件:

type
   TContosoFrobber = class(TCustomControl)
   private
   end;

在内部,我的组件创建了一个控件

type
   TContosoFrobber = class(TCustomControl)
   private
      FMyDateTimePicker: TDateTimePicker;
   public
      constructor Create(AOwner : TComponent); override;
      property DateTimePicker: TDateTimePicker read FMyDateTimePicker;
   end;

constructor TContosoFrobber.Create(AOwner: TComponent);
begin
   inherted Create(AOwner);

   FMyControl := TMyDateTimePicker.Create(AOwner);
end;

其中 TMyDateTimePickerTDateTimePicker

的简单后代
TMyDateTimePicker = class(TDateTimePicker)
protected
end;

所以总结一下我做了什么:

  • 声明一个私有变量TDateTimePicker
  • 将其公开为TDateTimePicker 类型的公共(即非已发布)属性
  • 通过多态,控件实际上是TDateTimePicker后代

这一切都奏效了——直到我最近重新安装了 Delphi XE6(在 Windows 10 上)。

DFM

这就是为什么我无法理解为什么我在设计时遇到错误:

找不到类 TMyDateTimePicker

为什么它试图找到那个类?实现细节中的那个类;它不是为流媒体发布的。流媒体系统甚至是如何尝试创建它的!?所以我检查了 DFM:

  object cfBeachBall: TContosoFrobber
     Left = 445
     Top = 25
     Width = 101
     Height = 22
     ...snip...
     object TMyDateTimePicker
        Left = 0
        Top = 0
        Width = 101
        Height = 22
        Date = 37306.581535243100000000
        Time = 37306.581535243100000000
        TabOrder = 0
        TabStop = False
     end
  end

为什么TInternalDateTimePicker 会出现在 dfm 中:

  • 资产公开,未发布

如何阻止表单流系统在 dfm 中放置未发布的属性?

更糟糕的是错误的 IDE

它不仅有时包含它不应该包含的属性。有时它包含不应两次的属性:

  object cfPlasticBag: TContosoFrobber
     Left = 445
     Top = 25
     Width = 101
     Height = 22
     ...snip...
     object TMyDateTimePicker
        Left = 0
        Top = 0
        Width = 101
        Height = 22
        Date = 37306.581535243100000000
        Time = 37306.581535243100000000
        TabOrder = 0
        TabStop = False
     end
     object TMyDateTimePicker
        Left = 0
        Top = 0
        Width = 101
        Height = 22
        Date = 37306.581535243100000000
        Time = 37306.581535243100000000
        TabOrder = 0
        TabStop = False
     end
  end
  • 如何阻止 dfm 包含未发布的属性?
  • 如何阻止 dfm 包含它两次

破解解决方法

我知道可怕的 hack:告诉 DFM 它不应该让任何企业知道的控制:

initialization
    RegisterClass(TMyDateTimePicker);

finalization
    UnRegisterClass(TMyDateTimePicker);

end.

现在 dfm 包含一个它不知道的控件。每当我保存表单时,它都会包含对不应包含的内容的引用。最糟糕的是:我证实这是对TMyDateTimePicker 的错误信念。

为什么Delphi XE6 在重装之前没有这样做?也许我需要安装长期以来不受支持的 Delphi 版本的最新更新?

已知错误?

【问题讨论】:

  • 尽量不要设置所有者。创建时将nil 作为其所有者传递。 (当然,在这种情况下你也必须明确地释放它)。

标签: delphi delphi-xe6 dfm


【解决方案1】:

问题是您将错误的Owner 分配给TDateTimePicker。您正在分配 TContosoFrobber 的所有者,而不是 TContosoFrobber 本身:

constructor TContosoFrobber.Create(AOwner: TComponent);
begin
  inherted Create(AOwner);
  FMyControl := TMyDateTimePicker.Create(Self); // <-- not AOwner!
end;

或者:正如 Jerry Doge 所说,您可以改用 nil Owner。你只需要手动Free()TDataTimePicker

type
  TContosoFrobber = class(TCustomControl)
  private
    FMyDateTimePicker: TDateTimePicker;
  public
    constructor Create(AOwner : TComponent); override;
    destructor Destroy; override;
    ...
  end;

constructor TContosoFrobber.Create(AOwner: TComponent);
begin
  inherted Create(AOwner);
  FMyControl := TMyDateTimePicker.Create(nil);
end;

destructor TContosoFrobber.Destroy;
begin
  FMyControl.Free;
  inherted Destroy;
end;

【讨论】:

  • 每当我做这样的事情时,我通常会通过nil 并且从来没有遇到过问题。你能解释一下它实际上有什么不同吗?
  • TDataModule/TFrame/TForm 保存到 DFM 时,他们直接拥有的任何未分配 Parent 的组件也会被流式传输。在给出的示例中,TMyDateTimePicker 对象没有分配Parent。如果将其Owner 设置为表单,则可能是该问题的原因。将TContosoFrobber 设置为所有者可以避免该问题(并避免在销毁过程中需要显式的Free)。
  • 任何见解为什么它会工作 11 年(D5 和 XE6)?我正在考虑 Delphi 扩展的 RTTI 类型系统,您现在如何选择 RTTI,而不仅仅是发布的属性,也许还有一个杂散的定义或编译器选项。如果解决方案(并且确实可以解决)解释了一切,那就太好了。
  • @IanBoyd:DFM 流媒体一直以这种方式工作,一直到 D5。 DFM 流不使用扩展 RTTI(在 D2010 中引入)。我不知道您为什么现在才看到这种行为,但无论如何,原始代码对于任何版本都是错误的。
猜你喜欢
  • 2022-10-12
  • 1970-01-01
  • 2010-09-21
  • 2010-10-28
  • 1970-01-01
  • 1970-01-01
  • 2021-01-17
  • 2021-05-07
  • 2015-11-06
相关资源
最近更新 更多