Delphi流面板到文件

Delphi流面板到文件,delphi,stream,Delphi,Stream,今天我有一个关于将表单的一部分流式传输到文件的问题。 在本例中,我使用Tmemo而不是文件来查看流 这是我的表格: 表单右上方的面板有一些控件,如标签、编辑等。 使用“保存面板”按钮,我将面板保存在TStream上: 代码如下: procedure TfrmMain.btnSaveClick(Sender: TObject); var idx: Integer; MemStr: TStream; begin MemStr := TMemoryStream.Create; Pan

今天我有一个关于将表单的一部分流式传输到文件的问题。 在本例中,我使用Tmemo而不是文件来查看流

这是我的表格:

表单右上方的面板有一些控件,如标签、编辑等。 使用“保存面板”按钮,我将面板保存在TStream上:

代码如下:

procedure TfrmMain.btnSaveClick(Sender: TObject);
var
  idx: Integer;
  MemStr: TStream;
begin
  MemStr := TMemoryStream.Create;
  PanelStr := TMemoryStream.Create;
  try
    for idx := 0 to pnlSource.ControlCount - 1 do begin
      MemStr.Position := 0;
      MemStr.WriteComponent(pnlSource.Controls[idx]);
      StreamConvert(MemStr);
    end;
    PanelStr.Position := 0;
    mmoStream.Lines.LoadFromStream(PanelStr);
  finally
    MemStr.Free;
  end;
end;
这里是StreamConvert:

{ Conversione stream in formato testo }
procedure TfrmMain.StreamConvert(aStream: TStream);
var
  ConvStream: TStream;
begin
  aStream.Position := 0;
  ConvStream := TMemoryStream.Create;
  try
    ObjectBinaryToText(aStream, ConvStream);
    ConvStream.Position := 0;
    PanelStr.CopyFrom(ConvStream, ConvStream.Size);
    lblStreamSize.Caption := IntToStr(ConvStream.Size);
  finally
    ConvStream.Free;
  end;
end;
PanelStr是在表单的私有部分声明的TStream对象,并在表单创建期间创建。 这一部分工作正常,正如您在图像右侧看到的,表单上的元素注册正确

现在我的问题是将这个元素恢复到表单左下角的面板中。 我尝试过这样的惯例:

{ Carica i controlli presenti nel pannello pnlSource in uno stream }
procedure TfrmMain.btnLoadClick(Sender: TObject);
var
  idx:  Integer;
  MemStr: TStream;
begin
  pnlSource.Free;
  MemStr := TMemoryStream.Create;
  try
    PanelStr.Position := 0;
    ObjectTextToBinary(PanelStr, MemStr);
    MemStr.Position := 0;
    MemStr.ReadComponent(pnlTarget);
  finally
    MemStr.Free;
  end;
end;
但它不起作用,在下图中,您可以看到结果:

我的例程有什么问题,我如何读取流中的所有元素,而不仅仅是第一个元素


有人能帮我解决这个难题吗?

您当前运行的代码有效地将源面板转换为标签。这是因为流化的第一个对象是标签,代码只读取一个组件。这样,当读卡器到达第一个
端时,由于流中没有子控件,读取就完成了

因此,首先,您必须编写面板-并且只编写面板。该小组是一个应该流它的孩子。要让它这么做,它必须拥有自己的控件

var
  idx: Integer;
  MemStr: TStream;
begin
  MemStr := TMemoryStream.Create;
  PanelStr := TMemoryStream.Create;
  try
    // transfer ownership of controls to the panel
    for idx := 0 to pnlSource.ControlCount - 1 do
      pnlSource.InsertComponent(pnlSource.Controls[idx]);
    // write the panel
    MemStr.WriteComponent(pnlSource);

    StreamConvert(MemStr);
    PanelStr.Position := 0;
    mmoStream.Lines.LoadFromStream(PanelStr);
  finally
    MemStr.Free;
  end;
这将生成一个输出到备忘录,如下所示:

object pnlSource: TPanel
  Left = 8
  Top = 8
  Width = 201
  Height = 265
  Caption = 'pnlSource'
  TabOrder = 0
  object Label1: TLabel
    Left = 48
    Top = 208
    Width = 31
    Height = 13
    Caption = 'Label1'
  end
  object Label2: TLabel
    ...
procedure TfrmMain.btnLoadClick(Sender: TObject);
var
  iTemp, iTemp2 : TStringList;
  MemStr: TStream;
  i: Integer;
begin
  // first read the destination panel an put it into a string list
  pnlSource.Free;
  iTemp := TStringList.Create;
  iTemp2 := TStringList.Create;
  iTemp.Duplicates := TDuplicates.dupAccept;
  iTemp2.Duplicates := TDuplicates.dupAccept;
  MemStr := TMemoryStream.Create;
  try
    PanelStr.Position := 0;
    iTemp2.LoadFromStream( PanelStr ); // our original source
    PanelStr.Size := 0;
    MemStr.Position := 0;
    MemStr.WriteComponent(pnlTarget);
    StreamConvert(MemStr);
    // PanelStr now has our destination poanel.
    PanelStr.Position := 0;
    iTemp.LoadFromStream( PanelStr );
    for i := 0 to iTemp2.Count - 1 do
    begin
      iTemp.Insert( ITemp.Count - 1, iTemp2[ i ]);
    end;
    PanelStr.Size := 0;
    iTemp.SaveToStream( PanelStr );
    PanelStr.Position := 0;
    mmoStream.Lines.LoadFromStream(PanelStr);
    MemStr.Size := 0;
    PanelStr.Position := 0;
    ObjectTextToBinary( PanelStr, MemStr);
    MemStr.Position := 0;
    RegisterClass( TLabel );
    RegisterClass( TPanel );
    RegisterClass( TEdit );
    RegisterClass( TCheckBox );
    RegisterClass( TRadioButton );
    MemStr.ReadComponent( pnlTarget );

  finally
    iTemp.Free;
    iTemp2.Free;
    MemStr.Free;
  end;
