数据表不定,数据表字段数目不定,字段长度不定;怎样动态地生成一个报表,把所有的字段都列出

解决方案 »

  1.   

    下面结合一个具体实例来说明如何其实现的方法和技术。
    1、 基本思路
        数据来源,我们以DELPHI自带的DBDEMOS中的employee.db表为例,它共有6                          个字段。
        在F_main主窗体中(如图一),可以自由选择所需要打印的字段。它的主要控件及属性设置如下:
        ①.Table1:Databasename设置为DEDEMOS,Tablename设置为employee.db
        ②.Listbox1:显示所连数据库表中的全部字段
        ③.Listbox2:用于选择所需报表输出的字段
        ④.AddBitBtn:用于把所选择的字段名添加到Listbox2中
        ⑤.DeleteBitBtn:用于把Listbox2中的字段名去掉
        ⑥.PreviewBitBtn:用于报表的预览
    ⑦.PrintBitBtn:用于报表的输出
    ⑧.CloseBitBtn:用于关闭应用程序
        在F_report窗体中,放置了以下主要控件,并设置属性,以减少程序的篇幅:
        ①.Table1:Databasename设置为DEDEMOS,Tablename设置为employee.db
    ②.QuickRep1:papesize属性为A4,dataset属性为Table1,bands属性中的hascolumnheader、hasdetail、hastitle设置为True                              图一    显示数据库表中的全部字段
        在F_report的Oncreate事件中加入了如下代码: 
     Table1.Open;
     if Table1.Active then
     Table1.GetFieldNames(Listbox1.Items);// 获得数据库表中的全部字段名
     DeleteBitBtn.Enabled:=False;  //在Listbox2中无字段时,DeleteBitBtn变灰2、 从Listbox1中选择字段添加到Listbox2中
        为AddBitBtn的Onclick事件加入如下代码:
    if listbox1.Items.Count=0 then exit;    //如Listbox1中无可供选择的字段,                                           
                                              则执行空操作
    if listbox1.Selected[listbox1.ItemIndex] then  //在Listbox1中选择字段
      begin
    Listbox2.Items.Add(Listbox1.Items[Listbox1.ItemIndex]);//往Listbox2
                                                        中增加选中的字段
        Listbox1.Items.Delete(Listbox1.ItemIndex);//从Listbox1中删除此字段
        if Listbox2.Items.Count>=1 then  //在Listbox2中有字段才允许执行删
        DeleteBitBtn.Enabled:=True;
      end;3、 从Listbox2中删除不需要的字段
        为DeleteBitBtn的Oncreate事件添加如下代码:
      if Listbox2.Items.Count=0 then exit;   // 如果Listbox2中无字段,则执行空操作
      if listbox2.Selected[Listbox2.ItemIndex] then           //在Listbox2中选择字段
      begin
        Listbox1.Items.Add(Listbox2.items[Listbox2.itemindex]);   //添加到Listbox1中
        Listbox2.Items.Delete(Listbox2.itemindex);         //从Listbox2中删除此字段
      end;  if Listbox2.Items.Count=0 then   //如果Listbox2中无字段,则DeleteBitBtn变灰
      DeleteBitBtn.Enabled:=False;
      

  2.   

    4、在报表中动态添加一列的步骤:
    ①. TitleBand1中打印的是报表的名称,这里假设为:动态报表生成示例。可以动态
    创建TQrlabel控件,把它的Parent属性置为F_report.TitleBand1,使其成为TQrlabel控件的容器控件。
    ②. ColumnHeaderBand1中需要打印的是报表的列名。为了使报表的格式更加整齐,我
    们同时动态创建TQrlabel控件和TQRshape控件,把F_report.ColumnHeaderBand1设为它们的容器控件。把Listbox2中选择的字段名,赋给Tqrlabel.caption,从而显示列名。
    ③. 在DetailBand1中创建TQRDbText控件与TQRshape控件,把它的Parent属性指向
    F_report.DetailBand1,与在ColumnHeaderBand1中创建列名相类似。并使TQRDbText控件的dataset属性指向相应的Ttable或Tquery控件,dataField属性指向对应的字段。
    ④. 在预览前根据选择字段,判断报表的总宽度是否超出宽度,以及报表的打印方向是
    横向还是纵向。如总宽度超出报表所限的最大值,则提示警告信息,并强制进行调整。
    ⑤.另外需要考虑的问题是如何确定列的宽度,以及各列之间的相对位置。通过 
    Columnswidth过程,确定所选择的字段中最大的字段长度maxwidth。各列的打印宽度可以通过公式(该列宽度=字段最大长度*给定字体下每字节所占的宽度+两边所留空隙)。  widthperbyte:=10;                                   // 每个字节对应的像数
      columnswidth;                                     //计算最大列宽与总宽度
      disposecontrols;                                    //释放动态创建的控件  if totalwidth*widthperbyte>1123 then          //判断总宽度是否超出最大值
        begin
          Application.MessageBox('报表超宽,请调整再输出!','警告',1);
          exit;                                  //提示警告信息,并强制进行调整
        end
      else if totalwidth*widthperbyte>794 then
        F_report.QuickRep1.Page.Orientation:=polandscape               //横向
      else
        F_report.QuickRep1.Page.Orientation:=poPortrait;                //纵向    Heading:=TQRlabel.Create(self);                    //创建TQRlabel控件
        Heading.parent:=F_report.TitleBand1;                  //设置其容器控件
        Heading.Caption:='动态报表生成示例';                        //报表标题
        Heading.Font.Size:=16;
        Heading.Font.Style:=[fsbold];                                //粗体字
        Heading.Alignment:=tacenter;
        Heading.Width:=Length('动态报表生成示例')*(widthperbyte+4);//标题的宽度
        Heading.Left:=(F_report.QuickRep1.Width-Heading.width)div 2;
        Heading.Height:=F_report.TitleBand1.Height-1;
        Heading.Top:=0;    Leftx:=(F_report.quickrep1.width-totalwidth*widthperbyte)div 2;
        F_report.QuickRep1.Font.Size:=12;
        for i:=0 to Listbox2.items.count-1 do  //根据所选择字段的数目来动态创建
        begin
          QRShape1:=TQRSHape.Create(self);    //创建TQRSHape控件打印列名的格线
          QRShape1.parent:=F_report.ColumnHeaderBand1;           //设置容器控件
          QRShape1.Left:=Leftx;                           //与列名的格线相对齐
          QRShape1.Width:=maxwidth*widthperbyte+4;       // TQRSHape控件的宽度
          QRShape1.Height:=F_report.ColumnHeaderBand1.Height;
          QRShape1.top:=0;      QRLabel:=TQRLabel.Create(self);                  //创建TQRLabel控件
          QRLabel.parent:=F_report.ColumnHeaderBand1;            //设置容器控件
          QRLabel.Font.Style:=[fsbold];               //设置列名的字体为粗体字
          QRLabel.Left:=Leftx+2;                              //左边空2个像数
          QRLabel.width:=maxwidth*widthperbyte;              //列名的打印宽度
          QRLabel.height:=F_report.ColumnHeaderBand1.Height-2;    //空2个像数
          QRLabel.top:=1;
          QRLabel.caption:=Listbox2.Items.Strings[i];      //显示选择的字段名称      QRShape2:=TQRSHAPE.Create(self);                 //创建TQRSHAPE控件
          QRShape2.Parent:=F_report.DetailBand1;                //设置容器控件
          QRShape2.Left:=Leftx;                
          QRShape2.Width:=maxwidth*widthperbyte+4;            //确定格线的宽度
          QRShape2.Height:=F_report.DetailBand1.Height;
          QRShape2.top:=0;      QRDBText:=TQRDBText.Create(self);              //创建TQRDBText控件
          QRDBText.parent:=F_report.DetailBand1;                //设置容器控件
          QRDBText.Left:=Leftx+2;                            //左边空2个像数
          QRDBText.Width:=maxwidth*widthperbyte;          //设置QRDBText的宽度
          QRDBText.Height:=F_report.DetailBand1.Height-2;        //空2个像数
          QRDBText.Top:=1;
          QRDBText.DataSet:=F_report.Table1;                  //连接数据表控件
          QRDBText.DataField:=Listbox2.Items.Strings[i];      //连接选择的字段      Leftx:=Leftx+maxwidth*widthperbyte+4;  //设置下一列的起始位置
        end;  F_report.Table1.Active:=true;
      F_report.QuickRep1.Preview;                                //报表的预览5.为计算报表的总宽度totalwidth与最大列宽maxwidth,定义了过程columnswidth。
    各字段的长度可以用Tfield的DataSize属性得到。字段名的长度根据Length函数来获得。为了整个打印表格整齐划一,通过比较字段名与字段长度来确定最大列宽maxwidth。
    maxwidth:=0;
    for i:=0 to F_main.Listbox2.items.count-1 do
      begin
        if  F_main.Table1.Fields.Fieldbyname(F_main.Listbox2.items.strings[i]).datasize
         >maxwidth then
        maxwidth:=F_main.Table1.Fields.Fieldbyname(F_main.Listbox2.items.strings[i]).datasize;                                              //确定字段中数据的最大长度
        if Length(F_main.Listbox2.items.strings[i])>maxwidth then
        maxwidth:=Length(F_main.Listbox2.items.strings[i]);     //确定字段名中最大长度
      end;  totalwidth:=0;                                                    
      for i:=0 to F_main.Listbox2.items.count-1 do
      totalwidth:=totalwidth+maxwidth+4;                                 //报表总宽
      

  3.   

    6.在动态创建完控件后,我们通过过程disposecontrols来释放其所占的资源。由于用
    户可能多次的点击预览键,因此我们必须在每次预览事件发生之前,释放上次动态创建的控件。
    for i:=0 to F_report.TitleBand1.ControlCount-1 DO            //取消系统对控件的控制
    F_report.TitleBand1.RemoveControl(F_report.TitleBand1.Controls[0]);for i:=1 to F_report.ColumnHeaderBand1.ControlCount DO    //取消系统对控件的控制
    F_report.ColumnHeaderBand1.RemoveControl(F_report.ColumnHeaderband1.Controls[0]);for i:=1 to F_report.detailband1.controlcount DO             //取消系统对控件的控制
      F_report.detailband1.removecontrol(F_report.detailband1.Controls[0]);  F_report.Table1.active:=false;
    三、注意事项
    1. 程序员可以根据用户的实际需求,设制表格线或者取消表格线。也可以不计算最大
    宽度,而根据各列实际宽度打印,则无须定义columnswidth过程。各列宽度的计算公式则改为(该列宽度=该字段长度*给定字体下每字节所占的宽度+两边所留空隙)。
    2. 在添加Preview的OnClick事件前必须先添加TQRLabel、TQRShape、TQRDBText  
    控件的系统标准引用单元QRCtrls。
    3. 在设置纸的打印方向时,必须引用Printers。
    4. 动态生成组件的宽度计算必须放在定义其字体属性完成后进行。
    5. 如果要修改QRDBText控件的数据位置,必须先设置其AutoSize属性为false,然
    后才能设置其Alignment属性为所需的左对齐、居中或者右对齐。这一点很容易被忽略。
    四、结束语
    以上程序在DELPHI中调试通过。
       上述示例只是介绍了报表动态生成的核心部分,由于在不同的实际情况下,用户对报表输出的格式会有所不同,因此需要根据具体情况,更加灵活的运用报表类控件,对上述示例程序进行修改和添加,以满足不同的要求。比如:可以动态的创建数据库表,再通过数据访问组件来获取所需数据。也可以动态创建TQRExpr控件来实现动态报表的计算功能等等。参考文献:
    1、DELPHI4.0数据库与INTERNET开发指南    潘将一    清华大学出版社  1999.9
    2、DELPHI3.0编程参考手册    (美)P.Thurrott,G.Brent,R.Bagdazian,S.Tendon著
                                  卢庆龄  蒋全  等译    清华大学出版社  1998.8