I've been trying to boil down to an MCVE some code the author of another q sent me
to illustrate a problem with a custom component.
The component is simply a TPanel descendant which includes an embedded TDBGrid.
My version of its source, and a test project are below.
The problem is that if the embedded DBGrid has been created with persistent columns,
when its test project is re-opened in the IDE, an exception is raised
Error reading TColumn.Grid.Expanded
. Property Grid
does not exist.
Executing the Stream
method of the test project shows how this problem arises:
For comparison purposes, I also have a normal TDBGrid, DBGrid1, on my form. Whereas the Columns of this DBGrid1 are streamed as
Columns = <
item
Expanded = False
FieldName = 'ID'
Visible = True
end
[...]
the embedded grid's columns are streamed like this
Grid.Columns = <
item
Grid.Expanded = False
Grid.FieldName = 'ID'
Grid.Visible = True
end
[...]
It's obviously the Grid
prefix of Grid.Expanded
and the other column properties which is causing the problem.
I imagine that the problem is something to do with the fact that DBGridColumns
is a TCollection descendant and that the embedded grid isn't the top-level object in
the DFM.
My question is: How should the code of TMyPanel
be modified so that the grid's
columns get correctly streamed?
Component source:
unit MAGridu;
interface
uses
Windows, SysUtils, Classes, Controls, ExtCtrls, DBGrids;
type
TMyPanel = class(TPanel)
private
FGrid : TDBGrid;
public
constructor Create(AOwner : TComponent); override;
published
property Grid : TDBGrid read FGrid;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Standard', [TMyPanel]);
end;
constructor TMyPanel.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FGrid := TDBGrid.Create(Self);
FGrid.SetSubcomponent(True);
FGrid.Parent := Self;
end;
end.
Test project source:
type
TForm1 = class(TForm)
DBGrid1: TDBGrid;
CDS1: TClientDataSet;
DataSource1: TDataSource;
MyPanel1: TMyPanel;
Memo1: TMemo;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
procedure Stream;
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
Stream;
end;
procedure TForm1.Stream;
// This method is included as an easy way of getting at the contents of the project's
// DFM. It saves the form to a stream, and loads it into a memo on the form.
var
SS : TStringStream;
MS : TMemoryStream;
Writer : TWriter;
begin
SS := TStringStream.Create('');
MS := TMemoryStream.Create;
Writer := TWriter.Create(MS, 4096);
try
Writer.Root := Self;
Writer.WriteSignature;
Writer.WriteComponent(Self);
Writer.FlushBuffer;
MS.Position := 0;
ObjectBinaryToText(MS, SS);
Memo1.Lines.Text := SS.DataString;
finally
Writer.Free;
MS.Free;
SS.Free;
end;
end;
end.
procedure TForm1.FormCreate(Sender: TObject);
var
Field : TField;
begin
Field := TIntegerField.Create(Self);
Field.FieldName := 'ID';
Field.FieldKind := fkData;
Field.DataSet := CDS1;
Field := TStringField.Create(Self);
Field.FieldName := 'Name';
Field.Size := 20;
Field.FieldKind := fkData;
Field.DataSet := CDS1;
CDS1.CreateDataSet;
CDS1.InsertRecord([1, 'One']);
end;
end.
See Question&Answers more detail:
os