I am having trouble reloading a saved compound component in Delphi where the subcomponent (a TChart) appears to be being created twice. When I create my component in code, it works fine.
My component is a TPanel hosting a client aligned TChart and I create the single series and its data myself. Here is my code:
type
TMyChart = class( TPanel )
PRIVATE
FChart : TChart;
PROTECTED
procedure Notification( AComponent : TComponent; Operation : TOperation ); override;
PUBLIC
constructor Create( AOwner : TComponent ); override;
destructor Destroy; override;
PUBLISHED
property Chart : TChart
read FChart;
end;
{ TMyChart }
constructor TMyChart.Create(AOwner: TComponent);
begin
inherited;
Width := 400;
Height := 150;
FChart := TChart.Create( Self );
FChart.Name := '';
FChart.SetSubComponent( True );
FChart.FreeNotification( Self );
FChart.Parent := Self;
FChart.Align := alClient;
FChart.AddSeries( TLineSeries.Create( FChart ));
FChart.Series[0].FillSampleValues(100);
end;
destructor TMyChart.Destroy;
begin
FreeAndNil( FChart );
inherited;
end;
procedure TMyChart.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (AComponent = FChart) and (Operation = opRemove) then
FChart := nil;
end;
initialization
RegisterClass( TMyChart );
RegisterClass( TChart );
RegisterClass( TLineSeries );
end.
I've followed the example in ExtCtrls 'TLabeledEdit' to include a FreeNotification for the subcomponent TChart.
I stream this out using TStream.WriteComponent and get the following DFM:
object Mychart1: TMyChart
Left = 0
Top = 0
Width = 400
Height = 150
Caption = 'Mychart1'
TabOrder = 3
Chart.Left = 1
Chart.Top = 1
Chart.Width = 398
Chart.Height = 148
Chart.Align = alClient
Chart.TabOrder = 0
Chart.ColorPaletteIndex = 13
object TChart
Left = 1
Top = 1
Width = 398
Height = 148
Align = alClient
TabOrder = 0
ColorPaletteIndex = 13
object TLineSeries
Marks.Arrow.Visible = True
Marks.Callout.Brush.Color = clBlack
Marks.Callout.Arrow.Visible = True
Marks.ShapeStyle = fosRoundRectangle
Marks.Visible = False
Brush.BackColor = clDefault
Pointer.InflateMargins = True
Pointer.Style = psRectangle
Pointer.Visible = False
XValues.Name = 'X'
XValues.Order = loAscending
YValues.Name = 'Y'
YValues.Order = loNone
end
end
end
Everything I require is there. I reload this DFM using ReadComponent as follows:
procedure LoadComponentFromFile( var AComponent : TComponent );
var
MS : TMemoryStream;
SS : TStringStream;
begin
MS := TMemoryStream.Create;
SS := TStringStream.Create;
try
SS.LoadFromFile( GetSpecialFolderPath( CSIDL_DESKTOP ) + '\test.txt' );
SS.Position := 0;
ObjectTextToBinary( SS, MS );
MS.Position := 0;
AComponent := MS.ReadComponent( nil );
finally
MS.Free;
SS.Free;
end;
end;
procedure TForm8.Button3Click(Sender: TObject);
begin
FreeAndNil( FMyChart );
LoadComponentFromFile( TComponent(FMyChart) );
FMyChart.Parent := Self;
end;
After this I seem to get TWO TCharts created - one with the correct series data (constructed by TMyChart.Create) and another (in front of this) with empty axes, presumably where ReadComponent is constructing 'object TChart'. I have proven this by placing a breakpoint in 'TMyChart.Create' and another in the source code for TChart in 'TCustomChart.Create'. When calling the loader code it hits the TMyChart.Create breakpoint once, first, and then hits the TCustomChart.Create breakpoint twice. I can see that I need to tell TStream.ReadComponent not to recreate my subcomponent TChart but after many trials and reading of documentation on component streaming I cant see how to structure it. Can anyone comment please? Many thanks.