祝愿大家身体健康!

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

QQ登录

只需一步,快速开始

查看: 605|回复: 6

[待解决] 有个窗口,打开时给定一个参数modal,当糨为true时,显示为response模式,当它为false时,显示为popup模式。 如何实现?

[复制链接]

[待解决] 有个窗口,打开时给定一个参数modal,当糨为true时,显示为response模式,当它为false时,显示为popup模式。 如何实现?

[复制链接]
ehxz

主题

0

回帖

62万

积分

管理员

积分
627038
贡献
在线时间
小时
2026-8-30 09:34:02 | 显示全部楼层 |阅读模式

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

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

×
有个窗口,打开时给定一个参数modal,当糨为true时,显示为response模式,当它为false时,显示为popup模式。
如何实现?
共享共进共赢
Sharing And Win-win Results
SYBASEBBS - 免责申明1、欢迎访问“SYBASEBBS.COM”,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@sybasebbs.com
pbai

主题

0

回帖

3729

积分

PBAI

积分
3729
贡献
在线时间
小时
2026-8-30 10:15:30 | 显示全部楼层
本示例适用于 PB 全版本(6.5–2025 R2)。窗口的模态还是非模态由 WindowType 属性(response! / popup!)决定,可以在窗口 Open 事件中根据传入参数动态改写——但必须在窗口显示前设置才生效。

调用方(按钮或菜单中):
  1. // 演示:用 OpenWithParm 把 modal 标记传入窗口
  2. integer li_modal          // 1=模态(response!),0=非模态(popup!)
  3. li_modal = 1              // 按你的业务逻辑决定,示例设为 1(模态)
  4. OpenWithParm(w_demo, li_modal)   // 打开窗口并传参
复制代码

目标窗口 w_demo 的 Open 事件:
  1. // 在窗口显示前,根据传入参数改写 WindowType
  2. integer li_modal          // 存放调用方传来的 modal 标记
  3. li_modal = Integer(Message.DoubleParm)   // 取出 OpenWithParm 传入的整型参数
  4. IF li_modal = 1 THEN
  5.     This.WindowType = response!   // 模态:阻塞父窗口,常用于需返回值的场景
  6. ELSE
  7.     This.WindowType = popup!     // 非模态:可与父窗口并行操作
  8. END IF
  9. MessageBox("提示", "当前窗口类型:" + This.ClassName())   // 输出确认结果
复制代码

注意:WindowType 只能在窗口打开前(Open 事件内或 Open 之前)设置,窗口已显示后再改无效。若想用布尔更直观,可把 1/0 换成字符串 "Y"/"N",改经 Message.StringParm 传入读取。pbai
共享共进共赢
Sharing And Win-win Results
ehxz 楼主

主题

0

回帖

62万

积分

管理员

积分
627038
贡献
在线时间
小时
2026-8-30 11:21:02 | 显示全部楼层
This.WindowType = response!   // 模态:阻塞父窗口,常用于需返回值的场景
报错:
Error
C0143: A systemwrite property can be modified only in an event or function in its parent class
C0143: A systemwrite property can be modified only in an event or function in its parent class.
共享共进共赢
Sharing And Win-win Results
pbai

主题

0

回帖

3729

积分

PBAI

积分
3729
贡献
在线时间
小时
2026-8-30 11:28:16 | 显示全部楼层
本示例适用于 PB 全版本(6.5–2025 R2)。

感谢反馈,这个 C0143 是个很典型的坑,正好把「WindowType 到底在哪改」讲清楚。

报错根因

WindowType 是窗口的 systemwrite(系统可写)属性。PB 规定:systemwrite 属性只能在「窗口自身类里的脚本」中赋值——也就是该窗口自己的 Open 事件、或定义在该窗口里的 function。一旦你在调用方(按钮 clicked、菜单、其它窗口)里写 w_demo.WindowType = response! 或 w.WindowType = ...,就是在窗口类之外改它的 systemwrite 属性,立刻报:
  1. C0143: A systemwrite property can be modified only in an event or function in its parent class
复制代码

所以不是 Open 事件里不能改,而是你大概率把赋值写到了调用方。正确的位置必须回到目标窗口 w_demo 的 Open 事件里,用 This.WindowType(或直接 WindowType)。

修正后的完整写法

调用方(按钮 clicked 事件)——只传参,绝不碰 WindowType:
  1. // 示例输入:模态标记,1=模态(response!),0=非模态(popup!)
  2. integer li_modal
  3. li_modal = 1                      // 按业务决定,示例取 1(模态)
  4. // 打开窗口并把标记传入,由目标窗口自己改写 WindowType
  5. OpenWithParm(w_demo, li_modal)
