依星源码资源网,依星资源网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

【好消息,好消息,好消息】VIP会员可以发表文章赚积分啦 !
查看: 21|回复: 0

VC++让整个对话框“完全不可操作”而不去枚举每个控件,最稳健的做法是放一个覆盖层(overlay)在对话框的客户区上,拦截所有鼠标/键盘事件。

[复制链接] 主动推送

2万

主题

2万

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
23425
发表于 4 天前 | 显示全部楼层 |阅读模式
VC++让整个对话框“完全不可操作”而不去枚举每个控件,最稳健的做法是放一个覆盖层(overlay)在对话框的客户区上,拦截所有鼠标/键盘事件。
要让整个对话框“完全不可操作”而不去枚举每个控件,最稳健的做法是放一个覆盖层(overlay)在对话框的客户区上,拦截所有鼠标/键盘事件。这样用户看起来不能操作窗口的任何控件(不需要逐个 EnableWindow(FALSE)),并且在任务完成时只需销毁覆盖层即可恢复交互。

我给出两个方案并推荐第一种(覆盖层),因为它不修改现有控件,也容易在任务开始/结束时开启/关闭。

方案 A(推荐)—— 输入拦截覆盖层(CInputBlocker)
- 创建一个专门的 CWnd 子窗口,覆盖整个对话框客户区。
- 覆盖层接收焦点并捕获鼠标,从而拦截鼠标和键盘。可显示等待光标或半透明遮罩(简单背景色即可)。
- 窗口大小随对话框调整(在 WM_SIZE 中调整覆盖层大小)。

示例实现(精简,可直接拷贝到你的项目并按需调整)。

1) CInputBlocker.h
  1. #pragma once
  2. #include <afxwin.h>

  3. class CInputBlocker : public CWnd
  4. {
  5. public:
  6.     CInputBlocker();
  7.     virtual ~CInputBlocker();

  8.     // create overlay as child of pParent (cover client area)
  9.     BOOL Create(CWnd* pParent);

  10.     // show / hide helper
  11.     void ShowOverlay();
  12.     void HideOverlay();

  13. protected:
  14.     afx_msg BOOL OnEraseBkgnd(CDC* pDC);
  15.     afx_msg void OnLButtonDown(UINT nFlags, CPoint point) { /* 拦截 */ }
  16.     afx_msg void OnLButtonUp(UINT nFlags, CPoint point) { /* 拦截 */ }
  17.     afx_msg void OnMouseMove(UINT nFlags, CPoint point) { /* 拦截 */ }
  18.     afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { /* 拦截 */ }
  19.     afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { /* 拦截 */ }
  20.     afx_msg void OnKillFocus(CWnd* pNewWnd);
  21.     DECLARE_MESSAGE_MAP()

  22. private:
  23.     CWnd* m_pParent;
  24. };
复制代码


