马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?站点注册
×
PowerBuilder DataWindow 入门:从取数到保存的一条完整链路
阅读说明
① 适用版本:PowerBuilder 9.0 ~ 12.5(PBIDEA 兼容);
② 支持数据库:示例基于 SQL Server(SQLCA 连接),本文 API 适用于任何 PB 支持的数据库;
③ 操作系统与环境要求:Windows 7 及以上,需能连接到目标数据库;
④ 难度系数:★★★☆☆(入门进阶,建议先看过 Day1《DataWindow 三缓冲区》再读本文效果更好);
⑤ 其它阅读说明:示例假设窗口上已放置数据窗口控件 dw_1,且其 DataObject 已指向某个 d_ 开头的 DataWindow 对象。
DataWindow 到底是什么
DataWindow(数据窗口)是 PowerBuilder 最核心的武器:一条 SQL 查出来,界面、录入、校验、更新全包了。理解它抓住三点:
- 两个输入:数据源(SQL 语句或外部数据)+ 表现风格(Grid 表格 / Tabular 列表 / Freeform 自由格式)。
- 三层对象:DataWindow 对象(设计时画的,存在 d_ 开头的对象里)→ DataWindow 控件(窗口里放的 dw_1)→ 缓冲区里的行数据。
- 四类成员:属性(DataObject、Border…)、函数(Retrieve、Update…)、事件(itemchanged、dberror…)、子对象(列、文本、band)。
日常工作就是围绕"取数 → 编辑 → 保存"这条链路转,本文按这个顺序讲透。
第一步:连接数据库(SQLCA)
PB 用事务对象 SQLCA 统一管数据库连接,窗口打开时配一次即可。
- // 窗口 open 事件:配置并连接数据库
- SQLCA.DBMS = "MSSQL" // 数据库类型
- SQLCA.ServerName = "127.0.0.1,1433" // 服务器和端口
- SQLCA.Database = "demo" // 数据库名
- SQLCA.UserID = "sa" // 用户名
- SQLCA.Pass = "123456" // 密码
- SQLCA.AutoCommit = false // 手动控制事务
- // 连接
- long ll_code
- ll_code = SQLCA.Connect()
- if ll_code <> 0 then
- MessageBox("错误", "连接失败:" + SQLCA.SQLErrText)
- return
- end if
- MessageBox("提示", "数据库连接成功")
复制代码
第二步:取数(SetTransObject + Retrieve)
把事务对象交给 dw_1,再执行 Retrieve 拉数据。顺序不能反:先 SetTransObject 再 Retrieve。
- // 按钮 cb_retrieve 的 clicked 事件:取数
- long ll_rows
- dw_1.SetTransObject(SQLCA) // 让 dw_1 用 SQLCA 这个连接
- ll_rows = dw_1.Retrieve() // 执行检索,返回行数
- if ll_rows = -1 then // -1 表示检索出错
- MessageBox("错误", "检索失败:" + SQLCA.SQLErrText)
- return
- elseif ll_rows = 0 then
- MessageBox("提示", "没有查到数据")
- else
- MessageBox("提示", "共检索到 " + String(ll_rows) + " 行")
- end if
复制代码
如果 DataWindow 对象定义了检索参数(比如按部门查),Retrieve 直接传参:
- string ls_dept
- ls_dept = "SALES"
- dw_1.Retrieve(ls_dept) // 参数顺序、类型与定义时一致
复制代码
第三步:编辑行(新增 / 删除 / 改值)
新增一行
- // 按钮 cb_add 的 clicked 事件:末尾加一行并跳到新行
- long ll_new
- ll_new = dw_1.InsertRow(0) // 0 表示插到末尾,返回新行号
- dw_1.ScrollToRow(ll_new) // 滚动到新行
- dw_1.SetColumn("name") // 光标定位到 name 列
- dw_1.SetFocus()
- // 给新行填默认值(可直接写入缓冲区,用户可见)
- dw_1.SetItem(ll_new, "create_date", Today())
- MessageBox("提示", "已新增第 " + String(ll_new) + " 行,请录入")
复制代码
删除当前行
- // 按钮 cb_del 的 clicked 事件:删除当前行
- long ll_cur
- ll_cur = dw_1.GetRow() // 当前行号
- if ll_cur <= 0 then
- MessageBox("提示", "没有可删除的行")
- return
- end if
- if MessageBox("确认", "删除当前行?", Question!, YesNo!) = 1 then
- dw_1.DeleteRow(ll_cur) // 注意:只是进删除缓冲区,还没真正删库
- MessageBox("提示", "已删除,记得点保存才会生效")
- end if
复制代码
第四步:保存(AcceptText + Update + 事务)
保存是新手最容易出错的地方,三个动作缺一不可:
- AcceptText():把正在编辑的单元格内容收进缓冲区——不调用的话,光标所在格的修改会丢;
- Update():把缓冲区的新增/修改/删除生成 SQL 发给数据库;
- COMMIT / ROLLBACK:按 Update 结果提交或回滚事务。
- // 按钮 cb_save 的 clicked 事件:保存全部修改
- integer li_ret
- li_ret = dw_1.AcceptText() // 1=成功 0=没输入框 -1=数据非法
- if li_ret <> 1 then
- MessageBox("错误", "当前单元格数据不合法,请检查后再保存")
- return
- end if
- // 更新数据库
- integer li_upd
- li_upd = dw_1.Update()
- if li_upd = 1 then
- commit using SQLCA ; // 提交事务
- MessageBox("提示", "保存成功")
- else
- rollback using SQLCA ; // 失败回滚,不留半截数据
- MessageBox("错误", "保存失败:" + SQLCA.SQLErrText)
- end if
复制代码注意:commit using SQLCA ; 末尾的分号是 PowerScript 嵌入式 SQL 的固定写法,不能省。
第五步:常用函数速查
| 分类 | 函数 | 作用 | | 取数 | Retrieve() | 执行检索,返回行数(-1 出错) | | 行操作 | InsertRow(n) / DeleteRow(n) | 增 / 删行(n=0 表示末尾) | | 行操作 | RowCount() | 当前主缓冲区行数 | | 行操作 | GetRow() / SetRow(n) / ScrollToRow(n) | 取 / 设 / 滚动到当前行 | | 列操作 | GetColumn() / SetColumn("列名") | 取 / 设当前列 | | 列操作 | GetItemString(n,"列") / GetItemNumber(n,"列") | 读某行某列的值 | | 列操作 | SetItem(n, "列", 值) | 直接写缓冲区某个格 | | 列操作 | AcceptText() | 把正在编辑的格子收进缓冲区 | | 数据 | Update() | 把改动写回数据库 | | 数据 | Reset() | 清空全部行 | | 排序过滤 | SetSort("列 A") + Sort() | 排序 | | 排序过滤 | SetFilter("条件") + Filter() | 过滤显示(不删数据) | | 查找 | Find("条件", 1, RowCount()) | 按条件找行,返回行号 |
- // 综合示例:读值、排序、过滤、查找
- // 输入值
- long ll_cnt
- ll_cnt = dw_1.RowCount() // 当前行数
- // 读当前行 name 列
- long ll_row
- ll_row = dw_1.GetRow()
- string ls_name
- ls_name = dw_1.GetItemString(ll_row, "name")
- // 按薪资降序排
- dw_1.SetSort("salary D") // A=升序 D=降序
- dw_1.Sort()
- // 只看薪资大于 5000 的行(数据还在,只是不显示)
- dw_1.SetFilter("salary > 5000")
- dw_1.Filter()
- // 按条件找行:找到返回行号,找不到返回 0
- long ll_find
- ll_find = dw_1.Find("name = 'Tom'", 1, dw_1.RowCount())
- MessageBox("查询示例", "共" + String(ll_cnt) + "行 / 当前行name=" + ls_name + " / 找到Tom在" + String(ll_find) + "行")
复制代码
第六步:事件入门(校验就写在事件里)
DataWindow 的事件是"活"的入口,最常用两个:
| 事件 | 触发时机 | 典型用途 | | itemchanged | 用户改完一个单元格、值被接受时 | 校验、联动计算 | | itemerror | 单元格输入非法(类型不符)时 | 提示用户改错位置 | | row**hanged | 当前行切换时 | 联动明细、刷新状态栏 | | dberror | 数据库操作出错时 | 统一错误处理 |
事件脚本的返回值很有讲究:itemchanged 返回 0 表示接受输入,返回 2 表示拒绝并让光标留在原地。
- // 这是 dw_1 的 itemchanged 事件脚本(在数据窗口控件上双击事件写)
- // data 是用户刚输入的新值(还没写进缓冲区);dwo 是列对象
- if dwo.name = "salary" then
- if not IsNumber(string(data)) then
- MessageBox("校验", "薪资必须是数字")
- return 2 // 2=拒绝输入,光标留在该格
- end if
- end if
- return 0 // 0=接受,写入缓冲区
复制代码
第七步:DataStore——没有界面的 DataWindow
DataStore 是没有可视界面的 DataWindow,用来做后台批量处理、导入导出。用法几乎一样,只是要自己 create / destroy。
- // 按钮 cb_batch 的 clicked 事件:后台批量读取处理
- datastore lds
- lds = create datastore
- lds.DataObject = "d_employee" // 指向同一个 DataWindow 对象
- lds.SetTransObject(SQLCA)
- long ll_cnt
- ll_cnt = lds.Retrieve()
- // 逐行读出来处理(循环变量先声明)
- integer li_i
- string ls_name
- for li_i = 1 to ll_cnt
- ls_name = lds.GetItemString(li_i, "name")
- // 这里做你的业务处理,比如拼字符串、写日志
- next
- // 整表数据导出为文本 / 从文件导入
- string ls_all
- ls_all = lds.Object.DataWindow.Data // 全部数据:制表符分隔的文本
- lds.ImportFile("C:\data\list.csv") // 从 CSV 导入
- destroy lds
- MessageBox("DataStore", "后台处理 " + String(ll_cnt) + " 行,导出文本长度 " + String(Len(ls_all)))
复制代码
小结:一条链路,四个要点
DataWindow 入门就是记住"连接 → 取数 → 编辑 → 保存"这条链路,以及四个易错点:
- 先 SetTransObject 再 Retrieve,顺序反了必然报错;
- 保存前先 AcceptText,否则正在编辑的那个格子会丢修改;
- Update 成功要 COMMIT,失败要 ROLLBACK,否则数据会处于"改了又没完全改"的悬空状态;
- DeleteRow 只是把行放进删除缓冲区,没点保存前数据库一行都没动——这也是 DataWindow 三缓冲区的妙处(详见 Day1 文章)。
把这四个要点和上面的速查表记住,DataWindow 的日常开发就不会再发怵。 |