end;
注意标签定义的缩进和所属面板缺少的“end”(在末尾)

您需要注册拖缆的类,以便在加载时能够找到它们:

var
  idx:  Integer;
  MemStr: TStream;
begin
  pnlSource.Free;

  RegisterClasses([TLabel, TEdit, TCheckBox, TRadioButton]);

  MemStr := TMemoryStream.Create;
  try
    PanelStr.Position := 0;
    ObjectTextToBinary(PanelStr, MemStr);
    MemStr.Position := 0;
    MemStr.ReadComponent(pnlTarget);
  finally
    MemStr.Free;
  end;
注册当然可以移动到其他地方,比如表单创建或单元初始化


如果需要,您还可以将控件的所有权转移回表单,如保存代码中所示。

正如我在注释中所说,您需要用Panel2信息来包围数据。您还需要注册正在保存和恢复的每个控件类型

这意味着只需更改加载过程-如下所示:

object pnlSource: TPanel
  Left = 8
  Top = 8
  Width = 201
  Height = 265
  Caption = 'pnlSource'
  TabOrder = 0
  object Label1: TLabel
    Left = 48
    Top = 208
    Width = 31
    Height = 13
    Caption = 'Label1'
  end
  object Label2: TLabel
    ...
procedure TfrmMain.btnLoadClick(Sender: TObject);
var
  iTemp, iTemp2 : TStringList;
  MemStr: TStream;
  i: Integer;
begin
  // first read the destination panel an put it into a string list
  pnlSource.Free;
  iTemp := TStringList.Create;
  iTemp2 := TStringList.Create;
  iTemp.Duplicates := TDuplicates.dupAccept;
  iTemp2.Duplicates := TDuplicates.dupAccept;
  MemStr := TMemoryStream.Create;
  try
    PanelStr.Position := 0;
    iTemp2.LoadFromStream( PanelStr ); // our original source
    PanelStr.Size := 0;
    MemStr.Position := 0;
    MemStr.WriteComponent(pnlTarget);
    StreamConvert(MemStr);
    // PanelStr now has our destination poanel.
    PanelStr.Position := 0;
    iTemp.LoadFromStream( PanelStr );
    for i := 0 to iTemp2.Count - 1 do
    begin
      iTemp.Insert( ITemp.Count - 1, iTemp2[ i ]);
    end;
    PanelStr.Size := 0;
    iTemp.SaveToStream( PanelStr );
    PanelStr.Position := 0;
    mmoStream.Lines.LoadFromStream(PanelStr);
    MemStr.Size := 0;
    PanelStr.Position := 0;
    ObjectTextToBinary( PanelStr, MemStr);
    MemStr.Position := 0;
    RegisterClass( TLabel );
    RegisterClass( TPanel );
    RegisterClass( TEdit );
    RegisterClass( TCheckBox );
    RegisterClass( TRadioButton );
    MemStr.ReadComponent( pnlTarget );

  finally
    iTemp.Free;
    iTemp2.Free;
    MemStr.Free;
  end;
end;
如前一个答案中所述,注册可以放在其他地方

与前面的答案不同,您不需要首先更改控件的所有权。(这只是一个评论,不是批评)。这只是我评论的一个实现


我的命名约定与你的不同。我尝试使用相同的名称,但如果我遗漏了任何名称,请原谅。

据我所知,您的列表是原始面板中的组件列表,但您正在尝试读取目标面板的属性。属性不存在,因为源中未定义目标面板。您需要做的是读取目标面板的属性,并将它们作为您读取的嵌入式组件属性的包装,即,如果有意义,将右面板中显示的数据放在您从目标面板读取的内容的结束语句之前。命名很好,我只能用粘贴来运行它:)。无论如何,我认为值得注意的是,您正在以不同的方式在不同的阶段转移所有权:手动修改流。所以,并不是控件的所有权可以保留在表单中,而是您不必首先修改它。和你一样,这不是批评,只是想注意一下。很好!只有一个问题:在pnlSource加载到pnlTarget之后,最后一个成为pnlSource;是吗?@Eros使用Sertacs解决方案是(除非您先重命名它)。使用我的解决方案,不,它保持为pnlTarget。好的,DSM,我已经尝试了Sertac的解决方案,现在我尝试你的。只是为了澄清你的第一行。它不会将TPanel更改为TLabel。它仍然是一个TPanel。但是所有ReadComponent在原始代码中都读取第一个对象(TLabel),忽略它是什么类型(文档中说明了这一点),并尝试将Label1的属性应用于Panel2。碰巧TLabel和TPanel具有相同的已发布属性,这就是为什么原始代码没有引发异常,面板移动到左上角,如图所示,看起来有点像原始Label1。