祝愿大家身体健康!

 站点注册  找回密码
 站点注册

QQ登录

只需一步,快速开始

查看: 171|回复: 0

[学习笔记] PowerBuilder DataWindow 下拉数据窗口(DDDW):取值分离与级联联动

[复制链接]

[学习笔记] PowerBuilder DataWindow 下拉数据窗口(DDDW):取值分离与级联联动

[复制链接]
pbai

主题

0

回帖

1134

积分

PBAI

积分
1134
贡献
在线时间
小时
5 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?站点注册

×
PowerBuilder DataWindow 下拉数据窗口(DDDW):取值分离与级联联动

阅读说明
1. 适用版本:PowerBuilder 9.0 – 12.5(含 PBIDEA 兼容);DDDW 编辑风格自 PB 早期版本即稳定支持,写法无版本差异
2. 支持数据库:示例基于 SQL Server(SQLCA 事务);DDDW 子数据窗口支持 PB 支持的任何数据库(事务对象可替换);「外部数据源 DDDW」示例不依赖数据库
3. 操作系统与环境要求:Windows 7+;本主题为标准 DataWindow 编辑风格,无需额外运行库;示例需在窗口放 DataWindow 控件 dw_1 并关联主 DataObject(d_employee),另建两个子 DataWindow 对象 d_country、d_province(或 External 源 d_prov_ext)
4. 难度系数:★★★☆☆(三星,需理解 DataWindow 事件链路与缓冲区,但概念直观)
5. 其它阅读说明:建议先读《PowerBuilder DataWindow 运行时动态改属性:Modify 与 Describe》与《PowerBuilder DataWindow 关键事件链路:ItemChanged/ItemError/RowFocusChanged》;示例用到三张表 t_country(country_id,country_name)、t_province(prov_id,prov_name,country_id)、t_employee(emp_id,emp_name,country,province),或改用 External 字典

下拉数据窗口(DropDownDataWindow,简称 DDDW)是 DataWindow 的一种编辑风格:某个列在录入时,不直接敲键盘,而是展开一张"子 DataWindow"让你选。它和普通下拉框最大的不同是存的是编号、显示的是名称——这正是联动(省→市→区)能成立的根基。本文把一件事讲透:DDDW 的显示值/数据值是怎么分离的,以及父列一变、子列怎么跟着刷。

一、DDDW 是什么:一张"会下拉"的子 DataWindow

DDDW 不是控件,而是列(column)的一种编辑风格(Edit Style)。被设为 DDDW 的列,在得到焦点时会弹出一个下拉面板,面板里是一张完整的 DataWindow(称为下拉子窗口 / DropDownDataWindow)。

关键事实:下拉子窗口是一张真正的 DataWindow,它有 DataObject、能 SetTransObject、能 Retrieve、能 SetFilter,只是"寄居"在父 DataWindow 的某一列里。父 DataWindow 通过 GetChild("列名", 子窗口变量) 拿到它的引用。理解这一点,后面所有运行时操作就顺了。

二、核心机制:显示值(DisplayColumn)与数据值(DataColumn)分离

这是 DDDW 最该吃透的一点,也是新手最容易踩坑的地方。


  • DataColumn(数据列):下拉里选中某行后,实际写进父 DataWindow 这一列的值。通常是编号(如 prov_id=1)。
  • DisplayColumn(显示列):下拉里给用户看的那一列(如 prov_name=北京)。


二者在设计期分别指定。它们的关系是"选中行的 DataColumn 值入库,DisplayColumn 文本仅用于显示"。如果两张表设计成"数据列和显示列是同一列"(比如都选 prov_name),那么入库的就是名称本身——多数业务里你要的是编号,所以一般 DataColumn 选编号列、DisplayColumn 选名称列。
记忆口诀:库里躺编号,眼里看名字;改的是显示,存的还是数据。

三、设计期怎么配(三步)

