There are quite a few issues with your code.
protected
FVar: string;
procedure Change; virtual;
Your SubProperty doesn't have a setter that calls Change() when FVar changes value. Not the cause of your issue, but should be noted anyway. There is no point in having an OnChange event that is not fired when changes are made.
constructor Create(AOwner: TComponent);
TPersistent doesn't have an Owner, so there is no reason to expose an Owner parameter in TExpandable's constructor, unless you want to limit it to a specific parent class, and maintain a pointer to that class during TExpandable's lifetime.
published
property Expandable: TExpandable read FExpandable write FExpandable;
Note that this is a memory leak waiting to happen. TNewComponent needs to maintain ownership of its FExpandable object at all times. If outside code were ever to assign a TExpandable object to this property, you would leak the previous object! This type of property needs a setter that calls FExpandable.Assign().
More importantly, your TNewComponent is *missing a constructor* to create the TExpandable object, that is why the Object Inspector can't work with the property - FExpandable is nil!
procedure TExpandable.Assign(Source: TPersistent);
begin
inherited Assign(Source);
end;
procedure TExpandable.AssignTo(Dest: TPersistent);
begin
inherited AssignTo(Dest);
end;
You are not testing the Source/Dest to see if a TExpandable object is being assigned to another TExpandable object so the FVar member can be copied.
With that said, the code should look more like this:
unit NewComponent;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils;
type
{ TExpandable }
TExpandable = class(TPersistent)
private
FVar: string;
FOnChange: TNotifyEvent;
procedure SetSubProperty(const Value: string);
protected
procedure Change; virtual;
public
procedure Assign(Source: TPersistent); override;
procedure AssignTo(Dest: TPersistent); override;
function IsEqual(Expandable: TExpandable): boolean;
public
property OnChange: TNotifyEvent read FOnChange write FOnChange;
published
property SubProperty: string read FVar write SetSubProperty;
end;
TNewComponent = class(TComponent)
private
FExpandable: TExpandable;
procedure SetExpandable(Value: TExpandable);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Expandable: TExpandable read FExpandable write SetExpandable;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Misc',[TNewComponent]);
end;
{ TExpandable }
procedure TExpandable.Change;
begin
if Assigned(FOnChange) then FOnChange(Self);
end;
procedure TExpandable.SetSubProperty(const Value: string);
begin
if FVar <> Value then
begin
FVar := Value;
Change;
end;
end;
procedure TExpandable.Assign(Source: TPersistent);
begin
if Source is TExpandable then
SubProperty := TExpandable(Source).SubProperty
else
inherited Assign(Source);
end;
procedure TExpandable.AssignTo(Dest: TPersistent);
begin
if Dest is TExpandable then
TExpandable(Dest).SubProperty := SubProperty
else
inherited AssignTo(Dest);
end;
function TExpandable.IsEqual(Expandable: TExpandable): boolean;
begin
Result := (FVar = Expandable.SubProperty);
end;
{ TNewComponent }
constructor TNewComponent.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FExpandable := TExpandable.Create;
end;
destructor TNewComponent.Destroy;
begin
FExpandable.Free;
inherited Destroy;
end;
procedure TNewComponent.SetExpandable(Value: TExpandable);
begin
FExpandable.Assign(Value);
end;
end.