【发布时间】:2016-11-12 13:57:55
【问题描述】:
我一直试图归结为一个 MCVE,一些代码是另一个 q 的作者发给我的 说明自定义组件的问题。
该组件只是一个包含嵌入式 TDBGrid 的 TPanel 后代。 我的源代码版本和测试项目如下。
问题在于,如果嵌入式 DBGrid 是使用持久列创建的, 在 IDE 中重新打开其测试项目时,会引发异常
读取
TColumn.Grid.Expanded时出错。属性Grid不存在。
执行测试项目的Stream方法可以看出这个问题是怎么出现的:
为了比较,我的表单上还有一个普通的 TDBGrid,DBGrid1。而此 DBGrid1 的列流式传输为
Columns = <
item
Expanded = False
FieldName = 'ID'
Visible = True
end
[...]
嵌入式网格的列像这样流式传输
Grid.Columns = <
item
Grid.Expanded = False
Grid.FieldName = 'ID'
Grid.Visible = True
end
[...]
显然是Grid.Expanded 的Grid 前缀和其他列属性导致了问题。
我认为问题与 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.
【问题讨论】:
-
只是想知道:为什么将嵌入式网格的父级设置为底层 MyPanel 的父级?在 TMyPanel.Create 期间,网格是 MyPanel 的父级。不应该一直这样吗?
-
@UweRaabe:感谢您的关注。 “在 TMyPanel.Create 期间,网格是 MyPanel 的父级。”不确定我是否遵循,从 TMyPanel.Create 退出时,FGrid 的父级为 Nil(无论如何在 D7 中),因此如果没有 TMyPanel.SetParent 中的分配,则 Grid 是不可见的。
-
@MartynA:您是否打算让
TDBGrid始终成为您小组的子项?如果是这样,那么 Panel 的构造函数应该有一个FGrid.Parent := Self;语句,并且应该完全删除SetParent()覆盖。 -
谢谢@RemyLebeau。我已经按照您的建议在我的代码和我的 q 中更新了 TMyPanel 构造函数。不幸的是,流式持久化 TColumns 的问题仍然存在。
-
我真的很好奇您是否未能使用我的答案中的代码正确地流式传输子组件的集合属性。你能评论一下吗?