QuickReport 4.05老系统报表维护:安装配置与分组小计实战
2026/9/7 7:57:25 网站建设 项目流程

简介:Quick Report 4.05 for BCB6 D7 是一套面向 Borland C++ Builder 6 与 Delphi 7 的经典报表生成控件及完整源码包,重点解决了自定义纸张尺寸下的打印布局与输出精度问题,适合使用 BCB6/D7 开发桌面应用、需要集成专业报表与打印功能的开发者。压缩包仅 247KB,包含 65 个文件,以 34 个 Pascal 单元(.pas)和 15 个窗体定义(.dfm)为主,另有 .dpk/.bpk 工程文件、资源文件(.res/.dcr)及少量 C++ 桥接代码,模块划分清晰,便于按需引用或二次封装。已有 280 人学习下载。资源附带了 Quick Report 4.05 全部源代码,可深入查看从基础打印到 PDF/XML 导出等扩展功能的具体实现;同时提供 qreport.dcr、qrabout.dfm、qrprev.dfm 等资源文件,内含组件图标、关于对话框、预览界面等原始设计定义。对于仍在维护 BCB6/D7 老项目、或希望基于经典报表引擎定制打印逻辑的开发者而言,这份包含可编译工程与完备窗体的控件包,既可用于直接集成,也可作为理解早期 Delphi/C++ Builder 报表架构的参考资料。 QuickReport 4.05,一看版本号就暴露年龄了。在 BCB6(C++ Builder 6)和 D7(Delphi 7)这两个 IDE 横扫桌面开发的年代,QuickReport 就是它们身上标配的报表组件,装完 IDE 自带 QReport 页的那种。前阵子一个跑了十来年的老系统要求加一张按部门统计的工资表,客户明确说继续用 QuickReport 4.05,原因是同事都会、不用买授权。没办法,我把这套东西重新翻出来,从安装到出报表又完整走了一遍,中间还踩了几个坑。这篇文章就把这些实操内容整理出来,给同样在维护老 VCL 项目的朋友做个参考。

QuickReport 解决的核心问题是什么?在 VCL 程序里把数据库记录组织成可预览、可打印、可导出的报表。它适合正在维护 BCB6/D7 老代码的开发者,也适合刚接触 VCL、想理解报表数据循环输出的入门者。整个 API 不复杂,但有些内部逻辑不搞清楚会绕不少弯路。

1. 为什么到今天还要用 QuickReport 4.05

1.1 当年它几乎是 VCL 报表的代名词

QuickReport 出现的时间点很有意思。2000 年前后桌面应用爆发式增长,银行、物流、仓管、财务这些行业对清单打印的需求大得惊人。当时的 Delphi 和 C++ Builder 装机量很高,而 QuickReport 一度被 Borland 集成进 IDE 的分发包里,开发者打开组件面板就能找到,不需要额外装任何东西。

数据感知是那个时代组件设计的核心思路。TQRDBText 直接绑定 DataSource 和 DataField,报表引擎自动遍历数据集,每条记录输出一行 Detail Band。开发者不用写绘制代码,不用管分页,甚至不用管打印机 DC,这在当时把报表开发门槛压到了极低。对比后来有些同事拿着 .NET 的 ReportViewer 调了半天还没出来一张单,你就会理解 QuickReport 为什么能活那么久。

1.2 老项目不换报表引擎的真实原因

很多人问我:为什么不建议客户迁移到 FastReport 或者 Rave?我通常先反问一个问题:你的老系统里有多少张报表?如果答案是几十上百张,那迁移成本根本不在报表本身,而在每一张报表对应的历史客户数据、打印格式、验收记录。QuickReport 4.05 在老项目里最大的优势就是稳定和可控,它不依赖额外的 DLL,二进制组件可以直接注册进 BCB6/D7,线上环境部署就是把编译好的 EXE 拷过去,没有一堆运行时依赖要处理。

而且 4.05 这个版本有源码,这一点很关键。遇到打印走样、分组小计不对这种问题,你可以直接打开 qrexpr.pas 看表达式解析逻辑,打开 qrprntr.pas 看打印机处理逻辑。这个源码在手的效果,等于你有一本自带注释的底层文档,出问题不用等厂商更新,自己就能定位。

