How to set the default of a component object property value?
Component Class Code:
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.
In my IDE it will appear as the object property was modified (bold blue).
I thought in making default and stored functions on the TConfiguration class properties, like so:
TConfiguration
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;
It colors the properties of TConfiguration in normal blue, but not the Configuration property of TCustomClass and it's not there that I want to set its default values, is on the TCustomClass, since TConfiguration maybe be used in another components.
Then, I also thought:
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;
But, this only sets the TCustomClass Configuration property color to normal blue, not its properties.
ANSWER
As @RemyLebeau pointed out (in the first and top answer), there were errors in the code. In that component and that case I decided to not set any default value to the properties.
There are several bugs in your code.
your TCustomClass
constructor is not creating the TConfiguration
object. You need to add that:
constructor TCustomClass.Create(AOwner: TComponent);
begin
inherited;
FConfiguration := TConfiguration.Create; // <-- add this
FConfiguration.FileName := 'FileName';
FConfiguration.Size := 10;
end;
That being said, the assignment of the FileName
and Size
properties should probably be moved to the TConfiguration
constructor rather than TCustomClass
constructor.
A String
property cannot be defined with a user-defined default value, the default is always a blank string. So your FileName
property will always appear as modified when your component is created, because your constructor is assigning a non-default value to it. Your stored
approach is the correct solution to handle that. Or, simply do not assign a default FileName at all
, leave it blank. If the user does not assign an explicit FileName
, your code could assume a default value if needed.
An Integer
property, on the other hand, can be defined with a user-defined default value. Your Size
property is not doing that, though. It should be (especially if you move the assignment of the default value into the TConfiguration
constructor):
property Size: Integer read FSize write SetSize default 10;
your TConfiguration.Assign()
method is implemented incorrectly. By calling the inherited Assign()
method before copying values, your code will always raise an EConvertError
exception at runtime if the caller assigns one TConfiguration
object to another. The reason is because the base class TPersistent.Assign()
implementation simply calls Source.AssignTo(Self)
, and TConfiguration
does not override the AssignTo()
method, so TPersistent.AssignTo()
gets called, which simply calls Dest.AssignError(Self)
, which raises the exception. So, DO NOT call the inherited Assign()
method if the Source
is actually a TConfiguration
object:
procedure TConfiguration.Assign(Source: TPersistent);
begin
if Source is TConfiguration then
begin
FDelay := TConfiguration(Source).Delay;
FSize := TConfiguration(Source).Size;
end else
inherited;
end;