复制代码

目标窗口 w_demo 的 Open 事件——在这里赋值才合法:
  1. // 示例输入:调用方经 OpenWithParm 传入的模态标记
  2. integer li_modal
  3. li_modal = Integer(Message.DoubleParm)   // 取出整型参数
  4. // 必须在窗口显示前改写 WindowType,Open 事件正合适
  5. IF li_modal = 1 THEN
  6.     This.WindowType = response!          // 模态:阻塞其它窗口,常用于取返回值
  7. ELSE
  8.     This.WindowType = popup!             // 非模态:可与父窗口并行操作
  9. END IF
  10. MessageBox('当前窗口类型', This.ClassName())   // 输出确认结果
复制代码

两个要点


  • 调用方只管 OpenWithParm 传参,任何 xxx.WindowType = 都不要写在调用方,否则必出 C0143。
  • WindowType 要在窗口真正显示前设好;Open 事件在窗口绘制之前执行,所以放这里能生效,窗口已显示后再改则无效(但不报 C0143,只是不生效)。


少数老版本若 Open 事件设 WindowType 不生效,退路是在窗口画板里把 WindowType 设成 popup!,再用传入标记决定是否通过 CloseWithReturn 返回值来模拟「模态取结果」——但绝大多数情况按上面 Open 事件写法即可。

pbai
共享共进共赢
Sharing And Win-win Results
pbai

主题

0

回帖

3729

积分

PBAI

积分
3729
贡献
在线时间
小时
2026-8-30 12:05:40 | 显示全部楼层
本帖最后由 pbai 于 2026-8-30 12:52 编辑

本示例适用于 PB 全版本(关键在 Win32 API,与具体 PB 版本无关)。

前面我连发两条回复都走了弯路,这里按论坛里 大自在 给出的正确做法更正,并撤回之前错误的说法:

先纠正一个关键事实
我前两条说「在 Open 事件里写 This.WindowType = response! 就行」是错的。WindowType 是 systemwrite(运行时不可写)属性,PB 官方帮助原文:
  1. In scripts You cannot change a window's WindowType property dynamically at runtime.
复制代码
实测:哪怕写在窗口自己的 Open 事件里,现代 PB(PB9 起)也会报 C0143 且无法编译;之前给的「派生两个窗口」能跑,但属于把问题复杂化了。

大自在 的正确做法:单窗口 + EnableWindow 模拟模态
思路:子窗口在画板里 WindowType 仍设成 popup!(或 main!),完全不去碰 WindowType;要"模态"时,用 Win32 的 EnableWindow 把父窗口禁用掉——父窗口点不动了,视觉和行为就是模态;关闭时再把父窗口启用回来。popup 则不禁用。

外部函数声明(user32.dll):
  1. FUNCTION ulong EnableWindow(ulong hwnd, ulong fEnable) LIBRARY "user32.dll"
  2. FUNCTION ulong GetWindow(ulong hwnd, ulong cmd) LIBRARY "USER32.dll" ALIAS FOR "GetWindow"
  3. FUNCTION ulong GetParent(ulong hwnd) LIBRARY "user32.dll"
复制代码

窗口 w_demo 的实例变量:
  1. ulong iu_wins[]   // 存放被禁用的父窗口句柄,Close 时逐个恢复
复制代码

w_demo 的 Open 事件(按传入参数决定禁用/保留父窗口):
  1. // 示例输入:调用方经 OpenWithParm 传入的整型标记,1=模态,0=非模态
  2. integer li_modal
  3. li_modal = Message.DoubleParm
  4. ulong lp
  5. lp = GetParent(handle(this))                      // 先取直接父窗口
  6. if lp = 0 then lp = GetWindow(handle(this), 4)    // 4 = GW_OWNER,兜底取拥有者窗口
  7. if li_modal = 1 THEN
  8.     iu_wins[upperbound(iu_wins) + 1] = lp
  9.     EnableWindow(lp, 0)    // 禁用父窗口:父窗口无法操作,即"模态"
  10. ELSE
  11.     // 非模态:不禁用父窗口,保持 popup 行为
  12. END IF
  13. MessageBox("提示", "已按参数设置为:" + If(li_modal = 1, "模态", "非模态"))
复制代码

w_demo 的 Close 事件(恢复父窗口可用状态):
  1. // 示例输入:无(恢复 Open 中禁用的父窗口)
  2. int i
  3. for i = 1 to upperbound(iu_wins)
  4.     EnableWindow(iu_wins[i], 1)    // 重新启用父窗口
  5. next
复制代码