当然它的短板我也要说清楚:复杂报表做起来累、图形图表几乎等于没有、单元格合并这类操作很反人类。但现实是,传统行业里绝大多数业务报表就是清单、明细、汇总,根本用不到那些花哨功能。在 BCB6/D7 这个技术栈里,QuickReport 4.05 依然是投入产出比最高的选择。

2. 安装环节:BCB6 和 D7 的差异处理

2.1 拿到安装包后的第一件事

QuickReport 4.05 的压缩包解压后,一般是 Source 目录加一堆 .pas、.dfm、.dcr 文件,偶尔带一个 install.txt 或 README。先别急着点安装,第一步是把整个目录放到纯英文路径下,比如 C:\QuickReport。千万别放中文目录或者带空格的目录,BCB6 的编译器在解析这些路径时问题很多,报错信息还很隐秘,你根本想不到是路径的锅。

然后打开 IDE,确认版本。BCB6 和 D7 虽然都是 VCL,但组件包机制有一点差别:Delphi 7 走的是标准 .dpk 流程,BCB6 则是通过组件包编译后自动生成 .hpp 头文件。遇到老外写的纯 Delphi 源码,BCB6 偶尔会提示找不到某些 .hpp,这通常不是代码问题,而是头文件生成失败或者 Library Path 没有配置到位。

提示:安装前先关闭当前打开的所有工程,并备份 IDE 的 Lib 目录。我习惯用压缩软件打一个带时间戳的 zip,装挂了五分钟就能回滚,这个习惯后来救过我好几回。

2.2 在 BCB6 里注册组件的完整步骤

我建议走 Component 菜单而不是手动改包文件,操作是:

  1. 打开 C++ Builder 6,点击 Component -> Install Component。
  2. 在弹窗里选择 Into existing package,Unit file name 一栏浏览选择安装包 Source 目录里的主组件单元。不同版本文件名不太一样,常见的是 QuickRpt.pas 或 qrpt.pas,以 README 标注为准。
  3. Package file name 填一个自己定义的包名,比如 dclQuickReport40.bpk,输出目录放在 C:\QuickReport 下。
  4. 点 OK 让 IDE 开始编译。如果中途报错找不到 .dcr 或 .res,去 Tools -> Environment Options -> Library 里把 Source 路径追加到 Library Path,再重新编译。
  5. 编译完成后,到 Component -> Install Packages 里确认新包已被勾选,确定后组件面板会刷出一个 QReport 页。

这一步最常见的翻车点有两个:一是路径带空格导致编译失败,二是包名和已有组件冲突。第二个问题一般出现在机器上装过其他版本 QuickReport 的情况,解决办法是先卸载旧包再装新的,别图省事直接 Add。

2.3 D7 下的安装更省事

Delphi 7 里安装这个组件会顺畅很多。版本包里如果直接带 .dpk 文件,双击打开,在 Package 窗口点 Compile,再点 Install,组件面板就会出现。如果打开 dpk 时报找不到单元,同样是 Library Path 的问题,按照上面说的路径流程处理一遍即可。

BCB6 和 D7 装完以后,除组件页名称一样,其他没有太大区别。唯一的细节是:用 BCB6 的工程里,如果代码中直接 include 了 QuickReport 的某个头文件,要注意头文件名的大小写和实际 gen 出来的一致,Windows 下虽然不区分大小写,但个别老版本的头文件缺失时,编译错误会非常费解。

3. 报表的核心逻辑:Band 与数据集之间怎么配合

3.1 把 Band 想象成一张表的若干行区域

QuickReport 设计上最核心的概念就是 Band,也就是报表区域。你可以把一张报表想象成一张多行的稿纸,每个 Band 对应稿纸上的一行区域,只是有的行重复出现,有的行只出现一次。QuickRep 是报表的外壳,上面摆的 TQRBand 控制每个区域的输出时机。

常用 Band 类型对应关系如下:

Band 类型出现时机典型用途
PageHeader每一页顶部页眉、公司名称、页码
Title整份报表只出现一次报表总标题
ColumnHeader明细区上方列名表头
Detail每条记录重复一次数据明细行
GroupHeader每组开始处分组标题、分组字段
GroupFooter每组结束处小计、人数统计
Summary整份报表结束处总计、全表汇总
PageFooter每一页底部页码、打印日期

