简介XLSReadWriteII v6.01.17 控件包面向 Delphi 12.3 环境下的桌面应用开发者用于在程序中高效读写 Excel 文件解决报表导出、数据导入及复杂表格处理等问题。资源共 499 个文件压缩后约 2.15MB以 Pascal 源码文件为主附带大量 HTML 帮助文档、DFM 窗体定义、DPR 工程文件以及示例数据与配置文件Pascal 源码便于查看控件实现细节HTML 文档提供接口参考DFM 与 DPR 则展示了可视化窗体设计和工程组织方式。包内演示样例涵盖创建图表、自动筛选、发票模板、数据监控、交互式与高级透视表等典型业务场景方便开发者对照学习各功能的调用方式也能直接移植到自己的项目里。示例均基于实际工程整理目录结构清晰便于按功能模块快速检索。目前已有 65 人学习下载适合需要为 Delphi 应用程序集成 Excel 读写能力的中高级开发人员收藏参考。1. 为什么我把 XLSReadWriteII 当 Excel 读写的非买不可控件读过 Excel 文件的 Delphi 开发者大多经历过这种时刻OLE 自动化在装有精简版 Office 的服务器上突然失去响应后台进程越杀越多报表卡在进度条上。我在一个跨平台报表模块里被折腾到翻车之后换成了 XLSReadWriteII。它不是用 COM 把 Excel 拉起来而是在 VCL 线程里直接解析 xlsx/xls 文件流运行时不依赖 Excel 是否安装。这份 v6.01.17 D12 压缩包正是针对 Delphi 12.3 编译的控件包自带可打开复现的样例工程。适合写报表导出、数据导入、模板填充或需要后端批量生成 Excel 文件的团队使用。2. 安装 D12 包把 cfg 样例工程跑起来的两个前提拿到压缩包后第一件事不是翻源码而是先明确你是打算做静态编译还是动态 BPL 分发。和我一样把控件当作唯一报表核心组件的人一般都会选择静态连接方式编译运行时包时不勾选 Build with runtime packages然后把设计期包装进 IDE 的面板。这个决定直接影响后面所有样例题是否能正常打开。2.1 包组与库路径先决定静态连接还是 BPL 运行打开 Delphi 12.3 的包管理器能看到两类包设计期包和运行时包。设计期包负责在组件面板里显示控件运行时包负责提供实际单元。常见做法是先把运行时包编译一遍再单独安装设计期包。注意如果你机器上同时装了 DevExpress 之类的大型控件套件不要把 XLSReadWriteII 的包并进同一个运行时包组否则日后的组件升级会互相牵连出现“包依赖版本不匹配”的玄学报错。库路径设置是最容易出错的一步。打开 Tools Options Environment Options Delphi Options Library把源码目录按顺序加进 Library PathD:\Dev\Components\XLSReadWriteII\Source D:\Dev\Components\XLSReadWriteII\Source\Delphi12 D:\Dev\Components\XLSReadWriteII\XLSGrid D:\Dev\Components\XLSReadWriteII\XLSGrid\Delphi12这个顺序有讲究。Source 在最前面因为 Delphi 编译时会按列表顺序搜索.dcu先找到基础单元再去子目录找功能单元。如果顺序反了可能发生XLSRWII.dcu和XLSRWII.pas版本错位的问题。路径里不能有中文或空格否则 Code Insight 会频繁提示 unit not found。加完路径后重启 IDE在 Component Install Packages 里勾选新装的运行时包。这里常见的坑是旧版本留下的.dcu缓存没有被清理我一般会手动删掉$(BDSCOMMONDIR)\Bpl下所有 XLSReadWriteII 开头的 BPL 文件同时清掉Source目录里的.dcu再重新编译一次。2.2 cfg 文件是工程配置不是控件配置文件压缩包里有一批.cfg文件比如CreateChartsSample.cfg、InvoiceSample.cfg、SampleAutoFilter.cfg。第一次接触这套控件的人容易误以为这是运行时配置其实这些文件是 Delphi 的工程配置文件等价于.dpr旁边的.cfg。IDE 打开对应样例工程时会读取它记录的条件编译符号、搜索路径和编译目标。v6.01.17 给 D12 系列项目带的样例大多是 32/64 位双目标所以根目录里会同时看到这些文件。用一个简化后的片段来说明它的作用[Compiler] ProjectCreateChartsSample UnitOutputDir.\Win32\Debug ConditionalsXLSD6_LICENSE;DEBUG [Dirs] Source_DirsD:\Dev\Components\XLSReadWriteII\Source;D:\Dev\Components\XLSReadWriteII\Source\Delphi12Source_Dirs指定编译演示工程时所需的库单元路径Conditionals里的XLSD6_LICENSE是关键的授权开关。你在自己新建项目时不要照抄这个 cfg因为里面是绝对路径换一台机器大概率会提示找不到.dcu。正确做法是把 Source_Dirs 换成相对路径或者干脆忽略 cfg在.dproj的 Search Path 里写同样内容。直接把 cfg 复制到新机器还保持绝对路径不变是最常见的翻车原因之一现象是工程管理器里很多单元标红编译时报File not found: XLSRWII.dcu。2.3 跑通最简验证拖一个 TXLSReadWriteII 读 Sheet安装完成后新建一个 VCL 应用程序在组件面板里把TXLSReadWriteII拖到窗体上。很多人到这里发现控件是灰色禁用状态第一反应是控件损坏。其实常见原因只有两个一是工程条件编译里没有定义XLSD6_LICENSE二是设计期包和运行时包版本不一致。前者去 Project Options Delphi Compiler Conditional defines 里加入XLSD6_LICENSE然后重新编译后者回包管理器确认两个包都选中 D12 版本不能混着用旧版本的 BPL。验证读写器的代码通常放在 FormCreate 里procedure TForm1.FormCreate(Sender: TObject); var XL: TXLSReadWriteII; Value: string; begin XL : TXLSReadWriteII.Create(nil); try XL.Filename : D:\Dev\Test\Demo.xlsx; XL.ReadFromFile; Value : XL.Sheets[0].Cells[1, 1].AsString; Caption : 第一个单元格: Value; XL.Clear; finally XL.Free; end; end;TXLSReadWriteII是读写器入口对象Filename指定目标文件ReadFromFile触发解析流程。Sheets[0]是第一个工作表Cells[1, 1]的坐标是列/行和 Excel 表格里的 A1 对应。出错时先检查文件是否被 Excel 或预览程序锁定其次确认文件确实是 xlsx 结构而不是改后缀的假文件。如果这段代码能读到字符串说明包安装与授权条件编译都生效可以进入样例工程拆解阶段。3. 用 CreateCharts 与 Invoice 样例看懂读写流程XLSX 最小实现样例项目里InvoiceSample和CreateChartsSample是最适合当模板的两个文件。一个演示数据填充一个演示图表生成。读懂它们等于掌握了这套控件 80% 的日常用法。3.1 读把 Sheet 内容映射到网格控件在发票场景中流程是先读取模板里的固定表头再把业务数据逐行填进去。最简单直观的验证方法是把工作表内容映射到 TStringGrid 上代码如下procedure LoadSheetToGrid(XL: TXLSReadWriteII; Grid: TStringGrid); var Col, Row: Integer; begin Grid.ColCount : XL.Sheets[0].LastCol; Grid.RowCount : XL.Sheets[0].LastRow; for Col : 1 to XL.Sheets[0].LastCol do for Row : 1 to XL.Sheets[0].LastRow do Grid.Cells[Col - 1, Row - 1] : XL.Sheets[0].Cells[Col, Row].AsString; end;LastCol和LastRow返回的是工作表里有内容的边界不是全表最大范围所以这个循环不会穷举几万行。合并单元格只有在左上角有值其它区域读出来是空字符串这是常被忽略的行为。逻辑上先取边界再逐行读能减少对Cells接口的调用频率。如果要读超大文件逐单元格读仍然偏慢更高效的做法是调用区域批量接口把二维块一次性取到内存变量里。3.2 写把 TDataSet 内容填进 Invoice 模板InvoiceSample的典型做法是用一个客户地址数据集挂到模板 sheet 上然后逐条写入。压缩包里的Addresses.cds就是这类数据集的样例。看完它的结构可以对应写出下面的代码procedure WriteDataSetToSheet(XL: TXLSReadWriteII; DS: TDataSet); var Col, Row: Integer; begin XL.Sheets[0].Clear; for Col : 0 to DS.FieldCount - 1 do XL.Sheets[0].Cells[Col 1, 1].AsString : DS.Fields[Col].FieldName; Row : 2; DS.First; while not DS.Eof do begin for Col : 0 to DS.FieldCount - 1 do XL.Sheets[0].Cells[Col 1, Row].AsString : DS.Fields[Col].AsString; DS.Next; Inc(Row); end; XL.WriteToFile; end;这段代码有两个容易踩的细节。第一所有字段都通过AsString转成字符串输出Excel 里可以显示但数值列不再是数字类型后面对账公式会算不出来。正确的做法是按字段类型分流金额字段用AsFloat日期字段用AsDateTime字符串字段才用AsString。第二WriteToFile之前必须已经设置过Filename否则控件会直接抛异常。很多报表项目最后导出的单价列变成文本原因就是这里图省事全用了 AsString。3.3 图表不是画出来的CreateChartsSample 的关键参数CreateChartsSample.cfg对应的样例工程重点不在 UI 绘图而在数据区域绑定。图表对象挂在 sheet 上由图表类型、系列来源、坐标范围三个参数决定。精简后的写法如下var Chart: TXLSChart; begin Chart : XL.Sheets[0].Charts.Add; Chart.ChartType : xlLCChartBar; Chart.Title : 月度营收; Chart.Series.Add( XL.Sheets[0].Range[B2:B13], XL.Sheets[0].Range[A2:A13], 营收 ); Chart.Top : 20; Chart.Left : 200; Chart.Width : 400; Chart.Height : 250; end;Range[B2:B13]是值区域Range[A2:A13]是分类轴标签区域最后的营收是系列名称。ChartType换成xlLCChartLine得到折线图换成xlLCChartPie得到饼图。图表的位置属性Top/Left在部分版本里单位是像素部分版本是英磅我的经验是直接沿用默认值打开 Excel 时位置就很合理不必强求换算。填充格式这一块也不复杂。列宽调整、合并单元格、批注写入都可以在写数据之后顺手完成with XL.Sheets[0] do begin Columns[1].Width : 30; Cells[2, 2].AsString : 批注测试; Cells[2, 2].Comment.Text : 由 Delphi 写入; end;Columns[1]是第一列Width按 Excel 的字符宽度单位计算。Comment.Text是单元格批注内容换行用#13#10另外批注文本最大为 32767 个字符超过会被截断。这套组合在填模板、做报表头、加说明注释时够用了。4. 避坑与排查cfg 加载、Unicode、单元格坐标与许可证这套控件真正让人摔跤的往往不是读写逻辑而是版本协调、类型转换和工作表坐标这三类问题。我按自己踩过的顺序记录成避坑清单每一条都是现象到处理连着的。4.1 五条高频翻车记录现象一设计期拖到窗体上的控件是灰色运行后整个面板报 Access violation。原因是设计期包没装好或者工程里缺少条件编译定义。解决方法是先确认 Project Options 里的 Conditional defines 包含XLSD6_LICENSE再回到 Component Install Packages 查看设计期包是否被勾选。还不行就手工删除缓存 BPL 重装一次。现象二复制样例 cfg 到另一台电脑后打开工程提示File not found: XLSRWII.dcu。原因是 cfg 里的 Source_Dirs 用了绝对路径。解决方法是把路径改成相对路径或在.dproj的 Search Path 里重新添加源码目录。不要直接改别人机器上的绝对路径改成..\Source这类相对路径最稳。现象三中文字符写入 Excel 后变成乱码打开文件看到的是类似??的占位符。原因是字符在某个环节被转成 AnsiString而目标 xlsx 内部是 UTF-8 编码。解决方法是在写入端统一用字符串类型的属性不要手动做CharToOEM之类的转换控件在WriteToFile时会自己处理编码。如果手写 XML则必须声明encodingUTF-8。现象四写几万行数据时耗时几十秒Excel 打开还提示内容损坏。原因是每个单元格单独触发一次内部变更通知并且文件没设置压缩选项。解决方法是写入期间调用BeginUpdate全部写完后调用EndUpdate保存时打开压缩标记。这样既减少刷新次数也让生成的 xlsx 容量更小。现象五发布到服务器后提示无法定位程序输入点。原因是运行时包缺失或版本不匹配。如果你不是发布 BPL 分发就必须把编译选项设置为静态连接确认目标机器不需要额外带 XLSReadWriteII 的动态库文件。4.2 兜底手段用文件结构反推问题源头当控件报错信息很模糊时最直接的办法是绕开控件直接检查生成的 xlsx 文件内部结构。因为 xlsx 本质是一个 zip 压缩包可以把扩展名改成.zip后查看条目或者用命令行检查unzip -l Demo.xlsx正常文件里至少应该能看到xl/workbook.xml、xl/worksheets/sheet1.xml、xl/styles.xml。如果xl/worksheets/sheet1.xml缺失多半是保存时工作簿结构没有被正确建立。如果字符串都写进了xl/sharedStrings.xml但 sheet 里引用为空则说明单元格坐标写入有偏移。这个方法能快速区分是控件用法问题还是文件流问题是我的排错底线。5. 逐文件拆解样例PivotTable 与 AutoFilter 的工程写法剩下这批 cfg 文件不是摆设每个都对应一个独立主题。这里把 PivotTable、AutoFilter、MonitorFile、XLSGrid 这四类拆开讲参考它们能少走很多弯路。5.1 PivotTableSimpleSample最少字段也能生成透视表简单透视表样例演示的是最基本的数据块登记指定源数据区域声明行字段和值字段然后控件在打开时自动重建透视缓存。核心参数如下var PT: TXLSPivotTable; begin PT : XL.Sheets[0].PivotTables.Add; PT.SourceData : XL.Sheets[0].Range[A1:D100]; PT.AddRowField(品类); PT.AddColumnField(月份); PT.AddValueField(金额, xlLCFSum); end;SourceData是透视表的数据源区域需要包含表头行。AddRowField声明透视表的行分组字段AddColumnField声明列分组字段AddValueField决定汇总指标和统计方式。xlLCFSum表示求和换成xlLCFCnt变成计数。透视表样例里最容易忽略的一点是源区域不要带上整列否则透视缓存会膨胀文件体积变大打开速度明显变慢。5.2 PivotTableAdvancedSample多 Sheet 联动与汇总字段高级透视表样例在简单样例基础上增加了数据关联和多个汇总字段。做法上不再把数据源限定在同一个 sheet而是把透视表的数据源指向另一张工作表的区域然后在当前 sheet 展示结果。示意代码PT.SourceData : XL.Sheets[1].Range[A1:F200]; PT.AddValueField(数量, xlLCFSum); PT.AddValueField(金额, xlLCFSum); PT.AddPageField(区域);数据源不在当前 sheet 是合法的控件内部会建立透视缓存。AddValueField可以加多个数量、金额各一行Excel 打开后会自动排列到值区域。页码字段AddPageField对应透视表的筛选器区域常用于按区域拆分统计。这里要多看几眼生成的 xlsx 文件确认透视表缓存字段顺序和源表一致否则打开时可能提示刷新数据。5.3 SampleAutoFilter筛选不靠循环用行区域过滤AutoFilter 样例展示的是自动筛选不遍历每一行做条件判断而是把筛选条件挂在工作表上。示例代码with XL.Sheets[0].AutoFilter do begin Range : XL.Sheets[0].Range[A1:D100]; Active : True; Items[0].Criteria : 1000; end;Range声明筛选区域第一行会被视作表头。Items[0]对应第一列Criteria是筛选条件写法与 Excel 自身的筛选语法一致。需要注意Criteria设置的是自定义条件和下拉勾选值不同Excel 打开后会在该列显示漏斗图标。不是所有列都需要条件只对目标列设置即可。5.4 MonitorFileSample文件锁定与占用监控MonitorFile 样例的主题是监控 Excel 文件变化。常见做法是定时器轮询目标文件的修改时间发现变化后重新读取避免文件被占用时反复读取失败。代码示意procedure TForm1.Timer1Timer(Sender: TObject); var NewTime: TDateTime; begin NewTime : FileAgeToString(Edit1.Text); if NewTime FLastWriteTime then begin FLastWriteTime : NewTime; XL.Filename : Edit1.Text; XL.ReadFromFile; RefreshGrid; end; end;FileAgeToString返回字符串形式的修改时间和上次记录的值比较不同就重新加载。这个方法不依赖系统文件监控 API跨 Windows 版本行为一致。要注意的是文件如果正在被写入读取会触发共享冲突。这时要么捕获异常等待下一轮定时器要么先复制一份临时文件再读取。5.5 XLSGridSample把 Sheet 数据交给网格预览XLSGrid 样例不是读写器的核心功能而是把读取结果展示到界面层。它演示了控件在交互场景下的延迟加载策略只加载可视区域的单元格滚动时再补充数据避免一次拉全量几十万行。如果你做的是桌面端 Excel 查看器这个样例可以直接作为模板。它和 TStringGrid 的区别在于列类型、行高、合并单元格都保留原样适合做轻量级预览模块。6. 把大文件写成流式的最后一步BeginUpdate、缓存与压缩平时小文件怎么写都行一旦面对十万行级别的导出就要把控件当成流式写入器来用。核心技巧是把整批写入包在一个 BeginUpdate 块里结束后统一提交避免每个单元格触发一次内部事件通知。流程上是这样XL.BeginUpdate; try for Row : 1 to 100000 do begin for Col : 1 to 10 do XL.Sheets[0].Cells[Col, Row].AsFloat : DataArray[Row][Col]; end; finally XL.EndUpdate; end; XL.WriteToFile;逻辑上BeginUpdate暂停界面刷新和内部状态同步EndUpdate时统一计算。这样十万行数据的写入时间能从几十秒降到几秒代价是期间不能读取用户输入。保存时打开压缩选项生成的 xlsx 会使用 zip 压缩大小明显减小。注意EndUpdate必须放在 finally 块中否则异常时控件一直处于更新挂起状态后续读文件会表现异常。验证时可以做一层额外检查生成后用压缩软件打开 xlsx查看xl/worksheets/sheet1.xml里row节点的数量和预期行数一致同时确认xl/sharedStrings.xml里字符串分组正常。坚持这个检查能发现大部分隐藏的坐标偏移问题。从那以后我每次交付报表模块前都强制走一遍“小文件能读、大文件能写、图表能刷新”三步自测再没出现过导出后用户反馈 Excel 打不开的情况。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?