Hello.
I've been enjoying my time with Lazarus and I'm diving into the world of creating custom components.
I've come across a minor issue where the designer-set properties are not being serialized/saved into the LFM. Clearly I am missing an important step. I hope somebody could point out my mistake?
A positive testTo set the scene, here is a boiled-down component that works as expected. It paints it's own background using the default Color property. Installing this to the IDE and dropping it on a form, we see the Aqua square as expected (image A).
unit MyCustomComponent;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs;
type
{ TMyCustomComponent }
TMyCustomComponent = class(TCustomControl)
private
protected
public
constructor Create(AOwner: TComponent); override;
procedure Paint; override;
published
property Color;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('My Components',[TMyCustomComponent]);
end;
{ TMyCustomComponent }
constructor TMyCustomComponent.Create(AOwner:TComponent);
begin
inherited Create(AOwner);
SetInitialBounds(0, 0, 50, 50);
Self.Color := clAqua;
end;
procedure TMyCustomComponent.Paint;
var
Bitmap: TBitmap;
begin
Bitmap := TBitmap.Create;
try
Bitmap.Height := Height;
Bitmap.Width := Width;
Bitmap.Canvas.Pen.Color := clBlack;
Bitmap.Canvas.Brush.Color := Self.Color;
Bitmap.Canvas.Rectangle(0, 0, Width, Height);
Canvas.Draw(0, 0, Bitmap);
finally
Bitmap.Free;
end;
end;
end.
I want to style my component based on different states (Normal, Hover, Down, Disable). I create a class type that will provide the blueprint for this structure:
TSpamStyle = Class
FBackColor: TColor;
FBorderColor: TColor;
FTextColor: TColor;
published
property BackColor: TColor read FBackColor write FBackColor;
property BorderColor: TColor read FBorderColor write FBorderColor;
property TextColor: TColor read FTextColor write FTextColor;
end;
For demonstration purposes let's focus on NormalStyle only, I add private storage for the value, and publish it as a property:
TMyCustomComponent = class(TCustomControl)
private
{ new }
FNormalStyle:TSpamStyle;
protected
public
constructor Create(AOwner: TComponent); override;
procedure Paint; override;
{ new }
destructor Destroy; override;
published
property Color;
{ new }
property NormalStyle: TSpamStyle read FNormalStyle write FNormalStyle;
end;
I add a constructor and destructor to allocate and free the property instance:
constructor TMyCustomComponent.Create(AOwner:TComponent);
begin
inherited Create(AOwner);
SetInitialBounds(0, 0, 50, 50);
Self.Color := clAqua;
{ new }
Self.NormalStyle := TSpamStyle.Create;
Self.NormalStyle.BackColor := clFuchsia;
Self.NormalStyle.BorderColor := clBlack;
end;
And adjust my Paint method to use the style:
procedure TMyCustomComponent.Paint;
...
Bitmap.Canvas.Pen.Color := Self.NormalStyle.BorderColor;
Bitmap.Canvas.Brush.Color := Self.NormalStyle.BackColor;
Bitmap.Canvas.Rectangle(0, 0, Width, Height);
Compile and install into the IDE, form designer and runtime shows MyCustomComponent using the NormalStyle colors correctly (image B).
The problemIf I change the color of the NormalStyle property in the IDE designer, they are not saved into the LFM file, and at run-time my chosen colors are not used (image C).
I realize my component is missing some important information to tell the IDE how to serialize class TSpamStyle, but cannot find this information online.
What I triedI have read the wiki page
How_To_Write_Lazarus_Component and also
Extending_the_IDE.
I looked at the source of other components to try identify what I am missing, but nothing stood out.
I implemented the property write as methods, but this seemed not to make any difference.
My mistake is likely silly, but I'd like to learn
Edit: I attached the zipped example project to this post.