添加 Band 的方法是右键 QuickRep,在弹出菜单里选择对应 Band 类型。刚开始用的时候很容易把所有区域都放在 PageHeader 里,导致数据行一多就重复打印表头,把 Band 语义理解对之后这个问题就会自然消失。

3.2 数据循环是怎么发生的

QuickRep 的 DataSet 属性指向一个 TDataSource 后,预览时报表引擎会自动遍历这个数据集的全部记录,每取出一条记录就渲染一次 Detail Band。这个循环是引擎内部处理的,完全不用手写 while not eof。

主从报表则多一层联动。父表挂在 QuickRep.DataSet 上,子表通过 QRSubDetail 的 DataSet 指定,同时把 QRSubDetail 的 Master 属性指向 QuickRep。运行时,父表每输出一条记录,子表会重新查询一次并按关联字段输出明细。新手最容易犯的错是把子表也挂到 QuickRep.DataSet 上,这样两张表的数据会交叉成笛卡尔积,行数爆炸,排查起来非常迷惑。

3.3 常用组件选型速查

组件作用使用位置
TQRLabel静态文本标题、固定文字
TQRDBText绑定字段数据显示Detail Band
TQRExpr表达式计算小计、合计、拼接
TQRShape画线、画矩形边框、分隔线
TQRMemo多行静态文本合同条款、说明
TQRDBImage数据库图片照片、签名
TQRGroup分组头GroupHeader
QRSubDetail子报表明细主从报表

3.4 QRBand 排序的隐藏规则

Band 之间的相对顺序其实会影响渲染结果。设计器里可以随便拖,但实际输出时 QuickReport 会按枚举顺序排列,不在设计顺序排列。我那次做工资表,把 Summary Band 拖到了 PageFooter 下面,结果合计一直不出现,后来把 Summary 拖到 PageFooter 上方才正常。

4. 实操:一张带分组小计的工资表

4.1 先想清楚需求和数据结构

需求很简单:打印员工工资单,按部门分组,每个部门结束显示该部门的人数和工资小计,最后整份报表显示全部合计。这个需求非常适合入门,因为它覆盖了 Band、分组、表达式计算、运行时过滤四个核心点。

先准备一份 SQL,注意分组字段必须排序:

SELECT dept_name, emp_no, emp_name, base_salary, bonus FROM employee ORDER BY dept_name, emp_no;

这里有一个非常重要的细节:QuickReport 的分组逻辑是“相邻相同才归为一组”,不是全局分组。如果数据没有按分组字段排序,同一个部门会分散成好几个不连续的组,小计也会跟着错乱。我见过不少人在问为什么分组统计不对,最后发现全是 SQL 里少了 ORDER BY。

4.2 一步一步搭报表

在窗体上放好 TADOConnection、TADOQuery、TDataSource,查询指向上面那条 SQL,DataSource 指向 TADOQuery。然后把 QuickRep 的 DataSet 设为这个 DataSource。

接下来按顺序添加 Band:

  1. 右键 QuickRep,添加 Title Band,放一个 TQRLabel,Caption 设成“员工工资表”,字体选宋体加粗 16 号。
  2. 添加 ColumnHeader Band,放五个 TQRLabel 分别写“部门”“工号”“姓名”“基本工资”“奖金”。
  3. 添加 Detail Band,放五个 TQRDBText,DataField 分别对应五个字段。
  4. 在 QuickRep 的 DataGroup 属性里新增一个分组项,设置分组字段为 dept_name。
  5. 添加 GroupHeader Band,放一个 TQRDBText 绑定 dept_name,再放一个 TQRLabel 写“部门:”。
  6. 添加 GroupFooter Band,放 TQRExpr 表达式分别写:
    • 人数:count
    • 小计:sum(base_salary + bonus)
  7. 添加 Summary Band,放 TQRExpr 表达式:
    • 总人数:count
    • 全表合计:sum(base_salary + bonus)
  8. 最后加 PageFooter,放 TQRLabel 写“第 ”加 TQRExprPageNumber,再写“ 页”。

这里要注意 TQRDBText 的 AutoSize 和 Autostretch 属性。AutoSize 决定控件是否随字体自动变大,Autostretch 决定行高是否随内容自动增高。在 Detail Band 里如果字段内容可能换行,通常要把 Autostretch 设为 True,否则长文本会被截断,打印出来数据不完整。

