利用Delphi中的Formula One构件生成复杂的报表

【摘要】用Delphi开发管理应用程序时,可利用Formula One构件强大的电子制表、数据分析和数据处理能力,来弥补其报表功能的局限性、增加管理系统报表的灵活性。本文叙述Delphi中用Formula One构件方便地生成理想的复杂文字报表的方法。
关键词    Delphi    Formula One    复杂报表
Build Complicated Report-forms Using Formula-One Component of Delphi
YANG Xue-mei
(Changzhou Power Supply Company,Changzhou 213003,China)
Abstract: Using strong electric-table, data-analyzing and data processing function of Formula-One Component may make up limitation of report-forms function and flexibility of management system report-forms, while developing management application by Delphi. The article describes the method of perfect complicated character report-forms built by Formula-One Component of Delphi.
Key words: Delphi;Formula-One;complicated report-forms
1.引言
Delphi是从事电子商务、Internet应用和数据库编程的最佳工具,以稳定、优雅、快捷著称,但是它的报表输出功能不是很强,QuickReport是 Delphi的内建报表工具,它适合生成一定固定格式的报表,形成一般简单输出报表。
针对比较复杂的报表形式,我们需要选择一种有力的工具。这时,我们看到:Formula One是个集电子表格和可视化编程优点与一身的开发工具,它不仅有电子制表的能力,而且具有灵活的数据分析和数据处理能力;同时,Delphi让程序员很方便地将符合工业标准的ActiveX控件集成到应用程序中,在ActiveX页,F1Book就是报表工具Formula One集成在IDE中的构件。
当然,还有其他方法可以生成复杂的报表,如利用嵌入和链接Microsoft Excel和Microsoft Word,或者自己开发报表打印单元等。在众多的方法中,笔者认为,用的Delphi中无缝集成在IDE中的Formula One构件生成复杂的文字表格报表比较方便,且效果比较理想。
2.报表生成及打印要求
Formula One生成的报表格式是非常灵活的, 它类似Excel的表格。下面我们以一张工作任务表为例。假设已经在后台数据库(笔者用的是DB2)中建立了一个有“类别”、“电压等级”、“停电设备范围”、“工作主要内容”、“工作地点”、“工作时间” 、“负责人”等字段的数据表;并在delphi的报表画面(Form)上利用构件“Database”及“Query”将数据取了出来,存在”Query”中。一般复杂的文字报表遇到的难题存在两方面:一是随意改变行的高度,二是在需要的地方画表格线。而Formula One构件类似Excel的表格形成的正是解决这两方面问题的好手。本文着重讨论的是从Query中取出数据放在工作任务报表后,由于“工作主要内容”字段长度有可能超出列宽,怎样把内容完整显示?笔者采取按字段的长短动态改变行的高度,报表各列宽度保持不变而使字段内容完整显示的办法,并且在需要的地方大画好表格线;另外,报表由于一页显示不完,分若干页显示和分页打印怎样实现的问题,以及在每页报表上显示当前页数和总页数的方法。
3.生成复杂报表的方法
首先,在Delphi中,新建一个报表的Form,本文中命名为Frm_BB,为了达到报表分页显示和打印的目的,Frm_BB上的按钮建有“第一页”“上一页”“下一页”“最后一页”“打印本页”“打印所有”的按钮 (均为TSpeedButton类),这几个按钮放在画面顶部的Panel条中,在画面的其余大部分的客户化区域放报表构件formular One: TF1Book类(在ActiveX页可以找到),Frm_BB中命名为f1book1。画面如图所见。放好构件后,需要先生成报表的表头,内容和内容的表格线留空,以便在程序中自动按需要生成。这里笔者给出f1book报表格式的生成方法:方法一是在Frm_BB中加入f1book构件,在f1book构件按鼠标右键,在弹出的菜单上选“workbook designer”,在打开的画面中画报表头;方法二是在软件“Formular One 6”中的“workbook designer”画好表头,存成“xls”后缀的文件,在Frm_BB中的f1book1的“workbook designer”中打开这个文件载入即可。字体格式可以在“workbook designer”中设置,也可以以后在程序中由命令设置,一般表头等不变的字体在这里设置就可以了。读者可以看出由于采用了类似Excel的表格,所以任意复杂的表头“workbook designer”也完全可以胜任,表内容也可以适应复杂的情况。画面如下所示。这时,为了在运行程序时观看的清楚,没有不必要的起见,还可以将f1book1的”property”属性中鼠标可移动性、键盘可移动性、可编辑性等属性去掉,只留有在表格超出显示画面的滚动条的“Vertical Scroll Bar”和“Horizontal Scroll bar”为“Automatic”属性,以符合滚动浏览报表的需要。
接下来,解决f1book1中显示行高度灵活可变的数据及所需的表格线的问题。Formula One类似电子表格,是由行和列定位的,程序员指定某行某列的内容用语句:               F1Book1.TextRC[行号,列号] := 字段值来显现出来。那么在读取了在数据感知控件里的数据后, 只要计算出内容字段的总长度,再根据本行宽度像素所能容纳的字数,进行求商数计算便能知道一条记录要占居的行数。行数本文用J表示,公式如下:J := MaxInt((Length(MStr1) + WCONST1 - 1) div WCONST1,1);这个式子中MStr1为第一个长字符字段,MaxInt为取整数最大值函数,代码如下:
function MaxInt(const FirstInt,LastInt: Integer):Integer;
begin
if FirstInt>LastInt then Result := FirstInt else Result := LastInt;
end;
WCONST1为第一个长字符字段所在的列宽字符数,它符合在“workbook designer”中的单元格的宽度像素值。
一条记录的行数确定好后,每页显示多少条记录呢?这个问题也好办。因为要打印的纸的大小是一定的,根据纸张的高度像素可以调试出“workbook designer”表格的行数,这个数目是一定的,本文用HCONST表示,每写入一条记录已知行数后就可算出本页剩余行数,本文用Mcount表示,当MCount为0时应该开始下一页,此时MCount再复原成一页的行数,下一页在Formula One中用AddRowPageBreak(K+1)表示,其中K为当前行号。
换页时,下一页的表头复制(用剪贴板)第一页已画好的Formula One的表头,方法为:    
F1Book1.SetSelection(1,1,3,7);//7指报表的列总段数,3指行数
EditCopy;
F1Book1.SetSelection(K+1,1,K+3,7);
EditPaste;
那么画面上的上下页按钮怎样实现呢?仅上述提到的变量K还不够,如果在每加一新页的同时记录下此时的行号作为一个数组(MStrings),数组的个数即位总页数(本文为countpage),再根据跳页时页数(本文为ThisPage)算出Formula One该页所在行号范围用F1Book1的TopRow属性指定首行即可,具体地说,在FormShow中实现数据显示分页的问题:
procedure TFrm_BB.FormShow(Sender: TObject);
begin
ReadRecord;//读记录并显示在单元格中
end;
procedure TFrm_BB.ReadRecord;
var
MStr1,MStr2,MStr3,MStr4,MStr5: String;
MCount,J,K,Index: Integer;
NewColor: LongInt;
begin
ThisPage := 1;//当前页号
CountPage := 1;//总页数
MCount := HCONST-4;//mcount为每页剩下的行数,预先减去表头4行
NewColor := RGB(0,0,0);
with Query1,F1Book1 do begin
Close;    SQL.Clear;    SQL.Add(‘select * from ….’);//读sql内容    
try      Open;    except     Exit;    end;
if EOF then Exit;
K := 4;  // k为当前行号
Index := 0;
while not EOF do begin
MStr1 := FieldByName('TDSBFW').AsString;
MStr2 := FieldByName('ZYNR').AsString;

