PowerBuilder 编程技巧十例
重 庆 商 品 交 易 所 技 术 部 王 江 伟随 着 数 据 库 技 术 在 各 行 各 业 的 广 泛 应 用, 作 为 企 业 级 数 据 库 前 端 开 发 工 具 的PowerBuilder 日 益 成 为 开 发 人 员 的 得 力 助 手。PowerBuilder 以 其 开 放 的 体 系 结 构, 友 好 的 用 户 界 面 和 简 洁 高 效 的 开 发 环 境 赢 得 了 众 多 程 序 员 的 喜 爱, 连 续 多 年 被 评 为 美 国 计 算 机 界 的 年 度 风 云 产 品, 在 数 据 库 开 发 工 具 领 域 占 据 了 高 达44 % 的 市 场 份 额。PowerBuilder 进 入 我 国 的 时 间 不 长, 许 多 编 程 人 员 希 望 了 解 并 掌 握 这 一 先 进 工 具。 在 这 里, 笔 者 将 自 己 平 日 用PowerBuilder 作 开 发 的 一 些 体 会 整 理 出 来, 奉 献 给 大 家。
PowerBuilder 是 由 多 个 功 能 模 块 组 成 的 可 视 化 集 成 开 发 环 境, 是 面 向 对 象 的 开 发 工 具, 用 它 可 以 方 便 地 建 立 起 基 于Windows 的 分 布 式 数 据 库 应 用。 其 功 能 模 块 分 别 完 成 应 用 管 理、 窗 口 对 象 设 计、 菜 单 对 象 设 计、 数 据 窗 对 象 设 计 和 数 据 库 查 询 等 工 作, 这 些 功 能 模 块 由 于PowerBuilder 提 供 的 色 彩 丰 富 的 工 具 条 而 被 称 作"Painter"( 画 板)。 下 文 便 依 据 各 模 块 作 大 的 分 类 介 绍 相 应 的 编 程 技 巧。
仅 让 应 用 程 序 运 行 一 次 的 技 巧
有 时 需 要 限 制 一 个PowerBuilder 应 用 同 时 运 行 的 实 例(Instance) 个 数 或 仅 让 应 用 运 行 一 次, 我 们 可 以 通 过 调 用WindowsSDK 函 数 或 使 用PowerBuilder 的Handle() 函 数 来 实 现。
先 谈 调 用SDK 函 数 的 方 法。 为 了 调 用SDK 函 数, 需 要 在ApplicationPainter 的 菜 单 项Declare\GlobalExternalFunctions... 中 定 义:
Function uint GetModuleHandle(string ModuleName) Library "Kernel.exe"
Function uint GetModuleUsage(uint ModuleHandle) Library "Kernel.exe"
下 面 这 段 程 序 写 在Application 的Open 事 件 中。 它 先 通 过 调 用SDK 函 数GetModuleHandle( ) 获 得 指 定 应 用 程 序 的 句 柄, 然 后 调 用GetModuleUsage( ) 函 数 确 定 应 用 程 序 同 时 运 行 的 实 例 个 数。
uint IApplHandle
int App_num
IApplHandle = GetModuleHandle("c:\rem\rem.exe")
if IApplHandle > 0 then
App_num = GetModuleUsage(IApplHandle)
if App_num > 1 then
Messagebox("注意","本程序已经运行!",Stopsign!)
return
end if
end if
Open(w_main)
若 需 要 限 制 应 用 同 时 运 行 的 实 例 个 数, 比 如 仅 允 许 同 时 运 行N 个 实 例, 那 么 将 上 述 程 序 中 的 语 句“if App_num > 1 then” 改 为“if App_num > N then” 即 可。
采 用Handle( ) 函 数 的 方 法 更 简 洁 一 些, 代 码 如 下:
int hand
hand= Handle(this,TRUE)
If hand > 0 then
Messagebox
(" 注意","本程序已经运行!",Stopsign!)
Halt
else
Open(w_main)
end if
1、 提 供 类 似 中 文 之 星 的 实 时 帮 助 条
中 文 之 星2.0 版 的 链 形 菜 单 管 理 器 提 供 了 实 时 帮 助 条, 增 强 了 系 统 的 易 用 性, 在PowerBuilder 中 也 可 以 实 现 类 似 的 功 能。 当 鼠 标 移 动 到 窗 口 中 的 某 些 控 制(Control), 如 编 辑 器、 图 片 等 时, 会 在 鼠 标 附 近 自 动 产 生 帮 助 条, 实 时 地 提 示 操 作 要 领。
首 先 在 窗 口w_main 中 任 意 位 置 定 义 一 个 黄 底 黑 字 的 静 态 文 本st_help, 设 定st_help.visible=false,st_help.text=&Help; 然 后 在 该 窗 口 模 块 的Declare\WindowFunctions... 下 定 义 函 数show_help( ), 其 参 数 只 有 一 个, 参 数 名 为text, 类 型 为string, 通 过 传 值 方 式 接 收 参 数; 无 返 值。show_help( ) 代 码 如 下:
if st_help.visible then return
st_help.text = text
st_help.width = Len(st_help.text) * 38
st_help.x = w_main.PointerX( )
st_help.y = w_main.PointerY( ) + 50
if st_help.x + st_help.width >
w_main.Workspacewidth( ) then
st_help.x = w_main.Workspacewidth( )
- st_help.width
end if
if st_help.y + st_help.height >
w_main.Workspaceheight( ) then
st_help.y = w_main.Workspaceheight( )
- st_help.height
end if
st_help.visible= true
接 下 来, 我 们 就 可 以 调 用show_help( ) 函 数 了。 但PowerBuilder 提 供 的 所 有 控 制 均 缺 乏 当 鼠 标 移 至 其 上 就 触 发 的 事 件, 显 然, 需 要 定 义 相 应 的 用 户 事 件。
先 选 中 准 备 定 义 用 户 事 件 的 控 制, 如 某 个 单 行 编 辑 器, 然 后 在 窗 口 模 块 的 菜 单Declare\UserEvents... 下, 双 击Paste Event ID: 中 的pbm_mousemove 条 目, 将 其 拷 贝 至Event ID 下, 取Event Name 为Mouseon, 这 样, 我 们 就 定 义 好 了 相 应 控 制 的 用 户 事 件Mouseon。
我 们 可 以 在 该 控 制 的 用 户 事 件Mouseon 下, 写 下 调 用 函 数show_help( ) 的 语 句:
if st_help.visible then Hide(st_help)
show_help(" 瞧! 这 便 是 实 时 帮 助 条!")
2、“ 跑 马 灯” 的 实 现 技 巧
有 时 需 要 用 一 矩 形 条 显 示 少 量 用 户 特 别 关 心 的 信 息, 这 条 信 息 串 首 尾 相 连, 向 一 个 方 向 循 环 滚 动, 我 们 通 常 将 其 称 作“ 跑 马 灯”。 证 券 业 中 常 用“ 跑 马 灯” 来 显 示 不 断 变 化 的 股 票 行 情; 实 际 应 用 中 也 常 通 过“ 跑 马 灯” 来 监 视 是 否 死 机。
我 们 可 以 写 一 个 简 单 的 函 数running_horse( ) 来 实 现“ 跑 马 灯” 的 显 示。running_horse 有 两 个 参 数, 第 一 个 参 数 的 参 数 名 为textline, 类 型 为string, 传 值; 第 二 个 参 数 的 参 数 名 为num, 类 型 为int, 传 值; 函 数 返 值 类 型 为string。 该 函 数 的 代 码 仅 一 句:
return Mid(textline,(num + 1)) + Left(textline,num)
下 面 就 可 以 调 用running_horse( ) 函 数 了。 先 在 一 个 窗 口 里 定 义 好 单 行 编 辑 器sle_running_horse, 在 该 窗 口 的Open 事 件 下 写 上:
sle_running_horse.text = "I am testing running_horse!"
Timer(0.2)
然 后 在 该 窗 口 的Timer 事 件 下 调running_horse( ), 代 码 如 下:
sle_running_horse.text = running_horse(sle_running_horse.text,1)
这 样, 当 你 打 开 这 个 窗 口 时,“ 跑 马 灯” 便 会 运 转 起 来。 可 以 在 程 序 中 加 些 语 句, 适 时 地 增 减sle_running_horse.text 中 的 内 容, 你 便 会 在“ 跑 马 灯” 中 看 到 相 应 变 化 的 信 息。
右 键 菜 单 的 实 现 技 巧
当 你 在 相 应 的 窗 口 或 控 制 上 按 鼠 标 右 键 时, 就 会 在 鼠 标 所 指 位 置 弹 出 菜 单, 这 就 是 右 键 菜 单。 程 序 中 支 持 右 键 菜 单 会 为 用 户 的 操 作 带 来 许 多 方 便, 同 时 鼠 标 右 键 可 以 分 担 部 分 左 键 的 功 能。 右 键 菜 单 在 证 券 期 货 业 中 的 许 多 大 型 行 情 分 析 软 件 中 得 到 了 广 泛 的 应 用。 在PowerBuilder 中 实 现 右 键 菜 单 非 常 简 单, 仅 两 个 步 骤:1. 设 计 相 应 菜 单;2. 在 窗 口 或 控 制 的Rbuttondown 事 件 下 写 上 调 用 语 句。
先 在Menu Painter 中 创 建 菜 单rbuttonpop,rbuttonpop 有 一 个 菜 单 条 目(Menuitem)m_choice。
然 后 在 需 要 调 用 该 菜 单 的 窗 口 或 控 制 的Rbuttondown 事 件 下 写 上:
m_rbuttonpop NewMenu
NewMenu = Create m_rbuttonpop
NewMenu.m_choice.PopMenu (PointerX( ),PointerY( ))
至 此, 右 键 菜 单 制 作 完 毕。 上 述 语 句 中 的NewMenu 的 数 据 类 型 为m_rbuttonpop, 当 你 在 相 应 位 置 按 鼠 标 右 键 时, 弹 出 的 菜 单NewMenu 是 菜 单m_rbuttonpop 的 一 个 实 例(Instance)。
数 据 窗 对 象 是PowerBuilder 中 最 重 要 的 概 念 之 一, 它 是PowerBuilder 应 用 区 别 于 其 它Windows 应 用 的 重 要 特 征, 同 时 也 是PowerBuilder 的 价 值 所 在。PowerBuilder 应 用 通 常 通 过 数 据 窗 对 象 从 数 据 库 或 其 它 数 据 源 取 得 数 据 并 加 以 显 示, 其 数 据 的 输 入、 添 加、 修 改 和 删 除 也 大 都 通 过 数 据 窗 对 象 来 实 现。 故 理 解 并 掌 握 数 据 窗 概 念 对 于 用 好PowerBuilder 具 有 重 要 意 义。 下 面 给 出 了 有 关 数 据 窗 的 几 个 编 程 技 巧。
1、 自 动 调 整 大 小 的 数 据 窗
在PowerBuilder 应 用 运 行 过 程 中, 常 常 会 用 鼠 标 拖 动 窗 口 角 以 改 变 窗 口 大 小, 尤 其 是 在 多 文 档 窗 口(MDI) 中, 通 常 有 多 个sheet 存 在 的 情 况 下, 有 时 为 了 察 看 后 面 窗 口 中 的 数 据 而 将 前 面 窗 口 缩 小, 但 窗 口 缩 小 了, 其 中 的 数 据 窗 并 没 有 缩 小, 由 此 而 不 能 方 便 地 使 用 数 据 窗 的 卷 滚 条, 那 么 怎 样 使 前 面 窗 口 中 的 数 据 窗 大 小 随 窗 口 的 大 小 自 动 调 整 呢 ? 很 简 单, 我 们 只 需 要 在 数 据 窗 所 在 窗 口 的Resize 事 件 下 写 上 一 句 话:
Resize(dw_datamon,this.Workspacewidth( ) - 50,this.Workspaceheight( ) - 50)
其 中dw_datamon 是 数 据 窗 的 名 字, 数 字50 可 以 调 整。
这 样, 你 就 拥 有 了 一 个 会 随 窗 口 大 小 变 化 而 自 动 调 整 大 小 的 数 据 窗 了。 卷 滚 条 用 起 来 很 方 便, 不 信 试 试。
2、Retrieve 后 不 回 卷 的 数 据 窗
我 们 经 常 面 对 一 大 堆 数 据, 其 具 体 体 现 就 是 数 据 窗 很 长, 需 要 拉 动 垂 直 卷 滚 条 才 能 看 到 后 面 的 数 据, 当 你 在 包 含 长 数 据 窗 的 窗 口 的Timer 事 件 中 写 下Retrieve( ) 语 句 后, 令 人 气 恼 的 事 情 就 会 发 生:Timer 事 件 一 执 行, 数 据 窗 就 翻 回 第 一 页; 如 果Timer 事 件 执 行 的 时 间 间 歇 很 短, 那 我 们 就 永 远 没 有 足 够 的 时 间 来 察 看 后 面 的 数 据 了。 下 面 我 们 着 手 解 决 这 个 问 题。
可 能 你 已 经 注 意 到 了, 每 个 数 据 窗 都 拥 有 两 个 与Retrieve 有 关 的 事 件:Retrievestart 和Retrieveend, 它 们 分 别 允 许 我 们 在Retrieve 的 前 后 干 一 些 事, 这 正 是 我 们 所 需 要 的。 实 际 上, 就 这 两 个 事 件, 我 们 已 经 能 够 提 出 两 个 解 决 方 案 了。 其 一, 在Retrievestart 事 件 中, 保 存 当 前 数 据 窗 中 可 见 的 数 据 行; 然 后Retrieve; 接 着 在Retrieveend 事 件 中, 恢 复 先 前 保 存 的 数 据 行。 其 二, 在Retrievestart 事 件 中, 保 存 当 前 垂 直 卷 滚 块 的 位 置;Retrieve 后 再 恢 复 其 位 置。 后 者 使 用 了 动 态 数 据 窗 函 数, 实 现 起 来 更 简 洁 一 些, 下 面 详 细 探 讨。
假 设 你 已 设 计 好 了 一 个 在 窗 口w_datamon 中 的 数 据 窗dw_datamon, 现 在 可 以 先 定 义 一 个 保 存 垂 直 卷 滚 块 位 置 的 类 型 为string 的Globle 变 量old_vspos, 然 后 在 该 数 据 窗 的Retrievestart 事 件 下 输 入 以 下 语 句 以 保 存 其 位 置:
old_vspos = this.dwDescribe("DataWindow.VerticalScrollPosition")
dw_datamon.SetRedraw(false)
在 相 应 的Retrieveend 事 件 下 输 入 恢 复 垂 直 卷 滚 块 位 置 的 语 句:
this.dwModify("DataWindow.VerticalScrollPosition = " + old_vspos)
dw_datamon.SetRedraw(true)
这 样, 数 据 窗 上 的 工 作 已 做 完。 下 面 是 相 应 窗 口 上 的 工 作。
该 窗 口 的Open 事 件 下:
dw_datamon.Settrans(sqlca)
dw_datamon.Retrieve( )
timer(6)
该 窗 口 的Timer 事 件 下:
Setfocus(w_datamon)
Retrieve(dw_datamon)
至 此,Retrieve 后 不 会 回 卷 的 数 据 窗dw_datamon 已 经 可 以 工 作 了。 值 得 注 意 的 是, 数 据 窗 的 排 序 分 类 等 操 作 应 在Retrieve 前 就 在 数 据 库 表 中 完 成, 否 则Retrievestart 事 件 保 存 的 卷 滚 块 位 置 很 可 能 并 不 是 你 所 期 待 的, 换 句 话 说,Retrievestart 事 件 应 发 生 在 所 有 数 据 窗 操 作 之 后; 另 外, 在 每 次Retrieve 后, 应 将 处 于 该 数 据 窗 上 的Focus 移 开, 以 免 具 有 焦 点 的 数 据 窗 的 第 一 行 第 一 列 总 要 显 示, 故 在 窗 口w_datamon 的Timer 事 件 中 设 置 了Setfocus(w_datamon) 这 条 语 句。
3、 依 据 条 件 改 变 数 据 颜 色
依 据 条 件 改 变 数 据 颜 色 是 许 多 场 合 都 要 用 到 的 重 要 功 能, 数 据 颜 色 的 改 变 不 仅 引 人 注 目, 而 且 能 起 到 暗 示 作 用, 清 楚 地 告 诉 用 户 价 位 的 涨 跌 或 状 态 的 改 变 等。 大 多 数 证 券 期 货 实 时 行 情 显 示 软 件 都 提 供 了 这 种 功 能。 在 当 前 价 位 比 其 前 一 价 位 高 时, 当 前 价 位 数 据 颜 色 变 红, 表 示 价 位 上 涨; 反 之, 颜 色 变 绿, 表 示 价 位 下 跌; 若 当 前 价 位 与 其 前 一 价 位 相 等, 则 数 据 颜 色 不 变。PowerBuilder 没 有 提 供 解 决 这 一 问 题 的 捷 径, 但 我 们 仍 可 利 用 动 态 数 据 窗 来 实 现。
先 考 虑 一 下 实 现 的 步 骤, 在Retrieve 前 需 要 把 有 关 列 的 数 据 先 保 存 起 来;Retrieve 后 我 们 获 得 了 相 应 列 的 新 数 据; 我 们 需 要 将 上 述 二 者 作 一 比 较, 以 确 定 颜 色 的 变 化。 值 得 指 出 的 是, 由 于 动 态 数 据 窗 函 数dwModify( ) 只 能 用 描 述 数 据 窗 的 模 式 串 作 参 数, 不 能 接 收 变 量 作 参 数, 故 我 们 得 想 法 把 比 较 的 结 果 传 递 给 数 据 窗。 为 解 决 这 个 问 题, 可 以 在 定 义 数 据 窗 时 多 定 义 几 个 空 列, 这 几 列 不 与 数 据 库 表 中 的 列 相 对 应, 它 们 作 为 存 放 比 较 结 果 的 缓 冲 区。 原 则 上 若 需 要N 列 实 时 地 变 色, 则 需 要N 列 缓 冲 区, 就 应 该 多 定 义N 个 空 列。 下 面 给 出 了 一 个 例 子 具 体 说 明。
这 段 程 序 写 在 某 窗 口 的Timer 事 件 中, 该 窗 口 内 有 数 据 窗dw_infor, 其"buy"、"sell" 列 分 别 表 示 买 价 和 卖 价, 需 要 实 时 地 变 颜 色。 为 此, 我 们 在 数 据 窗dw_infor 中 多 定 义 了"buybuf" 和"sellbuf" 两 列, 分 别 存 放"buy" 列 和"sell" 列Retrieve 前 后 数 据 比 较 的 结 果。
//Red = 255; Green = 65280
int i,infor_rownum
decimal buy_old[],sell_old[],buy_new[],sell_new[]
dw_infor.SetRedraw(false)
infor_rownum = dw_infor.RowCount()
FOR i=1 TO infor_rownum
buy_old[i] = dw_infor.GetitemNumber(i,"buy")
sell_old[i] = dw_infor.GetitemNumber(i,"sell")
NEXT
dw_infor.retrieve()
FOR i=1 TO infor_rownum
buy_new[i] = dw_infor.GetitemNumber(i,"buy")
sell_new[i] = dw_infor.GetitemNumber(i,"sell")
NEXT
FOR i=1 TO infor_rownum
dw_infor.Setitem(i,"buybuf",buy_new
[i] - buy_old[i])
dw_infor.Setitem(i,"sellbuf",sell_new
[i] - sell_old[i])
NEXT
dw_infor.dwModify("buy.color = '0 ~t if(buybuf>0,255,if(buybuf
<0,65280,0))'")
dw_infor.dwmodify("sell.color="0~t if(sellbuf>0,255,if(sellbuf<
0,65280,0))'"
) dw_infor.setredraw(true)
我 们 看 到, 程 序 在Retrieve 前 后 分 别 将"buy"和"sell"; 列 的 数 据 写 进 与 其 类 型 匹 配 的 数 组 中, 然 后 将 比 较 的 结 果 分 别 写 入"buybuf" 和"sellbuf" 列, 最 后 用 函 数dwModify() 改 变 有 关 列 的 颜 色。
记 住 在 该 窗 口 的Open 事 件 中 设 置 事 务 对 象 并 激 活Timer 事 件。
此 外, 还 有 一 些 方 法 可 以 改 变 颜 色, 比 如 先 在 某 些 需 要 变 颜 色 的 行 或 列 设 置 带 颜 色 的 长 方 形, 同 时 将 其 上 面 的 数 据 窗 中 的 数 据 设 置 成 透 明 的, 当 条 件 改 变 时, 可 以 通 过 改 变 数 据 窗 后 的 长 方 形 的 颜 色 来 实 现。
4、 用Enter 键 替 代Tab 键 切 换 栏 目 的 数 据 窗
许 多 情 况 下,PowerBuilder 应 用 的 数 据 是 通 过 数 据 窗 输 入 的, 而 且 输 入 的 数 据 是 单 纯 的 数 字 数 据, 也 就 是 说, 输 入 内 容 完 全 可 以 通 过 敲 击 键 盘 右 面 的 数 字 小 键 盘 来 完 成。 但 在 实 际 使 用 中, 数 据 窗 栏 目 间 的 切 换 却 要 通 过 按 键 盘 最 左 边 的Tab 键 来 实 现, 既 不 方 便 又 影 响 录 入 速 度。 如 果 能 用Enter 键 替 代Tab 键 切 换 栏 目 就 好 了。
由 于 按Enter 键 是Windows 直 接 支 持 的 消 息, 故 我 们 可 以 使 用 用 户 事 件 来 解 决 问 题。 在 用 户 事 件 中,PowerBuilder 提 供 的 一 条pbm_ 事 件 对 应Windows 的 一 条 或 几 条 消 息。 我 们 在 数 据 窗dw_datamon 的 用 户 事 件 中 选 择pbm_dwnProcessEnter 并 命 名 为Enterkeydown。 在 该 事 件 下 写 代 码:
Send(Handle(this),256,9,Long(0,0)) This.SetActionCode(1)
这 将 把 消 息 传 递 给Tab 键, 同 时 忽 略Enter 键 的 处 理。
下 面 是 一 段 用 数 据 窗 接 收 数 据 的 完 整 的 程 序 段, 其 中 采 用 了 用Enter 键 替 代Tab 键 的 代 码。 当 光 标 在 每 行 最 后 一 列 时 按Enter 键, 光 标 会 移 至 下 一 行