Delphi 2009:组件对象 属性 默认值

Delphi 2009: Component Object property default value

如何设置组件对象的默认属性值?

组件Class代码:

unit CustomClass;

interface

uses
    Classes;

type
  TCustomClass = class;

  TConfiguration = class;

  TCustomClass = class (TComponent)
  private
    FConfiguration: TConfiguration;
    procedure SetConfiguration(const Value: TConfiguration);
  published
    property Configuration: TConfiguration read FConfiguration write SetConfiguration;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

  TConfiguration = class (TPersistent)
  private
    FDelay: Integer;
    FSize: Integer;
    procedure SetDelay(const Value: Integer);
    procedure SetSize(const Value: Integer);
  published
    property Delay: Integer read FDelay write SetDelay;
    property Size: Integer read FSize write SetSize;
  public
    procedure Assign(Source: TPersistent); override;
  end;

implementation

{ TCustomClass }

constructor TCustomClass.Create(AOwner: TComponent);
begin
  inherited;
  Configuration.FileName:= 'FileName';
  Configuration.Size := 10;
end;

destructor TCustomClass.Destroy;
begin
  Configuration.Free;
  inherited;
end;

procedure TCustomClass.SetConfiguration(const Value: TConfiguration);
begin
  FConfiguration.Assign(Value);
end;

{ TConfiguration }

procedure TConfiguration.Assign(Source: TPersistent);
begin
  inherited;
  Delay := (Source as TConfiguration).Delay;
  Size := (Source as TConfiguration).Size;
end;

procedure TConfiguration.SetDelay(const Value: Integer);
begin
  FDelay := Value;
end;

procedure TConfiguration.SetSize(const Value: Integer);
begin
  FSize := Value;
end;

end.

在我的 IDE 中,它将显示为对象 属性 被修改(粗体蓝色)。

我想在 TConfiguration class 属性上创建默认函数和存储函数,如下所示:

配置

interface

private
  function FileNameStored: Boolean;
published
  property FileName: string read FFileName write SetFileName stored FileNameStored;
  property Size: Integer read FSize write SetSize default 10;

implementation

function TConfiguration.FileNameStored: Boolean;
begin
  Result := FileName <> 'FileName';
end;

它将 TConfiguration 的属性着色为普通蓝色,但不是 TCustomClass 的配置 属性 并且它不在那里,我想设置它的默认值,在 TCustomClass,因为 TConfiguration 可能会在其他组件中使用。

然后,我还想:

TCustomClass

interface

private
  function ConfigurationStored: Boolean;
published
  property Configuration: TConfiguration read FConfiguration write SetConfiguration stored ConfigurationStored;

implementation

function TCustomClass.ConfigurationStored: Boolean;
begin
  Result := Configuration.FileName <> 'FileName' and
    Configuration.Size <> 10;
end;

但是,这只会将 TCustomClass 配置 属性 颜色设置为普通蓝色,而不是其属性。

回答

正如@RemyLebeau 所指出的(在第一个也是最重要的答案中),代码中存在错误。 在那个组件和那种情况下,我决定不为属性设置任何默认值。

您的代码中有几个错误。

  1. 您的 TCustomClass 构造函数未创建 TConfiguration 对象。您需要添加:

    constructor TCustomClass.Create(AOwner: TComponent);
    begin
      inherited;
      FConfiguration := TConfiguration.Create; // <-- add this
      FConfiguration.FileName := 'FileName';
      FConfiguration.Size := 10;
    end;
    

    也就是说,FileNameSize 属性的赋值可能应该移至 TConfiguration 构造函数而不是 TCustomClass 构造函数。

  2. A String 属性 不能用用户定义的默认值来定义,默认总是空字符串。因此,当您的组件创建时,您的 FileName 属性 将始终显示为已修改,因为您的构造函数正在为其分配非默认值。您的 stored 方法是处理该问题的正确解决方案。或者,不分配默认值 FileName at all,将其留空。如果用户未指定显式 FileName,您的代码可以根据需要采用默认值。

    另一方面,Integer属性可以使用用户定义的默认值来定义。不过,您的 Size 属性 并没有这样做。它应该是(特别是如果你将默认值的赋值移动到 TConfiguration 构造函数中):

    property Size: Integer read FSize write SetSize default 10;
    
  3. 您的 TConfiguration.Assign() 方法实施不正确。通过在复制值之前调用继承的 Assign() 方法,如果调用者将一个 TConfiguration 对象分配给另一个对象,您的代码将始终在运行时引发 EConvertError 异常。原因是因为基本 class TPersistent.Assign() 实现只是调用 Source.AssignTo(Self),而 TConfiguration 没有覆盖 AssignTo() 方法,所以 TPersistent.AssignTo() 被调用,它只是调用 Dest.AssignError(Self),这会引发异常。因此,如果 Source 实际上是一个 TConfiguration 对象,请不要调用继承的 Assign() 方法:

    procedure TConfiguration.Assign(Source: TPersistent);
    begin
      if Source is TConfiguration then
      begin
        FDelay := TConfiguration(Source).Delay;
        FSize := TConfiguration(Source).Size;
      end else
        inherited;
    end;
    

But, this only sets the TCustomClass Configuration property color to normal blue, not its properties.

这是设计使然。 storage specifier 仅适用于指定的 属性。

将你 class 与 TFont 属性 在表格上进行比较。当ParentFontTrue时,Font属性不会被存储。但是它的成员 ColorName 仍然不是默认的,因此它们看起来好像将被存储。这是因为 TFont object 不知道它的所有者,怎么可能呢?一次是 canvas 的一部分,另一次是控件的一部分,或者根本没有所有者。在所有这些条件下,TFont object 应该是完全可用的,所以它不会询问它的 parent 它应该如何表现。另一方面:parent 不应该询问它的 child 如何表现。

现在回到你的场景:FileNameSize属性是否需要存储应该在TConfiguration中处理。是否应存储 TCustomClassConfiguration 属性 可能 而不是 取决于这些 FileNameSize 属性。如果 Configuration 属性 被存储(粗体),但它的所有成员都没有存储(非粗体),那么最后什么也没有存储。如果你有一个 ParentConfiguration 属性,类似 ParentFont,那么你可以不存储 Configuration 属性。否则,随它去吧,让大胆不再分散你的注意力。