J := MaxInt((Length(MStr1) + WCONST1 - 1) div WCONST1,(Length(MStr2) + WCONST2 - 1) div WCONST2);
…//j为当前记录所需行数
if MCount<J then begin  //如果每页剩余行数小于当前记录需要行数,则启动下一页
Inc(K);
MCount := HCONST-4;
AddRowPageBreak(K+1);
ClearClipboard;
MStrings.Add(IntToStr(K+1));
SetSelection(1,1,4,10);
EditCopy;  //拷贝表头
SetSelection(K+1,1,K+4,10);
EditPaste;  //粘贴表头
Inc(K,4);   行号加表头行数
end;
SetSelection(K+1,1,K+1,10);
SetBorder(-1,1,1,1,1,0,-1,NewColor,NewColor,NewColor,NewColor);//画表格行线
SetAlignment(F1HAlignLeft,True,F1VAlignCenter,0);  //设置文字显示格式
if J>1 then SetRowHeight(K+1,K+1,J*WDEFULT,False);
Index := Index+1 ;
….
TextRC[K+1,4] := MStr1;
TextRC[K+1,5] := MStr2;

Inc(K);
MCount := MCount-J;
Next;
end;
MStrings.Add(IntToStr(K+2));
CountPage := MStrings.Count;
for J := 1 to CountPage do
begin
SetSelection(StrToInt(MStrings[J-1])-1,10,StrToInt(MStrings[J-1])-1,10);
SetAlignment(F1HAlignRight,False,F1VAlignCenter,0);
TextRC[StrToInt(MStrings[J-1])-1,10] := '第'+IntToStr(J)+'页,共'+IntToStr(CountPage)+'页';
end;
end;
ResetEnabled;
end;
procedure TFrm_BB.ResetEnabled;//重置按纽及显示范围
begin
SpeedButton1.Enabled := (ThisPage<>1) and (CountPage>1);//第一页
SpeedButton2.Enabled := SpeedButton1.Enabled; //上一页
SpeedButton3.Enabled := (ThisPage<>CountPage) and (CountPage>1); //下一页
SpeedButton4.Enabled := SpeedButton3.Enabled; //最后一页
SpeedButton5.Enabled := CountPage>1;//打印本页
Panel1.Caption := Format('第%d页,共%d页',[ThisPage,CountPage]);
if ThisPage=1 then F1Book1.TopRow := 1
else F1Book1.TopRow := StrToInt(MStrings[ThisPage-2]);
end;
下一页按钮代码如下:
procedure TFrm_BB.SpeedButton3Click(Sender: TObject);//下一页
begin
ThisPage:=ThisPage+1;
ResetEnabled;
end;
那么打印某页时用的语句如下:
F1Book1.PrintArea:=Format('A%d:J%d',[StrToInt(MStrings[ThisPage-2]),StrToInt(MStrings[ThisPage-1])-1]); 
F1Book1.FilePrint(True);
其中A1:J%d表示打印行列范围,%d的值由方括号内表达式值代替,MStrings是一个Tstrings类型的变量,存放每页首行号的下一行。有了变量Mstrings,在每页显示页数情况就不难了,具体可由下列语句做到:
TextRC[StrToInt(MStrings[J-1])-1,10] := '第'+IntToStr(J)+'页,共'+IntToStr(CountPage)+'页';
4.Formular One(ActiveX)回顾:
在解决了上述的报表问题之后,笔者相信读者在遇到任何的不规则的文字报表后将不再成为难题。
在Delphi编程中,下面两种情况下应当考虑使用ActiveX控件,而不是Delphi自身的元件。一是没有可用的Delphi元件能满足你特殊的需要。由于ActiveX控件的市场比VCL控件要大,因此,你可以找到许多功能齐全、工业化的控件,诸如字处理器、WWW浏览器、表格等。二是你想使用多种编程语言进行开发,想在多个开发平台之间共享一些控件。所以MicroSoft积极鼓励把ActiveX控件作为与程序无关的自定义控件。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值