尧图精选

Delphi集成ECharts:用TEdgeBrowser实现桌面应用可视化图表

🕒 发布时间:2026/9/9 13:30:12 📁 来源:尧图网络
简介面向Delphi开发者的实际示例演示如何在Windows应用中调用百度ECharts库为传统桌面程序补充现代化数据可视化能力。ECharts作为开源JavaScript图表库支持折线图、柱状图、饼图等多种常见类型且交互性强本示例正好展示这一集成过程。压缩包共9个文件涵盖Pascal源代码、DFM表单、项目配置、演示图片及可执行程序文件类型紧凑整体仅904KB便于快速下载研究。项目通过WebBrowser组件加载HTML与JavaScript完整呈现从引入ECharts脚本、配置图表到动态更新数据的实现思路同时演示了如何执行JavaScript代码、监听BeforeNavigate2事件处理用户交互以及如何组织HTM容器与Pascal逻辑。已有787人学习参考适合需在Delphi中快速集成图表或了解桌面程序与Web技术跨语言调用的中高级开发者。1. 先想清楚为什么是 ECharts而不是原生控件Delphi 开发 Windows 桌面程序这么多年最让人头疼的其实不是业务逻辑而是界面审美。尤其是图表这块很多项目做出来功能一点问题没有但一放到大屏上丑得让人不好意思演示。早期我用 VCL 自带的 TChart、TeeChart柱状图、折线图、饼图都能画基本功能齐全但交互细节和视觉风格停留在“报表工具”层面做内部管理系统没问题想做出互联网产品那种渐变、动画、联动钻取的效果基本要自己写一堆 GDI/GDI 代码工作量很大还不一定好看。后来我把方向转到了前端生态。ECharts 是百度开源的一个 JavaScript 图表库图表类型覆盖折线、柱状、饼图、散点、地图、树图、3D 可视化等官方文档和示例都很全。对于 Delphi 开发者来说不需要懂前端框架只要会用浏览器控件加载一个 HTML 页面然后通过接口把数据传进去剩下的事情全部交给 ECharts。这样做还有个额外好处图表样式改起来非常快。前端同事调 CSS、调 series 配置Delphi 这边完全不用动只需要保证数据接口稳定。项目交接的时候前端把图表配置做好Delphi 这边封装成一个通用的“图表容器”两边各干各的活互不干扰。也就是说这套方案的本质是一个分工问题Delphi 负责数据采集、业务逻辑、交互入口浏览器控件负责展示和渲染ECharts 负责把数据变成图表。这三层各管一段替换成本都很低。1.1 传统 Delphi 图表的痛点功能没问题颜值跟不上TChart 在 Delphi 里的地位不用多说很多老项目从 D7 一路用到现在的 Delphi 11/12报表打印、简单统计都靠它。但它有几个绕不过去的问题。第一是样式陈旧。配色、字体、坐标轴刻度、提示框全部要手动调整调出来的效果还是有种“上世纪软件”的感觉。第二是交互受限。鼠标悬浮显示提示、点击图例联动、数据缩放这类操作TChart 要么不支持要么实现起来非常复杂。第三是扩展成本高。想做一个类似“中国地图按省份着色”的效果TChart 完全做不到需要引入第三方地图控件绕来绕去很麻烦。ECharts 在 Web 技术里属于上手最快的那一类。它不是框架只是一个带配置项的图表库。你给它一个 DOM 容器、一份 option 配置对象它自己负责渲染和更新。对 Delphi 开发者来说最友好的地方在于不需要写 React、Vue直接拿 HTML JavaScript 就能用跟写脚本差不多。我记得最早第一次成功跑通“Delphi 按钮点击ECharts 饼图切换数据”的时候旁边同事看到界面效果第一反应是“你这是嵌了个网页吧”我说对但用户不关心底层是什么他们的直观感受就是这个客户端“很现代”。1.2 浏览器控件三选一TWebBrowser、TEdgeBrowser、CEFDelphi 里能加载网页的控件主要有三个我一个个说大家根据项目情况选。第一个是 TWebBrowserDelphi 自带的老牌控件封装的是 IE 内核。优点是不用装任何运行时控件拖上去就能用。缺点是 IE 内核早就停止更新对现代 JavaScript 的支持很弱。ECharts 4 勉强能跑ECharts 5 的很多渲染特性在 IE 内核下会出问题动画卡顿、文字模糊、Canvas 渲染异常。如果项目还是在 Delphi 7 或者 Delphi XE 的老工程上用 TWebBrowser 加 ECharts 4 是可以接受的但我不建议新项目这么干。第二个是 TEdgeBrowser从 Delphi 11 开始官方原生支持底层是微软的 WebView2 运行时基于 Chromium 内核。它和 TWebBrowser 的用法非常像但渲染能力跟现代 Chrome 基本一致ECharts 5 跑起来完全没问题。缺点是目标机器上需要安装 WebView2 Runtime不过 Windows 10/11 系统基本都自带老系统单独装一下也就一两分钟的事情。第三个是 CEF也就是 Chromium Embedded Framework通过第三方组件库比如 DCEF3集成到 Delphi 里。优点是内核版本完全自己控制不受系统环境限制离线打包和深度定制都方便。缺点是库文件体积很大解压出来上百 MB而且升级维护比较麻烦。一般只有重度依赖网页交互、必须精确定制浏览器行为的项目才需要上 CEF。1.3 我的选型结论我的 Demo 用的是 TEdgeBrowser ECharts 5 本地文件。选择理由很直接TEdgeBrowser 是 Delphi 11 以后的官方原生组件代码写起来跟 TWebBrowser 差不多但性能和兼容性好太多ECharts 本地化是为了保证离线环境可用不依赖外网 CDN毕竟很多 Delphi 项目跑在企业内网机器不一定能访问外部网络资源。有一点要提醒目标机器上如果 WebView2 Runtime 缺失TEdgeBrowser 运行时会直接报错页面加载不出来。部署程序时最好加一个检测逻辑发现没装就引导用户下载安装。2. 搭一个最小可跑工程这个 Demo 我用四个文件来描述一个 Delphi 窗体单元一个 HTML 模板文件一个 ECharts 库文件一个和窗体同级的文件夹来放 HTML 和库文件。2.1 准备 ECharts 本地库去 ECharts 官网下载发行版我用的版本是 5.4.3体积大约 1 MB。选择完整版就行因为含了所有图表类型和组件。有些项目想瘦身可以用按需构建的方式但 Demo 阶段没必要直接完整版最省事。文件放的位置要注意不要放在 Delphi 的工程目录以外的深层路径建议建一个相对稳定的目录比如工程目录下的web文件夹项目根目录/ ├── Project1.dpr ├── Unit1.pas └── web/ ├── chart.html └── echarts.min.js用相对路径或者绝对路径都行但要保证运行时能找到。HTML 里引用 ECharts 的路径在文件协议下可以直接写相对路径script srcecharts.min.js/script如果你用绝对路径打开 HTML记得用file:///协议比如file:///D:/Project/web/chart.html直接在浏览器里双击打开也可以。2.2 窗体与控件初始化在 Delphi 窗体上放一个 TEdgeBrowser 控件一个 TButton一个 TComboBox。TEdgeBrowser 的 Align 属性设为alClient让网页区域铺满窗体主体。TEdgeBrowser 在运行时要注意一点它的Navigate方法里传入的如果是本地文件路径需要转成file:///协议格式。我一般写一个辅助函数function LocalUrl(const AFileName: string): string; begin Result : file:/// StringReplace(AFileName, \, /, [rfReplaceAll]); end;窗体创建时加载页面procedure TForm1.FormCreate(Sender: TObject); begin EdgeBrowser1.Navigate(LocalUrl(ExtractFilePath(Application.ExeName) web\chart.html)); end;这里用Application.ExeName来定位 HTML 文件前提是发布时把web文件夹一起拷贝到 exe 同目录。如果你在 IDE 里调试路径可能不对可以直接写死绝对路径等调试通过后再改回来。2.3 写一份能被调用的 HTML 模板HTML 模板是整条链路的核心先贴一份最简的!DOCTYPE html html head meta charsetutf-8 titleECharts Demo/title script srcecharts.min.js/script style html, body { width: 100%; height: 100%; margin: 0; } #chart { width: 100%; height: 100%; } /style /head body div idchart/div script var chart echarts.init(document.getElementById(chart)); window.setChartData function(data) { var option { tooltip: { trigger: item }, legend: { bottom: 0% }, series: [{ type: pie, radius: [40%, 70%], data: data }] }; chart.setOption(option, true); }; window.resizeChart function() { chart.resize(); }; if (window.chrome window.chrome.webview) { window.chrome.webview.postMessage(pageReady); } /script /body /html这里有几个关键点。echarts.init必须在 DOM 已经渲染完成后执行所以脚本放在/body之前。setChartData暴露成全局函数Delphi 端直接调用。chart.setOption(option, true)的第二个参数true表示 notMerge也就是每次调用都会清空旧数据重新渲染这样更新数据时不会出现残留。resizeChart是为了后面让图表跟随窗口大小变化。最后一行用window.chrome.webview.postMessage通知 Delphi 页面已经加载完成这个属于 WebView2 的通信机制后面会详细讲。3. Delphi 和 JS 的双向通信才是核心很多人第一次做这种集成最懵的就是 Delphi 怎么调用网页里的函数网页又如何把事件传回 Delphi。搞清楚这两个方向的通信整个 Demo 就通了。3.1 Delphi 主动调 JSExecuteScriptTEdgeBrowser 执行 JavaScript 的方法是ExecuteScript它的参数就是一段 JavaScript 字符串。比如上面 HTML 里暴露了全局函数setChartDataDelphi 端可以这样调用procedure TForm1.btnShowChartClick(Sender: TObject); var jsonStr: string; begin jsonStr : [{name:直接访问,value:335},{name:邮件营销,value:310},{name:联盟广告,value:234},{name:视频广告,value:135}]; EdgeBrowser1.ExecuteScript(Format(setChartData(%s), [jsonStr])); end;注意这里我传的是setChartData(...)括号里直接放 JSON 文本而不是setChartData(...)这种带引号的形式。因为 JSON 本身已经是一个合法的 JavaScript 对象字面量直接展开传参最安全省去转义麻烦。如果数据量很大或者 JSON 里有特殊字符比如字符串里包含换行、反斜杠直接拼字符串还是会有风险。更稳妥的做法是让 Delphi 端先做 JSON 序列化再调用时把整个字符串用JSON.stringify包一层不过 Demo 级别不需要搞这么复杂。实测下来结构化数据用Format直接拼对象字面量只要 JSON 本身是标准格式几乎不会出问题。执行完 JS 之后如果需要知道执行结果ExecuteScript有异步回调可以通过TWebView2ExecuteScriptCallback接收返回值。比如可以往 HTML 里写一个return ok然后在回调里处理。3.2 JS 主动调 DelphiWebMessageReceived反过来网页里的 JavaScript 要通知 Delphi用的是 WebView2 的postMessage机制。HTML 里写window.chrome.webview.postMessage(pageReady);Delphi 端在 TEdgeBrowser 的OnWebMessageReceived事件里接收procedure TForm1.EdgeBrowser1WebMessageReceived(Sender: TObject; const AWebMessage: string; const AArgs: TWebMessageArgs); begin if AWebMessage pageReady then Memo1.Lines.Add(页面加载完成可以开始更新图表); end;这个通信方向非常适合做“页面就绪通知”。因为 WebView2 加载本地 HTML 是异步的如果不等待页面完全加载就去调用 JSExecuteScript会执行失败或者找不到函数。我通常在页面里写postMessage(pageReady)Delphi 端收到后置一个标志位后续按钮才允许触发图表更新。如果通信的数据不是简单字符串而是结构化数据同样可以传 JSON。比如页面上有个下拉筛选器用户切换了某个条件JS 把选择结果postMessage给 DelphiDelphi 根据这个条件去查数据库再把新数据传回页面渲染。这就是一个完整的双向联动闭环。3.3 数据传递的转义问题必踩我发现很多人在“Delphi 拼 JS 参数”这个环节翻车出错的原因基本都是引号嵌套和转义。Delphi 字符串用单引号JS 字符串用单引号或双引号JSON 里字符串用双引号。三层叠加最容易出现的问题是Delphi 字符串里的反斜杠、换行符、单引号被 JS 端错误解析。我的经验总结成三条如果只是传简单数组或对象直接把 JSON 文本作为对象字面量传入不要在外层加引号也就是上面setChartData(%s)这种写法。如果 JSON 里有用户输入的内容包含引号、换行必须先用TJsonSerializer或TJsonObject.ToJSON做序列化确保生成合法 JSON再传给网页。如果实在要传字符串参数在 JS 端统一用JSON.parse接收这样传的时候只需要做一层转义调试起来省很多事。另外Delphi 的字符串是 AnsiString 和 UnicodeString 并存的老底子拼接中文内容时要注意编码问题。HTML 头里声明了charsetutf-8ECharts 显示中文标题、图例就不会乱码。但如果你在 Delphi 端拼 JSON 时用了本地代码页中招的几率会大很多。最保险的做法是用 UTF-8 编码构造 JSON 字符串。4. 完整 Demo一个能切饼图/柱状图的小工具前面讲了原理这一步我直接还原一个可运行的示例代码不复杂但流程完整界面加载页面页面发送就绪消息Delphi 收到后根据用户选择的数据生成 JSON调用 JS 刷新图表。4.1 界面布局与事件绑定窗体上放这些控件TEdgeBrowserAlign 为alClient占满剩余空间。顶部一个 TPanel里面放 TComboBox选择图表类型和 TButton刷新图表。侧面或底部放一个 TMemo用来显示调试日志和 JS 回调消息。初始化代码procedure TForm1.FormCreate(Sender: TObject); begin ComboBox1.Items.Add(饼图); ComboBox1.Items.Add(柱状图); ComboBox1.ItemIndex : 0; EdgeBrowser1.Navigate(LocalUrl(ExtractFilePath(Application.ExeName) web\chart.html)); end;等待OnWebMessageReceived收到pageReady然后才允许按钮点击procedure TForm1.EdgeBrowser1WebMessageReceived(Sender: TObject; const AWebMessage: string; const AArgs: TWebMessageArgs); begin Memo1.Lines.Add(JS 消息: AWebMessage); if AWebMessage pageReady then FPageReady : True; end;FPageReady是窗体私有布尔字段按钮点击前先判断如果还没就绪就提示等待。4.2 传递数据与刷新图表HTML 模板里我把setChartData函数改得更通用一点增加一个chartType参数用来切换图表类型script var chart echarts.init(document.getElementById(chart)); window.setChartData function(chartType, data) { var option { tooltip: {}, legend: { bottom: 0% }, series: [] }; if (chartType pie) { option.series [{ type: pie, radius: [40%, 70%], data: data }]; } else if (chartType bar) { option.series [{ type: bar, data: data.map(function(item) { return item.value; }) }]; option.xAxis { data: data.map(function(item) { return item.name; }) }; option.yAxis {}; } chart.setOption(option, true); }; /script在柱状图分支里我直接复用了{name, value}结构的数据通过map拆出xAxis和series.data这样 Delphi 端不用为不同图表类型构造不同格式的数据结构所有类型统一用同一种 JSON。Delphi 端按钮代码如下procedure TForm1.btnRefreshClick(Sender: TObject); var jsonStr: string; chartType: string; begin if not FPageReady then begin Memo1.Lines.Add(页面还没准备好稍后再试); Exit; end; if ComboBox1.ItemIndex 0 then chartType : pie else chartType : bar; jsonStr : [{name:分类A,value:335},{name:分类B,value:310},{name:分类C,value:234}]; EdgeBrowser1.ExecuteScript(Format(setChartData(%s, %s), [chartType, jsonStr])); end;这段代码的Format里第一参数用%s这是 Delphi 里表示单引号的方式生成到 JS 里就是setChartData(pie, [{...}])。执行后网页里的chart.setOption会被触发图表立即刷新。4.3 窗口缩放自适应与导出图片一个常见细节是窗口大小变化后图表还是原来那个尺寸导致四周空白或者溢出。ECharts 提供了resize()方法Delphi 端在窗体的OnResize事件里调用 JSprocedure TForm1.FormResize(Sender: TObject); begin if FPageReady then EdgeBrowser1.ExecuteScript(resizeChart()); end;HTML 里resizeChart就是chart.resize()。实测下来WebView2 的resize事件在最大化、还原、拖动调整大小时都会触发但要注意执行频率窗口拖动过程中可能会连续触发几十次。如果图表数据量大渲染有压力可以在 Delphi 端加一个 200ms 的定时器做防抖。导出图片也是一个很实用的能力。ECharts 自带getDataURL()方法可以拿到当前图表的 base64 PNG 数据Delphi 端调用后解析这个 base64 字符串保存为图片文件就能实现“一键截图”功能不需要扫码或用系统截图工具。window.exportChart function() { return chart.getDataURL({ type: png, pixelRatio: 2, backgroundColor: #fff }); };Delphi 端通过ExecuteScript的回调函数拿到返回值然后截取base64,之后的字符串用TNetEncoding.Base64.Decode转成TBytesStream最后SaveToFile。这个功能适合用来做定时报表推送程序每天自动生成图表截图配合邮件组件直接发出去。5. 实际操作中的常见问题与排查记录这套方案踩过的坑不算少我把最有代表性的几个问题整理出来这些都是网上文档不常写的。5.1 页面白屏 / WebView2 运行时缺失现象窗体上 EdgeBrowser 区域一片空白没有任何报错或者弹出找不到 WebView2 的提示。排查步骤先看代码里Navigate是否执行成功。如果第一次加载本地 HTML 就白屏大概率是 WebView2 Runtime 没装。可以到系统的“设置 - 应用”里搜 WebView2没搜到就去微软官网下载 Evergreen 版本安装。还有一种情况是 HTML 文件路径不对。用Application.ExeName获取路径时注意 IDE 调试时 exe 在Win32\Debug目录而你的web文件夹如果在工程根目录路径就对不上。调试期可以直接写死绝对路径或者用TDirectory.GetCurrentDirectory结合断点观察实际路径。如果 HTML 加载了但图表区域还是白的打开 WebView2 的开发者工具看控制台报错。右键页面选择“检查”在 Console 里能看到echarts is not defined之类的错误这种一般是 ECharts 文件路径引用错了检查 HTML 里的scriptsrc。5.2 图表不刷新或者 setOption 报错现象第一次调用setChartData能出图第二次调用同样的代码图表数据没变化。原因基本是setOption的合并机制。ECharts 默认是 merge 模式新数据会跟旧配置合并不是完全覆盖。我上面的代码统一传了true作为第二参数强制走 notMerge 模式每次都是全新渲染避免脏数据残留。还有一个坑传给setChartData的 JSON 如果格式不合法比如多了一个逗号、少了括号ExecuteScript执行后会直接抛 JS 异常但 Delphi 端不会报错。排查时在 HTML 里包一层try...catch把错误postMessage给 Delphi这样调试效率高很多。window.setChartData function(chartType, data) { try { // 图表配置逻辑 } catch (e) { if (window.chrome window.chrome.webview) { window.chrome.webview.postMessage(error: e.message); } } };Delphi 端OnWebMessageReceived里统一打印这样不管是页面就绪还是 JS 报错都能在 Memo 里看到。5.3 本地 HTML 引用 ECharts 的路径问题如果开发机和部署机的目录结构不一致HTML 里写死的相对路径可能会失效。最保险的做法是把 HTML 内容和 ECharts 文件都生成到 exe 同目录的固定子目录下部署时连同 exe 一起拷贝。不要依赖注册表、环境变量那些在客户端机器上不可控。还有一点ECharts 地图类的功能需要额外加载地图 JSON 数据。如果你后面要做中国地图、世界地图记得把 map JSON 文件也放到本地目录并在 HTML 里通过fetch加载。这个在 ECharts 5 里是通过registerMap注册的跟普通图表数据是两条线别混在一起。5.4 发布部署时的内存与性能观察TEdgeBrowser 本质上跑了一个完整的 Chromium 进程内存占用比普通 VCL 控件高这在项目初期会被一些老 Delphi 开发者质疑。实际上现代客户端软件大多数都接受了这个趋势只要不在一个窗体里开几十个 WebView2 实例内存完全可控。一个页面常驻占 100~200 MB 属于正常水平关闭窗体时记得释放浏览器资源。如果程序里同时用到多个页面建议复用同一个 EdgeBrowser用Navigate切换不同本地 HTML而不是开多个实例。页面切换前确保不再需要旧页面的状态因为重新导航之后之前的全局 JS 变量和图表实例都会销毁。最后再说一点我的实际体会做这个 Demo 之前我总想着把所有图表功能都用 Delphi 原生方案实现费了半天劲效果还是差强人意。改用 ECharts 之后前端图表配置能力瞬间提升了几个量级而且因为页面和数据分离后续调整样式、增加图表类型基本不用动 Delphi 代码。如果你正被 Delphi 项目的图表展示问题卡住按这个 Demo 的思路先跑通最小闭环EdgeBrowser 加载 HTMLDelphi 拼 JSON 传数据JS 更新图表。剩下的比如地图、3D 可视化、大屏联动都是在这个闭环上做增量。还有个小技巧ECharts 官方示例网站的代码可以直接复制到你的 HTML 模板里改一改用它来验证 Delphi 传的数据是否正常比从零手写配置快很多。这点对不熟悉前端的人来说特别省力。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →