PowerBuilder应用开发系列讲座(31)
在 上 期 中 我 们 讨 论 了 在Master/Detail 数 据 窗 口 中, 当Master
数 据 窗 口 没 有 使 用"Retrieve as Needed" 选 项 时, 两 个
数 据 窗 口 同 时 滚 动 的 问 题。 可 是 当Master 数 据 窗 口 使 用 了"Retrieve
as Needed" 选 项 时, 我 们 却 遇 到 了 麻 烦。 在 点 击dw_2 的 滚
动 条 时, 如 果dw_1 从 数 据 库 中 取 得 了 新 数 据, 将 导 致dw_2 的
复 位,dw_2 就 不 能 显 示 正 确 的 结 果 了。 解 决 这 个 问 题, 我 们
最 容 易 想 到 的 解 决 方 法 就 是 利 用dw_2 在scrollvertical 事 件 中
获 得 的dw_2 当 前 滚 动 条 的 位 置, 对dw_2 进 行 重 新 赋 值。
即 对dw_1 的SCROLLVERTICAL 事 件 编 程 如 下:
integer vmax_1, vpos_1,vmax_2, vpos_2 string r_code decimal vmax_1_percent vmax_1 = integer(dw_1.describe("datawindow.verticalscrollmaximum")) vpos_1 =integer(dw_1.describe("datawindow.verticalscrollposition")) vmax_2 = integer(dw_2.describe("datawindow.verticalscrollmaximum")) vmax_1_percent = vpos_1 / vmax_1 vpos_2 = vmax_1_percent * vmax_2 r_code = dw_2.modify("datawindow.verticalscrollposition="+string(vpos_2)) // 检 验 是 否 修 改 成 功 if r_code <> "" then beep(6) mle_1.text = "dw_1scroll = "+r_code+" vpos_2 = "+string(vpos_2) //在scrollvertical事件中无法使用MessageBox弹出错误信息框 end if 对dw_2 的SCROLLVERTICAL 事 件 编 程 如 下: integer vmax_1, vpos_1,vmax_2, vpos_2 string r_code decimal vmax_2_percent vmax_2 = integer(dw_2.describe("datawindow.verticalscrollmaximum")) vpos_2 = integer(dw_2.describe("datawindow.verticalscrollposition")) vmax_1 =integer(dw_1.describe("datawindow.verticalscrollmaximum")) vmax_2_percent = vpos_2 / vmax_2 vpos_1 = vmax_2_percent * vmax_1 r_code = dw_1.modify("datawindow.verticalscrollposition="+string(vpos_1)) // 检 验 是 否 修 改 成 功 if r_code <> ""then beep(6) mle_1.text = "dw_1 mod "+r_code end if r_code = dw_2.modify("datawindow.verticalscrollposition="+string(vpos_2)) // 检 验 是 否 修 改 成 功 if r_code <> "" then beep(6) sle_1.text = "dw_2 mod" + r_code end if
键 入 上 述 代 码 后, 运 行 时 你 就 会 发 现 点 击dw_2 时 运 行 的 结 果 是 正 常 的, 然 而 点 击dw_1 的 滚 动 条 却 发 生 了 错 误。 这 是 为 什 么 呢 ?
首 先 我 们 还 是 对 用 户 点 击dw_1 的 滚 动 条 向 下 滚 动 时, 系 统 进 行 了 哪 些 操 作 进 行 一 下 仔 细 地 分 析: 当 用 户 点 击 鼠 标 使dw_1 滚 动 到 了 最 后 一 行 时, 这 时 系 统 没 有 立 即 进 行 显 示 滚 动 的 操 作, 它 首 先 发 现 数 据 窗 口 的Primary! 缓 冲 区 的 数 据 已 经 告 罄, 于 是 就 向 后 台 的 数 据 库 中 攫 取 数 据, 取 得 新 的 数 据 后, 系 统 会 将 与dw_1 共 享 数 据 的dw_2 复 位, 使 之 回 到 显 示 第 一 条 记 录 的 位 置。 这 就 触 发 了dw_2 的scrollvertical 事 件( 我 们 称 之 为 事 件 一)。 在 这 个 事 件 中, 由 于 此 时dw_2 显 示 的 是 第 一 条 记 录, 它 将 修 改dw_1 的 滚 动 条 位 置, 使 之 也 显 示 第 一 条 记 录。 并 立 即 触 发dw_1 的scrollvertical 事 件 二。 在 事 件 二 中,dw_1 又 根 据 自 己 的 位 置 修 改dw_2, 但 是 由 于dw_2 修 改 前 后 的 位 置 相 同, 所 以 没 有 对 系 统 造 成 什 么 影 响, 又 回 到 了 事 件 一。 在 事 件 执 行 完 毕 后, 系 统 开 始 响 应 用 户 的 下 滚dw_1 的 操 作, 首 先 将dw_1 向 下 滚 动 一 条 记 录, 这 样dw_1 显 示 的 是 第 二 条 记 录, 并 触 发 了dw_1 的scrollvertical 事 件 三。 当 事 件 三 执 行 时, 将dw_2 也 滚 动 到 第 二 条 记 录, 并 触 发 了dw_2 的scrollvertical 事 件 四, 不 过 这 一 事 件 四 也 不 会 对 任 何 一 个 数 据 窗 口 造 成 影 响, 最 后 返 回 执 行 完 事 件 三。 这 就 完 成 了 用 户 点 击 鼠 标 后 的 一 连 串 响 应。 但 是 最 后 两 个 数 据 窗 口 却 显 示 的 是 第 二 条 记 录。 有 兴 趣 的 读 者 可 以 仔 细 分 析 这 一 系 列 事 件 发 生 的 顺 序。
用 户 点 击 滚 动 条 而 使 得 上 述 代 码 对 各 个 事 件 触 发 及 其 执 行 过 程 虽 然 十 分 复 杂, 并 不 是 每 一 个 人 都 有 兴 趣 对 其 分 析 清 楚, 但 是 这 个 代 码 的 错 误 却 是 一 目 了 然 的, 只 须 稍 加 测 试 就 可 发 现。 可 是 令 人 不 解 的 是 这 段 代 码 在 美 国 却 几 乎 被 视 作 经 典, 而 且 向 美 国PowerSoft 技 术 支 持 中 心 提 出 这 个 问 题, 那 里 的 工 作 人 员 也 会 引 用 这 段 代 码 作 为 回 答。
事 实 上 解 决 这 个 问 题 的 方 法 并 不 十 分 复 杂, 我 们 只 需 将 修 改 滚 动 条 位 置 的 时 间 稍 加 改 变 就 可 以 得 到 正 确 的 结 果。 首 先 我 们 在 这 两 个 数 据 窗 口 中 各 声 明 一 个ue_scrollvertical 的 用 户 自 定 义 事 件 事 件。 传 递 一 个 参 数, 参 数 名 为vpos,integer 型, 通 过 传 值 法 传 递。 然 后 键 入 下 列 代 码:
对dw_1 的SCROLLVERTICAL 事 件 编 程 如 下:
integer li_vmax_1, li_vpos_1,li_vmax_2, li_vpos_2 decimal ld_vmax_1_percent li_vmax_1 = integer(dw_1.describe("datawindow.verticalscrollmaximum")) li_vpos_1 = integer(dw_1.describe("datawindow.verticalscrollposition")) li_vmax_2 = integer(dw_2.describe("datawindow.verticalscrollmaximum")) ld_vmax_1_percent = li_vpos_1 / li_vmax_1 li_vpos_2 = ld_vmax_1_percent * li_vmax_2 if abs( li_vpos_2 - integer(dw_1.describe ("datawindow.verticalscrollposition")) ) > 1 then //如dw_2应显示的位置与dw_2的当前位置不符,产生修改dw_2的事件 event post ue_scrollvertical( li_vpos_2 ) end if 对dw_1 的UE_SCROLLVERTICAL 事 件 编 程 如 下: 事 件: 自 定 义ue_scrollvertical 参 数:vpos integer 型 返 回 值:integer string ls_ret_code dw_2.modify("datawindow.verticalscrollposition="+string(vpos)) if ls_ret_code <> "" then beep(6) sle_1.text = "dw_1scroll = " + ls_ret_code + " vpos = " + string(vpos) // 在scrollvertical 事 件 中 将 不 能 弹 出 错 误 信 息 框 end if return 1 对dw_2 的SCROLLVERTICAL 事 件 编 程 如 下: integer li_vmax_1, li_vpos_1,li_vmax_2, li_vpos_2 string ls_ret_code decimal ld_vmax_2_percent li_vmax_2 = integer(dw_2.describe("datawindow.verticalscrollmaximum")) li_vpos_2 = integer(dw_2.describe("datawindow.verticalscrollposition")) li_vmax_1 =integer(dw_1.describe("datawindow.verticalscrollmaximum")) ld_vmax_2_percent = li_vpos_2 / li_vmax_2 li_vpos_1 = ld_vmax_2_percent * li_vmax_1 event post ue_scrollvertical(li_vpos_1) //由于dw_1使用了retreive-as-needed,可能会自动将dw_2 滚回第一行,表现重新设置dw_2 ls_ret_code = dw_2.modify ("datawindow.verticalscrollposition="+string(li_vpos_2)) // 检 验 是 否 修 改 成 功 if ls_ret_code <> "" then beep(6) sle_1.text = "dw_2 mod" + ls_ret_code end if 对dw_2 的UE_SCROLLVERTICAL 事 件 编 程 如 下: 事 件: 自 定 义ue_scrollvertical 参 数:vpos integer 型 返 回 值:integer string ls_ret_code ls_ret_code = dw_1.modify("datawindow.verticalscrollposition="+string(vpos)) // 检 验 是 否 修 改 成 功 if ls_ret_code <> ""then beep(6) sle_1.text = "dw_1 mod " + ls_ret_code end if
我 们 再 分 析 一 下 上 述 代 码 执 行 的 过 程: 用 户 点 击 了dw_1 的 滚 动 条, 使dw_1 将 滚 动 到 最 后 一 行, 产 生 了dw_1 的scrollvertical 事 件 一, 但 并 没 有 立 即 执 行, 只 放 在 了 事 件 队 列 的 后 面。 系 统 首 先 发 现 数 据 窗 口 的Primary! 缓 冲 区 中 数 据 为 空, 向 后 台 数 据 库 取 数 据, 并 使dw_2 复 位, 回 到 显 示 第 一 条 记 录 的 位 置。 这 触 发 了dw_2 的scrollvertical 事 件 二, 在 首 先 执 行 的 这 个 事 件 二 中,PowerBuilder 根 据dw_2 当 前 显 示 的 是 第 一 条 记 录 这 一 条 件, 产 生 一 个ue_scrollvertical 事 件 三, 将dw_1 也 滚 回 显 示 第 一 条 记 录, 但 是 这 个 事 件 三 并 没 有 立 即 触 发 执 行, 而 是 放 在 了 事 件 队 列 的 最 后。 当 事 件 二 执 行 完 毕 后, 系 统 响 应 用 户 对dw_1 进 行 的 向 下 翻 页 的 操 作, 并 触 发dw_1 事 件 一 的 执 行。 这 时dw_1 显 示 的 是 用 户 希 望 看 到 的 正 确 的 记 录,PowerBuilder 根 据dw_1 的 当 前 位 置, 计 算 出dw_2 与 之 相 对 应 的 位 置, 将 修 改dw_2 位 置 的 操 作ue_scrollvertical 事 件 四 放 在 了 事 件 队 列 的 最 后。 这 时 该 执 行 事 件 三 了, 事 件 三 修 改dw_1 滚 动 条 的 位 置 至 第 一 条 记 录, 这 一 操 作 触 发 了dw_1 的scrollvertical 事 件 五, 此 时 两 个 数 据 窗 口 均 显 示 的 是 第 一 条 记 录。 在 这 一 事 件 中,PowerBuilder 经 过 计 算, 得 到 了dw_2 应 显 示 的 位 置 与 当 前 位 置 相 等 的 结 论, 因 此 不 产 生ue_scrollvertical 事 件, 事 件 五 没 有 对 系 统 产 生 任 何 影 响。 接 下 来 执 行 的 是 事 件 队 列 中 仅 存 的 事 件 四, 在 这 个 事 件 里, 系 统 将dw_2 滚 动 至 正 确 的 显 示 位 置 上, 并 触 发 了dw_2 的scrollvertical 事 件 六。 在 事 件 六 中, 系 统 又 产 生dw_2 的ue_scrollvertical 事 件 七, 但 是 这 两 个 事 件 均 没 有 对 显 示 造 成 任 何 影 响, 这 两 个 事 件 执 行 过 程 中 也 没 有 再 产 生 新 的 事 件, 这 一 系 列 的 响 应 便 最 终 结 束 了。 而 最 终 的 结 果 是 两 个 数 据 窗 口 都 是 停 在 了 正 确 的 结 果 上 了。
同 理, 我 们 对 用 户 滚 动dw_2 后 系 统 的 响 应 进 行 细 致 地 分 析, 也 可 以 看 到 上 述 代 码 的 正 确 性, 这 里 就 不 再 赘 述 了。
我 们 在 本 篇 中 编 写 的 代 码 可 能 并 不 一 定 在 您 开 发 的 软 件 中 使 用 得 上, 但 是 如 果 您 能 够 对 这 一 代 码 执 行 的 过 程 全 部 自 行 分 析 清 楚, 或 者 有 足 够 的 耐 性 看 明 白 了 上 面 文 章 的 分 析, 那 么 您 对PowerBuilder 的 事 件 一 定 会 有 更 深 的 理 解。