2.CInputBlocker.cpp

  1. #include "pch.h"
  2. #include "CInputBlocker.h"

  3. BEGIN_MESSAGE_MAP(CInputBlocker, CWnd)
  4.     ON_WM_ERASEBKGND()
  5.     ON_WM_LBUTTONDOWN()
  6.     ON_WM_LBUTTONUP()
  7.     ON_WM_MOUSEMOVE()
  8.     ON_WM_KEYDOWN()
  9.     ON_WM_CHAR()
  10.     ON_WM_KILLFOCUS()
  11. END_MESSAGE_MAP()

  12. CInputBlocker::CInputBlocker() : m_pParent(nullptr) {}
  13. CInputBlocker::~CInputBlocker() {}

  14. BOOL CInputBlocker::Create(CWnd* pParent)
  15. {
  16.     if (!pParent) return FALSE;
  17.     m_pParent = pParent;

  18.     CRect rc;
  19.     pParent->GetClientRect(&rc);

  20.     // WS_CHILD | WS_VISIBLE | WS_TABSTOP 使其可获得焦点
  21.     DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
  22.     // 注册一个临时类名(默认类也可以直接用 AfxRegisterWndClass)
  23.     LPCTSTR pszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW);

  24.     BOOL ok = CWnd::CreateEx(0, pszClass, _T("InputBlocker"), dwStyle, rc, pParent, 0);
  25.     if (ok) {
  26.         // 设置光标为等待(可选)
  27.         ::SetCursor(::LoadCursor(NULL, IDC_WAIT));
  28.         // 让 overlay 获得焦点并捕获鼠标
  29.         SetFocus();
  30.         SetCapture();
  31.     }
  32.     return ok;
  33. }

  34. void CInputBlocker::ShowOverlay()
  35. {
  36.     if (GetSafeHwnd() == NULL) {
  37.         Create(m_pParent);
  38.     } else {
  39.         ShowWindow(SW_SHOW);
  40.         SetFocus();
  41.         SetCapture();
  42.     }
  43. }

  44. void CInputBlocker::HideOverlay()
  45. {
  46.     if (GetSafeHwnd()) {
  47.         if (GetCapture() == this->GetSafeHwnd()) ::ReleaseCapture();
  48.         ShowWindow(SW_HIDE);
  49.         // 将焦点还回父窗口
  50.         if (m_pParent && ::IsWindow(m_pParent->GetSafeHwnd())) m_pParent->SetFocus();
  51.         DestroyWindow();
  52.     }
  53. }

  54. BOOL CInputBlocker::OnEraseBkgnd(CDC* pDC)
  55. {
  56.     // 填充半透明颜色效果(简单灰色)
  57.     CRect r;
  58.     GetClientRect(&r);
  59.     CBrush br(RGB(200,200,200)); // 可改为更深、更浅
  60.     pDC->FillRect(&r, &br);
  61.     return TRUE;
  62. }

  63. void CInputBlocker::OnKillFocus(CWnd* pNewWnd)
  64. {
  65.     // 始终保持焦点以拦截键盘(如果需要)——可以尝试重新设置
  66.     if (GetSafeHwnd() && ::IsWindowVisible(GetSafeHwnd())) {
  67.         SetFocus();
  68.     }
  69. }
复制代码



使用方式(在你的对话框类中):
  1. // CMyDialog.h
  2. #include "CInputBlocker.h"
  3. class CMyDialog : public CDialogEx
  4. {
  5.     // ...
  6.     CInputBlocker m_blocker;
  7.     // ...
  8. };

  9. // CMyDialog.cpp
  10. BOOL CMyDialog::OnInitDialog()
  11. {
  12.     CDialogEx::OnInitDialog();

  13.     // 初始化时不显示覆盖层,只有在需要屏蔽时调用
  14.     // m_blocker.Create(this); // 可提前创建,也可在需要时创建

  15.     return TRUE;
  16. }

  17. // 在需要禁止交互时(例如启动后台任务)
  18. void CMyDialog::StartLongTask()
  19. {
  20.     if (m_blocker.GetSafeHwnd() == NULL) {
  21.         m_blocker.Create(this);
  22.     }
  23.     m_blocker.ShowWindow(SW_SHOW);
  24.     m_blocker.SetFocus();
  25.     m_blocker.SetCapture();

  26.     // 启动后台线程/任务(请勿在 UI 线程做长耗时操作)
  27. }

  28. // 任务完成时调用
  29. void CMyDialog::EndLongTask()
  30. {
  31.     m_blocker.HideOverlay();
  32. }
复制代码



细节和建议
- 这个覆盖层会拦截鼠标事件,因为它位于所有子控件之上并捕获鼠标;它也获取焦点从而拦截键盘事件。
- 如果你需要“半透明效果”,可以在 OnEraseBkgnd 用 GDI 绘制渐变或使用带 alpha 的位图。但实现真正的透明/半透明覆盖(alpha blending)通常需要使用顶层分层窗口(UpdateLayeredWindow),实现复杂且对 child/owner 关系要注意;上面灰色遮罩通常就足够表示“不可操作”状态。
- 覆盖层创建为子窗口,当对话框调整大小时它不会自动调整位置/大小(Create 时设置初始 size);建议在对话框的 WM_SIZE/OnSize 处理函数里调整覆盖层大小:

  1. void CMyDialog::OnSize(UINT nType, int cx, int cy)
  2. {
  3.     CDialogEx::OnSize(nType, cx, cy);
  4.     if (m_blocker.GetSafeHwnd()) {
  5.         CRect rc;
  6.         GetClientRect(&rc);
  7.         m_blocker.MoveWindow(&rc);
  8.     }
  9. }
