我做了一个MDI的程序,Form1为主窗体,Form2、Form3为子窗体,我想在Form2中名字为“保存”的菜单项中写代码,把Form2和里面的控件保存起来,存放在硬盘上;在名字为“打开”的菜单项中写代码,把上面保存的Form2的界面打开。
    现在的问题是:程序运行后,点“保存”时可以通过,没问题; 点“打开”却出现错误:List index out of bounds(11).
    这是怎么回事呢?(为了便于调试程序,窗体Form2上就有一个DBGrid可视控件)
“打开”菜单项中的代码是:
procedure TForm2.N3Click(Sender: TObject);  //打开
var
  BinStream:TmemoryStream;
  i:integer;
begin
  BinStream := TmemoryStream.Create;
  if opendialog1.Execute then
  try
  for i:=0 to form2.ComponentCount-1 do
   BinStream.LoadFromFile(pchar(opendialog1.FileName));
   BinStream.Seek(0, soFrombeginning);
   binstream.ReadComponent(form2.Components[i]);
  finally
   BinStream.Free
  end;
end;

解决方案 »

  1.   

    budded(System is bussy!)兄:
          请问,那,应该怎样写呀?
      

  2.   

    http://dev.csdn.net/develop/article/46/article/46/article/46/article/41/41670.shtm
      

  3.   

    binstream.ReadComponent(form2.Components[i]);这个时候Form2上应该还没有任何控件的,当然会报index out of bound
      

  4.   

    tonylk(=www.tonixsoft.com=) 兄:
         您写的那篇《分析DFM文件生成程序界面》的文章对小弟来说太深奥了,我知识浅薄,不一定能真正看懂。还是直接请教您吧!
        “这个时候Form2上应该还没有任何控件的,当然会报index out of bound”  
    为此,我在binstream.ReadComponent(form2.Components[i]);前面加上 
      for i:=0 to form2.ComponentCount-1 do
       form2.Components[i].Free;
    后,提示:List index out of bounds(4).
    我去掉ADOTable和 DataSource后,提示List index out of bounds(3).
    接着我有去掉DBGrid,提示:List index out of bounds(2).
    剩下的不能在去掉了,就剩MainMenu1、SaveDialog1、OpenDialog1了。
    请问bounds后面的数字是什么意思?窗体上已经没有控件了。这个该怎样解决呢?
      

  5.   

    1.
     for i:=0 to form2.ComponentCount-1 do
       form2.Components[i].Free;
    这样的代码是不对的,假设form2上开始有2个控件,那么for循环第一次进入时,将Components[0]删除了,第二个控件的Index将会自动前移,变为0,而for循环第二次进入访问的则是Components[1],自然会Index out of bound了。
    正确的写法应该为:
    for I:=Form2.ComponentCount-1 downto 0 do begin
      Form2.Components[I].Free;
    end;
    2.看看Delphi中对于ReadComponent的说明:
    Initiates streaming of components and their properties.Delphi syntax:function ReadComponent(Instance: TComponent): TComponent;C++ syntax:TComponent* __fastcall ReadComponent(TComponent* Instance);DescriptionReadComponent is called indirectly by the global routine ReadComponentRes, by the ReadComponentRes method, or it can be called directly to initiate component streaming.ReadComponent reads data values from the stream and assigns them to Instance’s properties. It then constructs a reader object and calls the reader’s ReadRootComponent method to read Instance’s property values and construct child objects defined in the stream as children of the Instance. ReadComponent returns the component.If Instance is nil (Delphi) or NULL (C++), ReadComponent constructs a component based on the type information in the stream and returns the newly-constructed component.This example shows how to use the built-in component streaming support to convert any component into a string and convert that string back into a component.function ComponentToString(Component: TComponent): string;var
      BinStream:TMemoryStream;
      StrStream: TStringStream;
      s: string;
    begin
      BinStream := TMemoryStream.Create;
      try
        StrStream := TStringStream.Create(s);
        try
          BinStream.WriteComponent(Component);
          BinStream.Seek(0, soFromBeginning);
          ObjectBinaryToText(BinStream, StrStream);
          StrStream.Seek(0, soFromBeginning);
          Result:= StrStream.DataString;
        finally
          StrStream.Free;    end;
      finally
        BinStream.Free
      end;
    end;function StringToComponent(Value: string): TComponent;
    var
      StrStream:TStringStream;
      BinStream: TMemoryStream;
    begin
      StrStream := TStringStream.Create(Value);
      try
        BinStream := TMemoryStream.Create;
        try
          ObjectTextToBinary(StrStream, BinStream);
          BinStream.Seek(0, soFromBeginning);
          Result := BinStream.ReadComponent(nil);    finally
          BinStream.Free;
        end;
      finally
        StrStream.Free;
      end;
    end;如果传入参数为nil时,它会帮你自动创建一个对象,看上去你要的就是这种功能。
      

  6.   

    tonylk(=www.tonixsoft.com=) 兄:
          根据您的方法,我修改了程序:
    for  i:=form2.ComponentCount-1 downto 1 do
    //注意:这里不能遍历到0,到0将出错:List index out of bounds(-1).
    begin
    form2.Components[i].Free;
    end;
    这样,运行后点击“打开”菜单项,选择Form2的保存路径,窗体Form2可以打开了。但打开后又出现了下面的问题:
    1、Form2上面只显示一个控件。本程序是Lable1,别的控件都不显示。如果让Form2只剩Lable1控件,则运行后Lable1的Caption属性值将显示在Form2的标题栏的位置;
    2、Form2打开后,点击它上面的菜单项,都没有反应;
    3、如果再增加一个“退出”菜单项,则该菜单项的名字出现在Lable1的位置。
    我想要的结果是:保存时是什么样子,打开时也是什么样子。
    调试了大半天了,也没弄出来。还想请您帮帮忙!我修改后的代码是:
    var
      BinStream:TmemoryStream;
      i:integer;
    begin
      BinStream := TmemoryStream.Create;
      if opendialog1.Execute then
      try   BinStream.LoadFromFile(pchar(opendialog1.FileName));
       BinStream.Seek(0, soFrombeginning);
       i:=form2.ComponentCount-1;
       for  i:=form2.ComponentCount-1 downto 1 do
       begin
       form2.Components[i].Free;
       end;
       binstream.ReadComponent(form2.Components[i]);
      finally
       BinStream.Free
      end;
    end;
      

  7.   

    我已经做一天了,在网上也搜索这么长时间了,也没弄出上面想要的结果。怎么只能读出一个组件呀?别的为什么读不出来呀?为什么读出来的按钮都不能用啊?从文件里读组件的时候,容器里的组件都已经释放了呀,怎么还不行呀?
        tonylk(=www.tonixsoft.com=) 兄在吗?高人在吗?哪位仁兄能帮帮我。我自己真的是做不出来了!!!   分,我可以再加呀。
      

  8.   

    procedure TForm1.BitBtn1Click(Sender: TObject);
    var
    i:integer;
    begin
    for I:=self.ComponentCount-1 downto 0 do begin
      self.Components[I].Free;
    ReadComponentResFile('Temp.dfm', Form1);
    end;end;procedure TForm1.BitBtn3Click(Sender: TObject);
    begin
    WriteComponentResFile('Temp.dfm', Form1);
    end;
      

  9.   

    是这样的,但连续几次载入后会出错,但出错后还能操作
    procedure TForm1.BitBtn1Click(Sender: TObject);
    var
    i:integer;
    begin
      for I:=self.ComponentCount-1 downto 0 do
           self.Components[I].Free;
      ReadComponentResFile('Temp.dfm', Form1);
    end;procedure TForm1.BitBtn3Click(Sender: TObject);
    begin
    WriteComponentResFile('Temp.dfm', Form1);
    end;