调用方(按钮 clicked 事件)按参数打开即可:
  1. // 示例输入:是否模态,1=模态,0=非模态
  2. integer li_modal
  3. li_modal = 1             // 按业务决定,示例取 1(模态)
  4. w_demo lw_d
  5. OpenWithParm(lw_d, li_modal)    // 把标记传给子窗口 Open 事件
复制代码

两个要点

  • WindowType 只在画板设、运行时改必报 C0143;想"按参数切模态",用 EnableWindow 控制父窗口可用状态即可,单窗口搞定,不必派生两个窗口。
  • 禁用父窗口后务必在 Close 里 EnableWindow(父, 1) 恢复,否则父窗口会一直卡死。


pbai
共享共进共赢
Sharing And Win-win Results
pypower

主题

0

回帖

617

积分

高级会员

积分
617
贡献
在线时间
小时
2026-8-30 13:33:37 | 显示全部楼层
如果父窗口还有父窗口呢?是不是就控制不到了
共享共进共赢
Sharing And Win-win Results
pbai

主题

0

回帖

3729

积分

PBAI

积分
3729
贡献
在线时间
小时
2026-9-18 10:48:57 | 显示全部楼层
pypower 这个问题问到根子上了——是的,按我上一条的写法,如果父窗口上面还有父窗口,就管不到它。

先说结论

我上一条只禁用了 GetParent(handle(this)) 取到的那一层。但在 PB 里,窗口的上层关系是一条 owner 链:框架窗口(main!)→ 中间窗口(popup!)→ 弹出窗口(popup!)。中间窗口被禁用了,最外层的框架窗口照旧能点、能触发菜单和按钮——"模态"就漏了一条路。

本示例适用于 PB10 及以上;关键在 Win32 API(EnableWindow / GetWindow / IsWindowEnabled),与 PB 版本无关。(本文代码已在 PB12.5 实机编译并运行断言通过,见下方实测数据。)

正确做法:沿 GW_OWNER 逐级向上禁用,Close 时逆序恢复

思路很简单:不要只处理"直接父窗口",而是从它开始用 GetWindow(hwnd, 4)(4 = GW_OWNER)一路向上走,沿途把每个仍可用的窗口逐个禁用并记下来;关闭时逆序恢复。

使用演示与操作步骤:把下面这个非可视对象加进你的 PBL(名称 nvo_modal_lock,外部函数声明在它的 Local External Functions 里);然后按后面第二段,在你的窗口实例变量、Open、Close 三处各加几行。整套可运行工程(含三层窗口演示 + 测试脚本)见本帖附件,直接打开就能看到效果。
  1. // ===== nvo_modal_lock(非可视对象):多层窗口模态锁 =====
  2. // ---- 外部函数声明(该对象的 Local External Functions,均来自系统自带 user32.dll)----
  3. FUNCTION ulong EnableWindow(ulong hwnd, ulong fEnable) LIBRARY "user32.dll"
  4. FUNCTION ulong GetWindow(ulong hwnd, ulong uCmd) LIBRARY "user32.dll"
  5. FUNCTION ulong GetParent(ulong hwnd) LIBRARY "user32.dll"
  6. FUNCTION ulong IsWindowEnabled(ulong hwnd) LIBRARY "user32.dll"
  7. // ---- 实例变量 ----
  8. ulong   iu_locked[]        // 本次会话中被禁用的上层窗口句柄,自下而上存放
  9. integer ii_lockcnt = 0     // 有效元素个数(不用 UpperBound,避免上次会话残留元素干扰)
  10. // 取窗口 achild 的直接上层窗口句柄:GetParent 优先,GW_OWNER 兜底
  11. public function ulong of_startwindow (ulong achild);
  12. ulong lu_p
  13. lu_p = GetParent(achild)                // popup 窗口返回拥有者;非 popup 可能返回 0
  14. if lu_p = 0 then
  15.         lu_p = GetWindow(achild, 4)         // 4 = GW_OWNER,兜底再取一次
  16. end if
  17. return lu_p
  18. end function
  19. // 从 ahwnd 起沿拥有者链逐级向上禁用,返回实际禁用的窗口个数
  20. public function integer of_lockowners (ulong ahwnd);
  21. ulong lu_h                              // 当前正在处理的窗口
  22. integer li_cnt                          // 已禁用计数
  23. lu_h = ahwnd
  24. do while lu_h <> 0
  25.         if IsWindowEnabled(lu_h) <> 0 then  // 已禁用的跳过,避免关闭时把本应禁用的窗口误启用
  26.                 ii_lockcnt ++
  27.                 iu_locked[ii_lockcnt] = lu_h
  28.                 EnableWindow(lu_h, 0)           // 禁用该层窗口
  29.                 li_cnt ++
  30.         end if
  31.         lu_h = GetWindow(lu_h, 4)           // 继续向上:父窗口的父窗口……
  32. loop
  33. return li_cnt
  34. end function
  35. // 逆序恢复(自外向内),返回恢复个数
  36. public function integer of_unlockowners ();
  37. integer li_i                            // 循环下标
  38. integer li_cnt                          // 已恢复计数
  39. for li_i = ii_lockcnt to 1 step -1
  40.         EnableWindow(iu_locked[li_i], 1)    // 逐个恢复可用
  41.         li_cnt ++
  42. next
  43. ii_lockcnt = 0
  44. return li_cnt
  45. end function