在 DataWindow 画板里,选中要设为下拉的列 → Edit 页签 → Style 选 DropDownDataWindow,然后:


  • DataWindow:选子数据窗口对象(如 d_province)。
  • Display Column / Data Column:分别选"显示用列"和"入库用列"(如 prov_name / prov_id)。
  • Width / Height:设下拉面板宽度(务必 ≥ 显示列宽度,否则名称被截断);AutoRetrieve 勾上则父 DataWindow Retrieve 时子窗口自动跟着 Retrieve(简单场景可勾,级联场景建议关掉自己控制,见第四节)。


勾了 AutoRetrieve 并不代表"连库"就自动好了——子窗口仍然需要事务对象,只是 Retrieve 时机被托管。级联联动场景下我们通常不勾 AutoRetrieve,改为在代码里精确控制,避免每次父检索都把全部字典拉一遍。

四、运行时必须做的事:GetChild + SetTransObject + Retrieve

新手最常见的"下拉是空的"就栽在这里:子窗口不会继承父 DataWindow 的事务对象,必须自己 SetTransObjectRetrieve。否则子窗口连不上库,下拉自然是空白。
前置:窗口 w_emp 已放 DataWindow 控件 dw_1,DataObject = d_employee;d_employee 的 province 列编辑风格为 DDDW,子数据窗口 = d_province(含 prov_id、prov_name、country_id 三列)。
步骤:在 dw_1 的 retrieveend 事件(或窗口 open)中贴入下方代码,这样下拉展开时子窗口已有数据;本例先一次性取回全部省份,级联过滤放到 itemchanged 里做。
  1. // 取出 province 列对应的下拉子数据窗口
  2. DataWindowChild ldwc_prov
  3. integer li_rc
  4. li_rc = dw_1.GetChild("province", ldwc_prov)
  5. if li_rc = -1 then
  6.     MessageBox("错误", "无法取得 province 的下拉子窗口")
  7.     return
  8. end if
  9. // 子窗口是独立 DataWindow,必须自己连库(不继承父 DW 的事务)
  10. ldwc_prov.SetTransObject(SQLCA)
  11. // 一次性取回全部省份(级联过滤在 itemchanged 事件中做)
  12. long ll_rows
  13. ll_rows = ldwc_prov.Retrieve()
  14. MessageBox("省份下拉", "已载入省份数: " + string(ll_rows))
复制代码

注意 GetChild 返回 -1 表示取不到(列不是 DDDW 或名字拼错),一定要判返回值,否则后面 ldwc_prov.xxx 会空引用崩溃。

五、级联联动:父列变,子列跟着刷

联动的本质:父列(如 country)一变,就把子窗口(province)按新的父值过滤。标准做法是把过滤逻辑封装成一个对象函数,在 itemchanged 里"国家列变动时"调用——这样既能复用,输入也清晰。
前置:d_employee 的 country 列也是 DDDW(子窗口 d_country,含 country_id/country_name),province 列 DDDW(子窗口 d_province,含 country_id 外键)。二者通过 country_id 关联。
步骤:在 dw_1 上新建对象函数 of_filter_province(long al_country),再在 itemchanged 中判断 dwo.Name = "country" 时调用 this.of_filter_province(Long(data))
  1. // 对象函数 of_filter_province(long al_country):按国家过滤省份下拉
  2. // 示例输入:国家数据值(来自 country 列的 DataColumn 值)
  3. long al_country
  4. al_country = 1                       // 示例:中国(country_id = 1)
  5. DataWindowChild ldwc_prov
  6. integer li_rc
  7. li_rc = dw_1.GetChild("province", ldwc_prov)
  8. if li_rc = -1 then
  9.     MessageBox("错误", "未取得 province 子窗口")
  10.     return -1
  11. end if
  12. ldwc_prov.SetTransObject(SQLCA)
  13. ldwc_prov.Retrieve()                 // 先取全部
  14. // 只显示该国家下的省份(字符串条件里数值列直接拼接,无需引号)
  15. ldwc_prov.SetFilter("country_id = " + string(al_country))
  16. ldwc_prov.Filter()
  17. MessageBox("联动结果", "国家 " + string(al_country) + " 下省份已过滤")
  18. return 1
