简介:Quick Report 4.05 是面向 BCB6 与 Delphi 7 开发环境的专业报表生成工具,专注于可视化报表设计、数据分组与打印输出,尤其针对自定义纸张尺寸下的布局与打印精度做了优化,适合需要在 C++ Builder 或 Delphi 中集成复杂报表功能的开发者。工具核心包含拖放式报表设计器、打印预览引擎以及表格、图表、图片框等丰富控件,支持多级明细区、汇总区和分组功能,可满足企业级业务报表的定制需求。资源包共 65 个文件、247KB,以 pas 源码文件(34 个)和 dfm 窗体资源文件(15 个)为主,另有 res 资源、dpk/bpk 包工程文件、cpp 源文件及 inc 配置文件等,完整覆盖报表组件库的单元实现、设计期界面定义与运行期构建脚本,便于阅读、修改和重新编译。已有 284 人学习,包内代码与资源文件齐全,可用于深入剖析 Quick Report 内部工作机制,并在 D7/BCB6 环境下自行修复问题或扩展打印能力。
1. 这包 Quick Report 4.05 还在被翻出来用,不是因为情怀
你手上大概率还压着一个 Delphi 6 或 C++Builder 6 时代的旧项目,报表模块用了一整套 QuickReport 控件。系统跑了十几年没大改,偏偏最近客户要求加一张汇总单、改一列字体、导出成 Excel。这时候你打开源码一看,Form 上躺着一排 QR 开头的控件,安装目录里翻出一个 QuickReport 4.05 的压缩包。这个包就是给 BCB6 和 D7 两个老 IDE 准备的报表控件源码资源包,装完以后设计期控件面板多出一组 QR 组件,能继续画报表、绑数据集、做导出。适合谁?适合那些还在维护老系统、又没法说服客户重写报表模块的从业者。这篇会把安装流程、常用控件的配合方式、导出参数和跨版本编译的坑都过一遍,照着做能把老项目的报表部分重新跑起来。
2. 安装前先弄懂这套控件的组成和定位
2.1 QuickReport 在 BCB6/D7 里的角色定位
QuickReport 4.05 是挪威一家老牌组件商做的报表引擎,当年随 Delphi/C++Builder 一起分发,所以在 BCB6 和 D7 的安装盘里经常能看到它的踪影。它不是那种拖一个控件就自动出漂亮报表的“傻瓜式”工具,而是一个由十几个控件组成的报表框架:你要自己放一个报表容器、放若干 Band(带区)、在 Band 上摆数据集字段控件,再把 Band 的 DataSet 属性指过去。这套思路和后来的 FastReport、iReport 相比确实老派,但它有个实打实的优势:和 BDE、ADO 数据集打交道非常直接,配置链条短,调试起来能少绕很多弯。
2.2 压缩包里该有什么:源码、DCU 和 Demo
拿到 QuickReport 4.05 for BCB6 D7 的压缩包后,我建议先解压到一个不带空格的纯英文路径,比如 D:\QuickReport405。正常来说包里会包含 Source(.pas 源码)、Lib(.dcu 编译单元)、LibD7 或类似命名区分版本的目录,还有几个 Demo 子目录。你的第一件事不是打开 BCB6 就去装,而是先确认 Source 目录里有没有 QREdit.pas、QRPrntr.pas、QRCTRLS.pas 这几个核心文件——它们是整个控件的骨架。Demo 目录里挑一个最简单的例子先编译一次,能通过就说明包本身没有缺胳膊少腿。
D:\QuickReport405 ├── Source\ // 核心源代码,含 QR*.pas 系列 ├── Lib\ // 对应 BCB6 的 .dcu/.bpi 编译产物 ├── D7\ // 对应 Delphi 7 的版本目录 ├── Demo\ │ ├── Simple\ │ └── Printer\ └── Readme.txt // 版本说明,先看这个这是一个典型的目录检查动作,重点确认 Source 里的核心文件存在,因为后面重编译控件时必须以源码为基准。如果 Lib 目录里已经有 .dcu 但你用的是不同 Update 版本的编译器,旧 DCU 可能会报“无法定位”或“版本不匹配”,这时候就得删掉 DCU 用源码重新编译。
2.3 装进 IDE 的方式:组件菜单还是手工编译
把 QuickReport 装进 BCB6 有两种常见做法。第一种是走 Component 菜单的 Install Packages,选中 QREdit.dcp 或 QR*.bpl 直接安装,好处是快,缺点是如果控件包是用旧版本编译器编的,装完可能偶尔报错。第二种是我更习惯的方式:打开 Source 里的 QREdit.dpk 或 QRCTRLS.bpk,在 IDE 里点 Compile 重新编译一次,再点 Install。这么做虽然慢,但能保证控件包和你的 IDE 补丁版本完全对得上。
// 手工编译安装的伪流程(BCB6) // 1. File -> Open 打开 QREdit.bpk // 2. Project -> Build 重新编译 // 3. Component -> Install Packages -> Add -> 选生成的 .bpl // 4. 重启 IDE,看到 Tool palette 多出 QReport 页编译这一步的核心逻辑在第二步:用源码重新构建 .bpl,再把 IDE 的包引用指向新产物。参数上需要注意 .bpk 文件里的 Conditional defines,一般建议保持默认,不要随意加额外宏,否则控件行为会变化。装完以后,你要是发现 Tool palette 里有 QuickReport 页但没有控件图标,多半是图标资源漏了,重新检查包编译输出的 .res 文件即可。
3. 把第一张 QuickReport 报表跑起来:拖、连、预览的三步闭环
3.1 报表容器和 Band 的关系先理顺
很多第一次接触 QuickReport 的人容易卡在一个点上:报表容器是一张白纸,但你不能直接在它上面摆控件,必须先放 Band。QuickReport 的运作逻辑是容器(TQuickRep)负责纸张大小、边距、打印方向和数据集的总入口,而 Band(TQRBand 及其子类)负责把内容分成页头、页脚、明细、汇总这些区块。你要在容器上右键加 Band,然后把明细型 Band 的 DataSet 属性指向一个数据集组件(比如 TQuery 或 TADOQuery)。
// 在设计期建立最小报表的三个步骤 // 1. 放 TQuickRep 到 Form 上,设其 DataSet 属性为 ADOQuery1 // 2. 在 TQuickRep 上右键 -> 添加 Band,选 rbDetail // 3. 往 rbDetail 上放 TQRDBText,把 DataSet 和 DataField 指过去第一步是关键:TQuickRep 的 DataSet 决定了整张报表的“数据源主入口”,连打印时的记录计数和分组都以它为准。第二步的 rbDetail 表示明细带区,每来一条记录它就重复渲染一次。第三步的 TQRDBText 是指向具体字段的文本控件,它本身也有 DataSet 属性和 DataField 字符串属性,设计期如果数据集打开着,你甚至能在属性下拉框里直接选字段名。这组配合不复杂,但顺序不能乱:先容器,再带区,最后字段控件。
3.2 预览和打印的代码调用没有想象中复杂
报表设计完以后,运行期只需要一行代码就能弹预览窗口,或者直接送打印机。QRLabel 之类的标题控件也在带区里,TQRDBText 负责数据字段,两者各司其职。常见做法是把预览动作挂到一个按钮的 OnClick 事件里:先调 Prepare 让报表完成数据准备和分页计算,再调 PreviewModal 弹预览窗口,等用户点窗口里的打印按钮后,真正的打印动作才在预览模块内部触发。
void __fastcall TForm1::btnPreviewClick(TObject *Sender) { QuickRep1->PreviewModal(); // 弹出打印预览窗口 }就这么一句就够了,因为 QuickRep 的 DataSet 在设计期已经绑定,运行期打开数据集后,Prepare 会在 PreviewModal 内部自动完成。参数上有一个值得注意的点:如果数据集是 TQuery 这种非游标类数据源,PreviewModal 前最好主动调一次数据集 Open,否则预览窗口里可能看不到数据。还有一个容易被忽略的细节:预览窗口的默认显示比例受 QuickRep 的 Zoom 属性影响,设计期设为 100 就是按实际尺寸预览,如果客户总说“报表字太小看不清”,不是改字体,而是先把 Zoom 调低到 85 左右。
4. 导出 PDF/Excel 和分组合并:三个参数决定输出质量
4.1 TQRPDFExportFilter 是官方导出 PDF 的老路子
QuickReport 4.05 自带一个 TQRPDFExportFilter 过滤器组件,它的工作原理是:把报表的每一个绘制动作转换成 PDF 的矢量绘制指令,所以文字和线条在 PDF 里都是矢量的,放大不模糊。这一步不需要额外装第三方库,你只要在 Form 上放一个 TQRPDFExportFilter,然后在打印或预览完毕后调用它的 OnPrint 系列方法即可完成保存。
// 导出 PDF 的最小代码片段 TQRPDFExportFilter *pdfExport = new TQRPDFExportFilter("report.pdf"); pdfExport->Compression = true; // 开启 PDF 压缩,能减少体积 pdfExport->MaxPages = 0; // 0 表示不限制页数 QuickRep1->ExportToFilter(pdfExport); // 执行导出 delete pdfExport;Compression 属性是第一个值得关注的参数,置 true 后大报表文件体积能压掉约 40%,但如果报表里嵌了大尺寸位图,压缩反而会增加 CPU 耗时。MaxPages 的意义是配合“只导出前几页”的场景,比如只给客户看第一页摘要时设 1,其余场景保持 0。ExportToFilter 会从第一页开始顺序遍历所有带区,到最后一页才结束,中途如果数据集被别的代码关闭了,这里会直接报“数据源没有活动”一类的异常。
4.2 Excel 导出引用的 QRExcelfilter 需要注意行列限制
老项目里要把报表导成 Excel,QuickReport 4.05 自带的是早期 .xls 格式过滤组件。它的导出逻辑是把每一个报表单元格直接映射成一个 Excel 单元格坐标,所以报表里的横向列数不能超 256 列(xls 格式老限制),超过以后会出现“单元格写不进去”的静默丢失——报表不报错,但导出后的 Excel 文件少了数据。
// 导出 Excel 的正确姿势 TQRExcelFilter *xlsF = new TQRExcelFilter("report.xls"); xlsF->WorksheetName = "汇总数据"; // 工作表名别用中文以外的特殊字符 QuickRep1->ExportToFilter(xlsF); delete xlsF;WorksheetName 属性在导出时会决定工作表页签名,注意不要带 、?* 等 Excel 非法字符,否则文件打不开。另一个参数是后面会提到的 TextOnly 类选项,在部分版本里叫 StringOnly,它会决定单元格是文本格式还是数值格式——如果客户后续要用 Excel 对数字做 SUM,你必须让数字字段以数值类型落进单元格,否则公式会算出 0。这个坑我踩过一次:某财务汇总表导出后,财务同事说求和全是 0,查了半天发现是 StringOnly 被设成了 true,所有数字全变文本了。
5. 避坑手册:跨版本编译、字体移位和数据源断连的三个典型翻车现场
5.1 现象一:BCB6 编译时找不到“QRTour”或“vr.typ”之类的单元
这是 QuickReport 4.05 在 BCB6 下最常见的报错,报错信息看起来像找不到某个源文件或某个单元名。实际原因有两个:一是 Source 目录没加到 IDE 的 Library 搜索路径,编译器只去 Lib 目录翻 DCU;二是你用的编译器版本(比如 BCB6 打了 Update4)和包里预编译的 DCU 不匹配。解决办法是在 IDE 的 Tools -> Environment Options -> Library 里把 Source 目录加到搜索路径,然后删掉 Lib 目录里所有 DCU 让编译器强制重新编译源码。这条路之所以可靠,是因为 QuickReport 4.05 在 BCB6 和 D7 双版本之间有源码兼容层,重新编译源码能绕开预编译产物的版本校验。
5.2 现象二:预览正常,打印出来纸张上内容整体右移下移了
老项目报表换打印机后经常出现这个问题——预览界面看着是居中的,打印出来整体偏移 1~2 毫米。根因是 QuickReport 4.05 的坐标计算基于打印机 DC 的分辨率,而不同驱动上报的物理分辨率不一致。常规解决手段是在 QuickRep 的 OnStartPage 或打印设置里强制指定 PrinterSettings 的 XDPI/YDPI,但更直接的手段是把 QuickRep 的 Units 属性设为 MM(毫米),并用 LeftMargin、RightMargin 这些毫米单位的边距属性去微调。还有一种玄学情况是报表容器设计期是用“屏幕分辨率”拖出来的,到了打印机 DC 上一换算就偏了,这时用顶部对齐线手工微调节基线。从那以后我做老报表兼容时都会强制先跑一遍同一张报表到 PDF 和物理打印机的双份对照,偏了就调边距,而不是去改每个带区上的控件坐标。
5.3 现象三:代码里打开了数据集,预览却一片空白
这个问题最常见于 ADO 连接 + TQuickRep 的组合。第一反应是查数据集有没有打开,但往往数据集已经 Open,列表里也有记录,报表仍空白。原因在于 TQuickRep 的 DataSet 属性和明细 Band 的 DataSet 属性必须同时指向同一个数据集——如果容器上挂的是 ADOQuery1,明细 Band 却还默认指着一个已经关闭的 Table1,预览时明细带区没有数据流可读,报表自然空白。解决方式是先看整个 Form 上的所有 Band 控件的 DataSet 属性,逐个统一指到活动数据集,再检查 TQuickRep 的 Dataset 属性。这两处不一致是 QuickReport 报表空白的第一大原因,比字体和坐标问题出现频率高得多。
5.4 现象四:报表里中文变成“?”或乱码方块
QuickReport 4.05 在 BCB6 下常用默认字体名如 Arial,而这些老字体不含中文字形映射,打印出来就是方块或问号。这不是控件 bug,是字体缺字符映射,解决办法是统一把报表上的 TQRDBText 和 QRLabel 的 Font.Name 指到“宋体”或“微软雅黑”。这里有一个隐藏坑:QR 控件在设计期你改了 Font,但带区重建时 Font 属性可能没有被继承下来,所以导出 PDF 前先做一个全局字体替换函数,把整张报表的所有控件 Font 全量扫一遍替换成指定中文字体,能省掉大量逐个找控件的体力活。
6. 进阶:编一个全局字体修正函数,顺手把报表的打印分辨率统一收口
这个技巧来自一次真实教训。当时给某公司维护一套老 Delphi 6 进销存系统,十几张报表的字体不统一,有的用宋体、有的用默认 Arial,客户打印出来中文字体大小参差不齐,业务回来说“报表看起来像临时工做的”。后来我写了一个报表遍历函数,专门把 Form 上所有 QuickRep 容器内部的中文控件字体统一收口到宋体,顺带修正打印 DPI 参数。从那以后我每接手一套 QuickReport 老项目,都会先跑一遍这个函数再谈其他改动。
// 递归遍历 QuickRep 所有子控件,统一中文字体 void __fastcall FixReportFonts(TComponent *AOwner) { for (int i = 0; i < AOwner->ComponentCount; i++) { TComponent *comp = AOwner->Components[i]; if (dynamic_cast<TQRDBText*>(comp) || dynamic_cast<TQRLabel*>(comp)) { TQRCustomLabel *lbl = dynamic_cast<TQRCustomLabel*>(comp); if (lbl) { lbl->Font->Name = "宋体"; // 统一中文 lbl->Font->Charset = DEFAULT_CHARSET; } } // 递归深入到子容器,因为 Band 里的控件套了好几层 if (comp->ComponentCount > 0) FixReportFonts(comp); } }这个函数每遍历到一个 TQRDBText 或 TQRLabel 就强制把字体名改成宋体,同时把字符集设为默认,避免部分控件因为 Charset 不匹配造成中文字符替换成问号。注意这里用的是 dynamic_cast 而不是直接访问控件的公共属性,因为 QuickReport 4.05 里 TQRLabel 和 TQRDBText 各自是不同继承链上的类,直接统一类型访问很容易漏掉。CharSet 这个参数在旧控件里经常被忽视,很多老报表的乱码根源就在它身上——Font.Name 是宋体,但 Charset 被 IDE 写成了 ANSI_CHARSET,最终打印时还是走的西方字符映射表。
调用函数的时机也有讲究,我一般放在 Form 的 OnCreate 事件里:报表设计期已经打开过,创建时统一设置字体,之后数据绑定不需要再动字体。如果你想更彻底,可以在 QuickRep 的 BeforePrint 事件里调用这个函数,这样每次打印前都会强制修正一次,哪怕运行期间有用户改了控件字体也能被拉回来。代价是每次打印前多花几十毫秒遍历,但老报表系统优先级永远是“稳定第一、性能第二”,这点开销可以接受。希望这个函数能帮你在接手工期报表时少踩几个坑。
本文还有配套的精品资源,点击获取