4.3 运行时动态过滤和预览

实际场景里往往需要按条件筛选,比如只打印某个部门。代码在 BCB6 下这样写:

ADOQuery1->Close(); ADOQuery1->SQL->Text = "SELECT dept_name, emp_no, emp_name, base_salary, bonus " "FROM employee WHERE dept_name LIKE :Dept ORDER BY dept_name, emp_no"; ADOQuery1->Parameters->ParamByName("Dept")->Value = "%" + EditDept->Text + "%"; QuickRep1->Preview();

用 Preview 而不是 ShowModal,这样用户在预览窗口里可以直接选打印机、设份数、缩放查看,交互是现成的。还有一个 Prepared 属性,数据量大的时候先在代码里调用QuickRep1->Prepare()再 Preview,可以避免预览时重复取数,速度提升很明显。

4.4 边框和外观整理的几个小经验

我习惯在 ColumnHeader Band 底部放一条 TQRShape 的横线,形状选 QRSRectangle,宽度设为 0.6,搭出来的稿纸感很清爽。明细行之间的分隔线不建议用控件边框,因为 TQRDBText 的边框跟随控件大小,缩进不好控,用 TQRShape 画线最灵活。

字体这块务必统一设置。BCB6 自带的默认字体在某些中文 Windows 上会把中文字段渲染成乱码或方块,把报表上所有控件的 Font.Charset 设置成 GB2312_CHARSET,Font.Name 设置为“宋体”或“微软雅黑”,基本就不会再出中文问题。

5. 实际排坑记录和日常维护经验

5.1 编译与安装类问题

现象可能原因解决办法
编译时报找不到 qr*.pasLibrary Path 没配置把 Source 目录追加进 Library Path
安装后组件面板没有 QReport包未勾选或编译失败检查 Install Packages 列表
BCB6 下提示找不到 .hpp头文件未生成成功删除包后重新编译,确认路径无空格
打开 .dpk 报单元缺失单元文件路径未加载检查 Environment Options 的 Library Path

5.2 预览和打印不符,先查纸型

预览和打印不一致是我遇到最多的投诉。排查顺序非常固定:先看 QuickRep 的 Page 属性,PaperSize 是 A4 还是 Letter,Length 和 Width 是否和物理纸张一致。很多公司局域网里的共享打印机默认是 Letter,而虚拟预览用的是 A4,出来的内容就会“看起来对不上”。

我的做法是在 BeforePrint 事件里强制设一次纸型:

void __fastcall TFormReport::QuickRep1BeforePrint(TObject *Sender) { QuickRep1->Page->PaperSize = A4; QuickRep1->Page->Length = 14000; QuickRep1->Page->Width = 9900; }

这样不管用户在系统里选了什么打印机,报表生成时始终保持同一个页面基准。这个习惯极大降低了“在自己机器上正常、客户机器上错乱”的概率。

5.3 分组错乱和小计为 0

分组错乱九成是 SQL 没排序,前面已经说过,把 ORDER BY 加上就好。小计为 0 则要看字段类型和表达式写法。TQRExpr 里 sum 只接受数值类型,如果字段是字符串类型,先转换再计算。注意空字段问题,sum 遇到空行会直接为 0,不会报错,所以要先确认数据源里没有脏数据。

5.4 一个容易被忽略的 OnNeedData 问题

QuickReport 支持通过 OnNeedData 事件动态生成数据,我用它做过拼接数据的临时表报表。但 OnNeedData 事件一定不能放耗时操作,比如打开数据库连接、大循环、网络请求。报表引擎逐条取数时这个事件会被反复调用,性能问题会被放大几十倍,预览直接卡死。如果有耗时逻辑,提前在内存里准备好数据,事件里只做赋值。

5.5 值得坚持的一个维护习惯

最后分享一个个人习惯:拿到 QuickReport 4.05 之后,先花半天时间把源码里的 qrexpr.pas、qrprntr.pas、qrextra.pas 三个文件通读一遍。这套代码放在今天看不算完美,但胜在逻辑清楚,很多“怪问题”翻一遍源码就能定位到具体语句。对还在维护 BCB6/D7 老项目的团队来说,这个阅读成本换来的排障速度,远比表面上省下的几小时更有价值。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询