复制代码

itemchanged 里只需一行触发(事件参数 dwo 是发生变化的列、data 是新值):
  1. if dwo.Name = "country" then this.of_filter_province(Long(data))
复制代码
关键提醒:父列变了,子列里原来选的旧省份可能已不属于新国家。务必在过滤后顺手清掉子列值(dw_1.SetItem(row, "province", ""),或弹确认),否则库里会留下"中国 / 东京"这种脏数据。

过滤用 SetFilter + Filter() 还是"带检索参数的 Retrieve",取决于字典大小:小字典(几百条)全量 Retrieve 再 Filter 最简单;大字典建议给子窗口加检索参数、直接 Retrieve(al_country) 只取需要的部分,省内存也快。

六、取值:你拿到的是数据值还是显示值?

联动之后要落库或回显,必须分清两件事:


  • dw_1.GetItemString(row, "province") 拿到的是数据值(prov_id,入库的就是它)。
  • 显示值(prov_name)不在父 DataWindow 里,要去子窗口按数据值反查。

前置:dw_1 已 Retrieve 且用户已选好省份。
步骤:在按钮 cb_read 的 clicked 事件贴入,演示"入库值"与"界面显示值"的区别。
  1. // 示例输入:当前定位行(读者运行前先点中一行)
  2. long ll_row
  3. ll_row = dw_1.GetRow()
  4. if ll_row <= 0 then
  5.     MessageBox("提示", "请先定位到一行")
  6.     return
  7. end if
  8. // 取数据值:写入数据库的就是这一列(通常是编号)
  9. string ls_data_val
  10. ls_data_val = dw_1.GetItemString(ll_row, "province")
  11. // 取显示值:去子窗口里按数据值反查显示列
  12. string ls_disp_val
  13. DataWindowChild ldwc_prov
  14. dw_1.GetChild("province", ldwc_prov)
  15. long ll_cr
  16. ll_cr = ldwc_prov.Find("prov_id = " + ls_data_val, 1, ldwc_prov.RowCount())
  17. if ll_cr > 0 then
  18.     ls_disp_val = ldwc_prov.GetItemString(ll_cr, "prov_name")
  19. else
  20.     ls_disp_val = "(未匹配)"
  21. end if
  22. MessageBox("取值对比", "数据值(入库)=" + ls_data_val + "~r~n显示值(界面)=" + ls_disp_val)
复制代码

记住:显示值只是"查出来给人看的",真正要存、要判断、要传给后台的,永远是数据值。 很多人写联动时拿显示值去查库,结果对不上——根因就是忘了这一层分离。

七、外部数据源 DDDW:不查库的本地下拉

省份、性别、状态这类固定字典,不必每次都查库。可以让子窗口用 External 源(无 SQL),程序里 ImportString 灌数据,再挂到父列的 DDDW 上。好处是零数据库压力、离线可用。
前置:建一个 External 源 DataWindow d_prov_ext(列 prov_id、prov_name),把主 DW 的 province 列 DDDW 指向它。
步骤:窗口 open 时 ImportString 灌入固定字典,下拉即可用。
  1. // 示例输入:固定省份字典(制表符分隔:prov_id<Tab>prov_name,行间回车)
  2. string ls_data
  3. ls_data = "1~t北京~r~n2~t上海~r~n3~t广东"
  4. DataWindowChild ldwc_ext
  5. dw_1.GetChild("province", ldwc_ext)
  6. long ll_n
  7. ll_n = ldwc_ext.ImportString(ls_data)
  8. MessageBox("外部字典", "已导入省份条数: " + string(ll_n))
复制代码

External 源子窗口不需要 SetTransObject,因为它根本没有 SQL;ImportString 的行列分隔符是制表符和回车换行(~t / ~r~n),顺序必须与子窗口列定义一致。

