How to properly pass the TCollection property of a subcomponent, for example. Columns property of the built-in TDBGrid

I tried to reduce to MCVE the code that the author of another q sent me to illustrate the problem with the custom component.

The component is simply a descendant of TPanel, which includes an embedded TDBGrid. My version of its source and test project are below.

The problem is that if the built-in DBGrid was created with constant columns, when its test project reopens in the IDE, an exception is thrown

Read error TColumn.Grid.Expanded. The property Griddoes not exist.

Execution of the Streamtest project method shows how this problem occurs:

For comparison, I also have a regular TDBGrid, DBGrid1, in my form. While the columns of this DBGrid1 are passed as

Columns = <
  item
    Expanded = False
    FieldName = 'ID'
    Visible = True
  end
[...]

Grid.Columns = <
  item
    Grid.Expanded = False
    Grid.FieldName = 'ID'
    Grid.Visible = True
  end
[...]

, , Grid Grid.Expanded , .

, , DBGridColumns TCollection DFM.

: TMyPanel , ?

:

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.

:

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.
+4
2

, . WriteCollectionProp ( TWriter.WriteProperties), , FPropPath WriteCollection.

TDBGrid TCustomDBGrid , stored false, DefineProperties, TCustomDBGrid.WriteColumns .

, , WriteCollection, FPropPath . , FPropPath .

, , , , , FPropPath .

Delphi 10.1 Berlin , Delphi 7, QP .

+2

, , , . "Grid", , , , . .

, SetSubComponent,

constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FGrid := TDBGrid.Create(Self);
//  FGrid.SetSubcomponent(True);
  FGrid.Parent := Self;
end;

csSubComponent, .

GetChildren, . GetChildren, documented, , () . (), , .

type
  TMyPanel = class(TPanel)
  private
    FGrid : TDBGrid;
  public
    constructor Create(AOwner : TComponent); override;
    procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
  published
    property Grid : TDBGrid read FGrid;
  end;

...

procedure TMyPanel.GetChildren(Proc: TGetChildProc; Root: TComponent);
begin
  inherited GetChildren(Proc, Self);
end;


. , , . . , , . .

, , , , , , , .

, . . : ( ), GetChildOwner . , TMyPanel2 (TMyPanel ...).

unit TestPanel2;

interface

uses
  Windows, SysUtils, Classes, Controls, ExtCtrls, DBGrids;

type
  TMyPanel2 = class(TPanel)
  private
    FGrid : TDBGrid;
  protected
    function GetChildOwner: TComponent; override;
  public
    constructor Create(AOwner : TComponent); override;
    destructor Destroy; override;
    procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
  published
    property Grid : TDBGrid read FGrid write FGrid;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Test', [TMyPanel2]);
end;

constructor TMyPanel2.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  if not (csReading in AOwner.ComponentState) then begin
    FGrid := TDBGrid.Create(Self);
    FGrid.Name := 'InternalDBGrid';
    FGrid.Parent := Self;
  end else
    RegisterClass(TDBGrid);
end;

destructor TMyPanel2.Destroy;
begin
  FGrid.Free;
  inherited;
end;

function TMyPanel2.GetChildOwner: TComponent;
begin
  Result := Self;
end;

procedure TMyPanel2.GetChildren(Proc: TGetChildProc; Root: TComponent);
begin
  Proc(Grid);
end;

end.
+1

All Articles