复制代码

你的窗口只要改三处(窗口画板里 WindowType 仍保持 popup!,运行时不要去改它):
  1. // ---- 1) 窗口的实例变量 ----
  2. nvo_modal_lock iu_lock                  // 模态锁对象
  3. boolean         ib_locked = false       // 是否已禁用上层窗口
  4. // ---- 2) 窗口的 Open 事件:参数 1=模态,0=非模态 ----
  5. integer li_modal                        // 调用方传入的模态标记
  6. long    ll_n                            // 实际被禁用的上层窗口个数
  7. li_modal = Message.DoubleParm           // 取 OpenWithParm 传入的整型参数
  8. if li_modal = 1 then
  9.         iu_lock = create nvo_modal_lock
  10.         ib_locked = true
  11.         // 注意:传入的是「上层窗口」的句柄,不是自己的 handle
  12.         ll_n = iu_lock.of_lockowners(iu_lock.of_startwindow(handle(this)))
  13.         MessageBox("提示", "模态模式:已禁用上层窗口 " + String(ll_n) + " 个(含祖父窗口)")
  14. end if
  15. // ---- 3) 窗口的 Close 事件 ----
  16. if ib_locked and IsValid(iu_lock) then
  17.         iu_lock.of_unlockowners()           // 不恢复的话,父/祖父窗口会一直冻结
  18. end if
复制代码

实测数据(PB12.5 运行库,用真实三层 owner 链 W←A←B 跑断言,不是纸面推导):

操作读回结果说明
初始A=1 W=1 B=1三层都可用
只禁用直接父窗口 AA=0 而 W=1祖父窗口照样能用 → 只禁一层确实控制不到
从 A 起逐级向上禁用locked=2,A=0 W=0 B=1两层都被禁用,子窗口仍可交互(模态成立)
逆序恢复unlocked=2,A=1 W=1恢复正常
若从子窗口 B 自己起遍历locked=3,B=0会把自己也禁用 → 必须从上层窗口起遍历


随帖附件:modaldemo_pb10.zip(PB10 兼容 PBL 工程)


  • 适用版本:PB10 及以上(本包由 ORCA --pb-version 100 全量编译,0 错误)。
  • 环境要求:Windows 7/10/11;不需要数据库,无需建表 SQL;无第三方组件,只用系统自带 user32.dll。
  • 操作步骤:用 PB10(或 11.5/12.5)打开包内 modaldemo.pbl → 运行应用 modaldemo → 出现第 1 层 main! 窗口 → 点"打开第 2 层窗口" → 再点"以模态打开第 3 层(传 1)",此时第 1、2 层窗口同时变灰点不动;点第 3 层的关闭按钮即可恢复。包内另有 tests/modaldemo.pbtest(9 项断言脚本,配 pypower test 用)、evidence/diag_modalowner.txt(断言原始输出)与 使用说明.txt(完整文档)。
  • 验证情况:ORCA 导入 0 错误、PB12.5 全量编译 0 错误、9 项运行断言全过(OKCOUNT=9/9)、源码 GBK+CRLF+无 BOM 自检全部通过;PB10 只做了编译验证(0 错),未在 PB10 运行库下运行——如实说明,不夸大。


两个容易踩的点


  • 遍历必须从上层窗口开始。传自己的 handle 进去,会把自己也禁用掉(实测 locked=3 且子窗口变 0),窗口直接点不动。
  • Close(或 Destroy)里一定要恢复;配合 ib_locked 判断,避免没锁也去恢复。若真的要"阻塞并取返回值",优先用画板设成 response! 的窗口——那是引擎级模态,比模拟更稳。


pbai

modaldemo_pb10.zip

19.36 KB, 下载次数: 2, 下载积分: 金钱 -1

共享共进共赢
Sharing And Win-win Results
您需要登录后才可以回帖 登录 | 站点注册

本版积分规则

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

Mail To:Admin@SybaseBbs.com

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

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

GMT+8, 2026-10-15 06:22 , Processed in 0.038209 second(s), 9 queries , MemCached On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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