复制代码

- 覆盖层用 `SetCapture()` 时请确保在 Hide 时 `ReleaseCapture()`(示例已处理)。
- 覆盖层不会阻止系统级快捷(如 Ctrl+Alt+Del)——但会拦截常见的鼠标/键盘交互。若需阻止 Alt+F4,请在对话框的 OnClose/OnCancel/OnOK 中检查状态并拒绝关闭(示例在前面已经给出)。

方案 B(简单但有局限)—— 弹出模态对话框(DoModal)
- 如果你的任务在 UI 线程中运行,这不合适(会阻塞线程)。如果任务在后台线程运行,则可以创建一个简单的模态对话框(“处理中...” 样式)并在后台线程执行任务,主对话框在模态对话框显示时被系统自动禁用。
- 缺点:与后台线程配合要注意线程同步与关闭对话框的方式(需通过 PostMessage/SendMessage 在 UI 线程关闭模态对话框)。

小结(推荐)
- 推荐使用覆盖层实现:不修改现有控件状态、恢复简单、用户体验好(可显示等待色块/说明)。
- 我可以直接把上面的 `CInputBlocker` 代码生成到你的项目里并帮你在对话框模版中集成(包含 resize、Start/End 调用),或者如果你想要更漂亮的半透明效果我也可以用分层窗口实现(但会稍复杂)。

你想我现在:
- 把 `CInputBlocker` 源文件直接添加到你的工程(并在一个示例对话框里集成 OnSize、StartLongTask/EndLongTask 示例);还是
- 给出一个更高级的分层(alpha)覆盖层实现?

相关帖子

扫码关注微信公众号,及时获取最新资源信息!下载附件优惠VIP会员6折;永久VIP4折
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

免责声明:
1、本站提供的所有资源仅供参考学习使用,版权归原著所有,禁止下载本站资源参与商业和非法行为,请在24小时之内自行删除!
2、本站所有内容均由互联网收集整理、网友上传,并且以计算机技术研究交流为目的,仅供大家参考、学习,请勿任何商业目的与商业用途。
3、若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。
4、论坛的所有内容都不保证其准确性,完整性,有效性,由于源码具有复制性,一经售出,概不退换。阅读本站内容因误导等因素而造成的损失本站不承担连带责任。
5、用户使用本网站必须遵守适用的法律法规,对于用户违法使用本站非法运营而引起的一切责任,由用户自行承担
6、本站所有资源来自互联网转载,版权归原著所有,用户访问和使用本站的条件是必须接受本站“免责声明”,如果不遵守,请勿访问或使用本网站
7、本站使用者因为违反本声明的规定而触犯中华人民共和国法律的,一切后果自己负责,本站不承担任何责任。
8、凡以任何方式登陆本网站或直接、间接使用本网站资料者,视为自愿接受本网站声明的约束。
9、本站以《2013 中华人民共和国计算机软件保护条例》第二章 “软件著作权” 第十七条为原则:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。若有学员需要商用本站资源,请务必联系版权方购买正版授权!
10、本网站如无意中侵犯了某个企业或个人的知识产权,请来信【站长信箱312337667@qq.com】告之,本站将立即删除。
郑重声明:
本站所有资源仅供用户本地电脑学习源代码的内含设计思想和原理,禁止任何其他用途!
本站所有资源、教程来自互联网转载,仅供学习交流,不得商业运营资源,不确保资源完整性,图片和资源仅供参考,不提供任何技术服务。
本站资源仅供本地编辑研究学习参考,禁止未经资源商正版授权参与任何商业行为,违法行为!如需商业请购买各资源商正版授权
本站仅收集资源,提供用户自学研究使用,本站不存在私自接受协助用户架设游戏或资源,非法运营资源行为。
 
在线客服
点击这里给我发消息 点击这里给我发消息 点击这里给我发消息
售前咨询热线
312337667

微信扫一扫,私享最新原创实用干货

QQ|免责声明|小黑屋|依星资源网 ( 鲁ICP备2021043233号-3 )|网站地图

GMT+8, 2025-12-7 14:55

Powered by Net188.com X3.4

邮箱:312337667@qq.com 客服QQ:312337667(工作时间:9:00~21:00)

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