简介:这份文档面向 PowerBuilder 开发者,尤其是需要提升 DataWindow 实战能力的中级程序员,系统整理了 38 个实用技巧,覆盖数据展示、交互优化与性能调优等常见开发痛点。资源为单个 doc 文档,压缩包约 49KB,内容紧凑、便于随查随用。文档从列属性控制入手,讲解如何让 DataWindow 列只能追加不能修改、实现列数据自动折行、利用内置定时器制作动画效果,并针对选中行外观单调的问题给出单行与多行选择的背景色表达式方案。此外还涉及动态修改属性、条件格式化、隐藏列、自定义列宽、排序、计算字段、过滤数据、嵌套 DataWindow、分组汇总、打印导出、行状态判断、缓存优化与实时校验等技巧,基本覆盖日常开发高频场景。已有 403 人学习,适合希望快速查阅技巧、优化数据界面交互与提升应用质量的开发者参考。
1. PB DataWindow 38个技巧,非常实用的!
做 PowerBuilder 维护的同行大概都有同感:一个跑了十几年的老系统,界面土、逻辑绕,但业务部门就是不让换,因为里面沉淀了太多只有这套系统才跑得通的报表和录入逻辑。而撑起这些界面的核心控件,几乎全是 DataWindow。很多人对它的印象还停留在“拖一个 dw 上去、配个 SQL 就能显示”,可真到改需求的时候才发现,同一张报表,别人两小时收工,你折腾两天还在跟检索参数较劲。差别不在语法,而在对 DataWindow 那些“隐藏开关”的熟悉程度。这篇笔记把我在实际维护中反复用到的 38 个技巧按场景拆开讲,覆盖数据检索、显示格式、更新控制、打印导出和性能排查,新手能照着改,熟手能对照检查自己漏了哪些参数。
2. 数据检索与参数:让 DataWindow 少跑冤枉路
DataWindow 的性能问题,八成出在检索环节。很多人习惯把 SQL 直接写进 dw 的 select 里,运行时全量拉回来再在客户端过滤,数据量小的时候看不出问题,一旦表上了几十万行,打开窗口就要等好几秒。这一章先把检索相关的技巧讲透,后面再谈显示和更新。
2.1 用 Retrieve 参数替代客户端过滤
最常见的翻车场景:窗口打开时dw_1.Retrieve()不带参数,把整张表拉回来,然后在代码里循环判断某列的值来决定显示哪些行。正确做法是把过滤条件下推到 SQL 的 where 子句,通过 Retrieve 参数传入。
// 在 DataWindow 画板中定义检索参数,例如 as_dept_id // 脚本中调用时传入实际值 string ls_dept ls_dept = sle_dept.text dw_1.SetTransObject(sqlca) dw_1.Retrieve(ls_dept)逻辑说明:DataWindow 的 Retrieve 参数会替换 SQL 中对应的检索参数占位符,数据库只返回匹配的行。参数说明:SetTransObject必须在Retrieve之前调用,否则事务对象为空会报数据库连接错误;如果窗口用了共享事务对象,注意SetTransObject和SetTrans的区别,前者不自动提交,后者会在检索后自动提交,容易引发锁表。
2.2 动态修改 SQL 的 Where 子句
有些报表的查询条件是可选的,用户勾了部门就按部门过滤,没勾就查全部。这时候硬编码多个 dw 不现实,可以用Modify动态改 where。
string ls_where ls_where = "emp.dept_id = :as_dept" if sle_dept.text = "" then ls_where = "1 = 1" // 不加条件时用恒真表达式占位 end if dw_1.Modify("DataWindow.Table.Select = ~"SELECT emp_id, emp_name FROM employee WHERE " + ls_where + "~"") dw_1.Retrieve()逻辑说明:Modify直接改 DataWindow 对象的 SQL 定义,改完必须重新 Retrieve 才生效。参数说明:字符串里的双引号要用~"转义;如果 where 里用了检索参数,记得在 Modify 之后重新设置参数值,否则会提示参数未赋值。
2.3 检索行数限制与进度提示
大表检索时用户不知道程序在干什么,容易重复点击按钮。可以在检索前设置最大行数,并配合进度条。
dw_1.SetRedraw(false) // 检索期间关闭重绘,减少闪烁 dw_1.Retrieve() dw_1.SetRedraw(true)逻辑说明:SetRedraw(false)在批量操作时能明显提升响应速度,但必须在操作完成后恢复为 true,否则界面会一直不刷新。参数说明:如果检索时间超过几秒,建议在窗口上放一个静态文本显示“正在查询…”,检索前 Visible=true,检索后 Visible=false。
2.4 共享数据窗口减少重复查询
同一个数据在多个 dw 中显示时,不要每个都去查数据库。用ShareData让它们共享同一份结果集。
dw_2.ShareData(dw_1) dw_2.ShareData(dw_1) // 再次调用可解除共享逻辑说明:共享后两个 dw 的数据完全一致,修改其中一个另一个也会变。参数说明:共享要求两个 dw 的列结构完全一致,否则运行时会报错;解除共享用再次调用ShareData并传入原 dw 的方式,具体行为取决于 PB 版本,建议在共享前先确认业务是否真的需要联动。
3. 显示格式与交互:让报表看起来“专业”
数据查出来了,但显示得乱七八糟,用户照样不买账。日期显示成 20240101、金额没有千分位、状态码显示 0 和 1,这些细节直接影响使用体验。这一章讲显示格式和交互控制的技巧。
3.1 用 DisplayFormat 统一日期和金额格式
在 DataWindow 画板中选中列,在 Format 标签页设置 DisplayFormat。日期常用yyyy-mm-dd,金额常用#,##0.00。
// 也可以在脚本中动态设置 dw_1.Modify("emp_salary.Format = '~"#,##0.00~"'")逻辑说明:DisplayFormat 只影响显示,不影响实际存储的值。参数说明:如果列本身是 string 类型但存的是数字,需要先用String函数转换再设置格式,否则格式不生效。
3.2 条件显示不同颜色
根据某列的值改变文字颜色,比如金额为负显示红色。
// 在列的 Font 属性中设置表达式 // 表达式示例:if(salary < 0, rgb(255,0,0), rgb(0,0,0))逻辑说明:DataWindow 的属性支持表达式,运行时根据当前行的值动态计算。参数说明:表达式里引用列名直接用列名即可,不要加引号;如果表达式复杂,建议先在画板里测试,避免运行时才发现语法错误。
3.3 用 Code Table 替代硬编码下拉
性别、状态这类固定选项,不要在每个窗口里写 if-else,用 Code Table 统一维护。
// 在列的 Edit 风格中选择 DropDownListBox // 在 Code Table 中定义显示值和实际值 // 例如:显示“男”对应值 M,显示“女”对应值 F逻辑说明:Code Table 把显示文本和实际存储值分离,数据库里存 M/F,界面上显示男女。参数说明:Code Table 定义在 DataWindow 对象里,如果多个 dw 共用同一套编码,建议做成外部文件或数据库表,避免改一处漏一处。
3.4 行焦点与选中效果
用户点击某一行时要有明显的视觉反馈,否则不知道当前操作的是哪条记录。
// 在 DataWindow 的 RowFocusChanged 事件中 dw_1.SelectRow(0, false) // 先清除所有选中 dw_1.SelectRow(currentrow, true) // 选中当前行逻辑说明:SelectRow控制行的选中状态,配合 dw 的选中背景色设置,能实现整行高亮。参数说明:currentrow是系统变量,表示当前行号;如果 dw 允许行内编辑,注意选中行和编辑焦点的区别,避免用户以为选中了就能直接改。
4. 更新控制与并发:别让多人同时改崩数据
DataWindow 的更新机制是它最强大的地方,也是最容易出问题的地方。默认情况下,PB 会根据你设置的 Update 属性生成 insert/update/delete 语句,但如果主键、更新列、并发检测没配对,轻则更新失败,重则覆盖别人的修改。
4.1 正确设置 Update 属性
在 DataWindow 画板的 Rows 菜单里选 Update Properties,指定可更新表、主键列和可更新列。
// 脚本中检查更新是否成功 if dw_1.Update() = 1 then Commit; else Rollback; MessageBox("提示", "更新失败") end if逻辑说明:Update()返回 1 表示成功,-1 表示失败。参数说明:如果 dw 涉及多表连接,必须指定哪张表可更新,否则 Update 会报错;主键列必须勾选,否则 PB 无法定位要更新的行。
4.2 用 Update Where Clause 控制并发
默认情况下,PB 用主键作为 where 条件。如果两个人同时打开同一条记录,A 改了保存,B 再保存时会覆盖 A 的修改。可以在 Update Properties 里把 where 条件改成包含所有可更新列的原始值。
// 在 Update Properties 中选择“Key and Modified Columns” // 或“Key and Updatable Columns”逻辑说明:选择更严格的 where 条件后,如果记录已被别人修改,Update 会返回 0 行受影响,PB 据此判断为并发冲突。参数说明:这种方式会增加 where 子句的长度,对性能有轻微影响,但能有效防止覆盖;如果业务允许后写覆盖,用默认的主键条件即可。
4.3 处理 Update 失败后的回滚
更新失败时不能只弹个框就完事,要把数据恢复到修改前的状态。
if dw_1.Update() = 1 then Commit; else Rollback; dw_1.Retrieve() // 重新检索,丢弃未保存的修改 end if逻辑说明:Rollback撤销数据库层面的事务,Retrieve重新拉取数据,把 dw 里的缓冲区也重置。参数说明:如果用户输入了很多内容,直接 Retrieve 会丢失,建议先提示用户是否放弃修改;更友好的做法是用dw_1.ResetUpdate()清除更新标志,让用户继续编辑。
4.4 批量更新时的提交策略
循环更新多条记录时,不要每改一条就 Commit 一次,那样事务太碎,性能差还容易锁表。
// 循环修改数据 for ll_i = 1 to dw_1.RowCount() dw_1.SetItem(ll_i, "status", "A") next // 统一更新 if dw_1.Update() = 1 then Commit else Rollback逻辑说明:所有修改先在客户端缓冲区完成,最后一次性提交。参数说明:如果数据量特别大,比如上万行,建议分批 Update,每 500 行提交一次,避免事务日志过大。
5. 打印与导出:把数据送到该去的地方
DataWindow 的打印和导出功能很全,但默认设置往往不符合实际需求。比如打印出来没有页眉页脚、导出 Excel 后格式全乱、PDF 里中文变方块,这些问题都有对应的解法。
5.1 用 Print 函数控制打印范围
不要直接调Print()无参版本,那样会打印全部数据。可以指定打印哪些页。
dw_1.Print(false, 1, 3) // 打印第1到第3页逻辑说明:第一个参数表示是否显示打印对话框,后面两个参数是起始页和结束页。参数说明:如果 dw 数据量很大,建议先Preview()让用户确认,再决定打印范围。
5.2 导出 Excel 时保留格式
用SaveAs导出 Excel 时,默认只导出数据,格式会丢。可以在导出前设置 dw 的导出属性。
dw_1.SaveAs("c:\temp\report.xls", Excel!, true)逻辑说明:第三个参数 true 表示包含列标题。参数说明:Excel! 是 PB 内置的导出格式枚举,不同版本可能支持 xlsx 需要额外配置;如果导出后中文乱码,检查系统区域设置和 PB 版本的兼容性。
5.3 打印时自动分页与页眉页脚
在 DataWindow 画板中设置页眉页脚区域,放入页码、日期等表达式。
// 在页眉中插入计算列 // 表达式:Page() + " / " + PageCount()逻辑说明:Page()返回当前页码,PageCount()返回总页数。参数说明:这两个函数只在打印预览和打印时有效,在普通显示模式下返回 0;如果页眉内容需要动态变化,可以在 Print 前用 Modify 修改。
5.4 导出 CSV 时的分隔符处理
导出 CSV 时,如果字段内容本身包含逗号,会导致列错位。
dw_1.SaveAs("c:\temp\data.csv", CSV!, true, ",")逻辑说明:第四个参数指定分隔符,PB 会自动对包含分隔符的字段加引号。参数说明:如果目标系统要求用制表符分隔,把最后一个参数改成~t;导出前最好先检查数据里有没有换行符,有的话需要额外处理。
6. 避坑与排查:那些年我们踩过的 DataWindow 坑
这一章记录几个高频问题,每个都按“现象 → 原因 → 解决”写,方便对照排查。
6.1 检索后数据不显示,但 RowCount 有值
现象:dw_1.Retrieve()返回了行数,但界面上看不到任何数据。原因:dw 的 Visible 属性为 false,或者列宽被设置为 0,或者数据窗口的 Band 高度不够。解决:检查 dw 的 Visible 属性,检查每列的 Width 是否大于 0,检查 Detail Band 的高度是否被意外拖成了 0。
6.2 Update 返回 -1 但看不出哪里错
现象:dw_1.Update()返回 -1,但错误信息很模糊。原因:可能是主键未设置、可更新列未勾选、或者数据库约束冲突。解决:在 Update 前调用dw_1.SetTransObject(sqlca)确保事务对象正确;用dw_1.GetSQLPreview()查看实际生成的 SQL;检查数据库端的触发器或约束是否拒绝了更新。
6.3 共享数据窗口后修改不同步
现象:dw_2.ShareData(dw_1)后,修改 dw_1 的数据,dw_2 没变化。原因:共享的是数据缓冲区,但显示格式和排序是各自独立的。解决:如果希望完全同步,两个 dw 的对象结构必须完全一致;如果只是数据同步,共享后调用dw_2.SetRedraw(true)强制刷新。
6.4 打印预览时中文显示为方块
现象:打印预览或导出 PDF 时,中文变成方块或问号。原因:打印机驱动或 PDF 虚拟打印机不支持当前字体。解决:在 DataWindow 画板中把字体改成系统自带的中文字体,如宋体或黑体;如果导出 PDF,检查 PDF 虚拟打印机的字体设置。
6.5 动态 Modify 后 Retrieve 报参数错误
现象:用 Modify 改了 SQL 后,Retrieve 提示“参数未赋值”。原因:Modify 修改了 SQL 定义,但检索参数列表没有同步更新。解决:在 Modify 之后重新设置检索参数,或者改用dw_1.SetSQLSelect()方法,它会自动处理参数映射。
7. 进阶技巧:用 Describe 和 Modify 做动态报表
前面讲的都是相对固定的用法,真正让 DataWindow 灵活起来的是Describe和Modify这对组合。Describe读取对象的属性,Modify修改属性,两者配合可以在运行时动态调整报表的几乎任何部分。我一般用它们来做“用户自定义列”功能:让用户勾选需要显示的列,程序根据勾选结果动态设置列的 Visible 和 Width。
// 读取当前列的可见性 string ls_visible ls_visible = dw_1.Describe("emp_name.Visible") // 根据用户选择设置可见性 if cbx_name.checked then dw_1.Modify("emp_name.Visible = '1'") else dw_1.Modify("emp_name.Visible = '0'") end if逻辑说明:Describe返回字符串形式的属性值,Modify接受字符串形式的赋值语句。参数说明:属性名大小写不敏感,但列名必须和 dw 中定义的一致;如果列名包含特殊字符,需要用引号括起来。
另一个实用技巧是用Describe获取 dw 的 SQL 语句,方便调试。
string ls_sql ls_sql = dw_1.Describe("DataWindow.Table.Select") MessageBox("当前SQL", ls_sql)逻辑说明:这能帮你确认运行时实际执行的 SQL 是什么,尤其是在用了 Modify 动态改 SQL 之后。参数说明:返回的 SQL 可能包含检索参数占位符,实际执行时会被替换成具体值。
还有一个我常用的技巧:用Modify动态添加计算列。比如用户临时想按某个公式排序或显示,不需要改 dw 对象,直接在运行时加。
dw_1.Modify("Create compute_1 as integer") dw_1.Modify("compute_1.Expression = 'salary * 12'")逻辑说明:Create动态创建一个计算列,Expression设置计算公式。参数说明:计算列的类型要和表达式结果匹配;创建后需要重新 Retrieve 或 SetRedraw 才能看到效果。
这些技巧单独看都不复杂,但组合起来能解决很多“看起来要改代码”的需求。我自己的习惯是:遇到需要动态调整界面的需求,先想能不能用 Describe/Modify 搞定,实在不行再改 dw 对象。这样改出来的程序灵活,用户也满意。希望帮到你。
本文还有配套的精品资源,点击获取