Ide Indy Delphi

Title: Creating a TRect published property that can be changed in the Object Inspector
Question: The object inspector has an inbuilt ability to edit any TPersistent based object. It uses RTTI to inspect the published properties of the TPersistent descendant and displays them as an expandable property. This is how the constraints property is implemented for example. The following unit provides an implementation of an editable TRect. I use this in controls for specifing margins for example.
Answer:
unit PersistentRect;

interface

uses
Classes, SysUtils, types;

type
TPersistentRect = class(TPersistent)
private
FRect : TRect;
FOnChange: TNotifyEvent;
function GetRect: TRect;
procedure SetRect(const Value: TRect);
procedure SetRectBottom(const Value: integer);
procedure SetRectLeft(const Value: integer);
procedure SetRectRight(const Value: integer);
procedure SetRectTop(const Value: integer);
protected
procedure AssignTo(Dest: TPersistent); override;
public
property AsRect : TRect read GetRect Write SetRect;
constructor create; virtual;
published
property Left : integer read FRect.Left write SetRectLeft;
property Top : integer read FRect.Top write SetRectTop;
property Right : integer read FRect.Right write SetRectRight;
property Bottom : integer read FRect.Bottom write SetRectBottom;
property OnChange : TNotifyEvent read FOnChange write FOnChange;
end;

implementation

{ TPersistentRect }

procedure TPersistentRect.AssignTo(Dest: TPersistent);
begin
if Dest is TPersistentRect then
with TPersistentRect(Dest) do
begin
AsRect := Self.AsRect;
end
else inherited AssignTo(Dest);
end;

constructor TPersistentRect.create;
begin
inherited;
FOnChange := nil;
end;

function TPersistentRect.GetRect: TRect;
begin
result := FRect;
end;

procedure TPersistentRect.SetRect(const Value: TRect);
begin
FRect.Left := value.left;
FRect.top := value.top;
FRect.right := value.right;
FRect.bottom := value.bottom;
if assigned(FOnChange) then FOnChange(self);
end;

procedure TPersistentRect.SetRectBottom(const Value: integer);
begin
FRect.Bottom := Value;
if assigned(FOnChange) then FOnChange(self);
end;

procedure TPersistentRect.SetRectLeft(const Value: integer);
begin
FRect.Left := Value;
if assigned(FOnChange) then FOnChange(self);
end;

procedure TPersistentRect.SetRectRight(const Value: integer);
begin
FRect.Right := Value;
if assigned(FOnChange) then FOnChange(self);
end;

procedure TPersistentRect.SetRectTop(const Value: integer);
begin
FRect.Top := Value;
if assigned(FOnChange) then FOnChange(self);
end;

end.
Example To create a panel component with controlable margins........
TPanelWithMargins = class(TCustomPanel)
private
{ Private declarations }
FMargins: TPersistentRect;
protected
{ Protected declarations }
procedure MarginsChanged(Sender : TObject);
procedure AdjustClientRect(var Rect: TRect); override;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
{ Published declarations }
property Margins : TPersistentRect read FMargins write FMargins;
end;

procedure TPanelWithMargins.AdjustClientRect(var Rect: TRect);
begin
inherited;
Rect.Left := rect.Left + FMargins.Left;
Rect.Top := rect.top + FMargins.Top;
Rect.Right := rect.Right - FMargins.Right;
Rect.Bottom := rect.Bottom - FMargins.Bottom;
end;

constructor TPanelWithMargins.Create(AOwner: TComponent);
begin
inherited;
FMargins := TPersistentRect.create;
FMargins.OnChange := MarginsChanged;
FMargins.AsRect := Rect(2,2,2,2);
end;

destructor TPanelWithMargins.Destroy;
begin
FreeAndNil(FMargins);
inherited;
end;

procedure TPanelWithMargins.MarginsChanged(Sender: TObject);
begin
Realign;
end;