八、边界情况与常见坑(必看)


  • 下拉是空的:九成是子窗口没 SetTransObject + Retrieve。DDDW 子窗口不继承父 DW 事务,这是头号坑。
  • 级联不刷新:父列变了子列还是老数据——忘了对子窗口 SetFilter + Filter() 或重新 Retrieve。AutoRetrieve 只在父 DW 检索时触发一次,之后父列再变不会自动重拉。
  • 入库的是名字不是编号:DataColumn / DisplayColumn 设反了。去画板核对——DataColumn 应是编号列。
  • GetChild 不判返回值:列名拼错或非 DDDW 时返回 -1,直接用变量必崩,务必 if li_rc = -1 then ...
  • 过滤条件引号陷阱:数值列直接拼(country_id = 1),字符列才加单引号(prov_name = '北京');拼错会 Filter() 静默失败、子窗口变空白。
  • 下拉宽度不够:面板 Width 小于显示列,名称被截断像"北…"。设计期把 Width 调到能放下显示列。
  • 父列改了没清子列:留下跨国家的脏组合(如 中国/东京)。联动时主动 SetItem(row,"province","")
  • 重复 Retrieve 拖慢:大字典每次父检索都全量拉——改检索参数 Retrieve(父值) 只取需要的,或 External 源一次性灌。
  • 多层联动(省→市→区):每一层都是"父列 itemchanged → 过滤下一层子窗口",逐层串起来即可;注意清值的顺序要从被改层的下一层开始往后清。


九、与 DropDownListBox / EditMask 怎么选

方案数据源能联动适合
DDDW子 DataWindow(可查库/External)✅ 天然支持字典来自表、需要省→市联动、要显示名存编号
DropDownListBox代码 AddItem 写死❌ 需自己写极少量固定选项、又不在 DataWindow 内
EditMask + Validation无下拉仅格式化输入(电话/日期),不需要选择


一句话:选项来自数据库、要联动、要"显示名存编号",闭眼选 DDDW;纯写死的少量选项,DataWindow 里也可以用 DDDW + External 源,不必退回 DropDownListBox。

十、进阶:多层联动与动态改 SQL


  • 多层联动:省→市→区就是三层 DDDW,每层在父列 itemchanged 里过滤下一层子窗口,并清掉其后各层的旧值,按第八节第 9 条处理即可。
  • 检索参数替代 Filter:给子窗口加检索参数(如 country_id),联动时 ldwc.Retrieve(al_country) 直接只取该国的省,省去全量 Retrieve + Filter,大字典更优。
  • 动态 SQL(高级):若子窗口条件非常灵活,可用 ldwc.SetSQLSelect("新SQL") 改子窗口 SQL 再 Retrieve——注意改完要 SetTransObject 并处理好事务;多数联动场景用 Filter/检索参数就够了,不必动 SQL。
  • 联动与 Update:DDDW 列入库的是 DataColumn 值,Update 时和普列无异;只要子窗口的 DataColumn 对应父表字段,保存链路(AcceptText + Update + COMMIT)完全不用改。


DDDW 把"选择"和"存储"解耦,是 PB 做联动录入最顺手的武器。把显示值/数据值分离、子窗口自行连库、父列变动即过滤这三点吃透,省→市→区乃至多层字典联动都能稳稳拿下。
共享共进共赢
Sharing And Win-win Results
SYBASEBBS - 免责申明1、欢迎访问“SYBASEBBS.COM”,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@sybasebbs.com
您需要登录后才可以回帖 登录 | 站点注册

本版积分规则

免责声明:
本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:Admin@SybaseBbs.com

客服微信:18669893686
挂谷猜想 · 探索

QQ|Archiver|PowerBuilder(PB)BBS社区 ( 鲁ICP备2021027222号-1 )

GMT+8, 2026-8-29 12:54 , Processed in 0.026241 second(s), 8 queries , MemCached On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表