65.9K
CodeProject 正在变化。 阅读更多。
Home

CBalloonMsg - AfxMessageBox 的一个易于使用的非模态替代品

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (27投票s)

2008年3月6日

CPOL

5分钟阅读

viewsIcon

130022

downloadIcon

3747

轻松使用气球提示框非模态地传达提示/消息

引言

在开发一个新应用程序时,我希望使用气球风格的工具提示来向用户传达简单的消息,而不是依赖传统的 ::AfxMessageBox。这样可以减少对用户输入错误的警告,例如编辑错误、未填写字段等,显得不那么干扰。

我找到了一篇 CodeProject 的现有文章,似乎正是我想要的:《气球帮助作为 MessageBox() 的非模态替代品》。然而,仔细检查后,我决定不使用它,因为它没有使用 Windows 原生的气球提示。它自己绘制气球,而且在我撰写本文时,我认为它需要为 Vista 进行一些小更新。肯定有更简单的方法可以使用真正的工具提示控件吧?我没有找到,于是我写了自己的,叫做 CBalloonMsg

必备组件

在继续之前,请注意,我编写此代码是为了在主题感知型应用程序(即具有请求公共控件 v6 的清单的应用程序)中运行,这些应用程序运行在 Vista 或 XP(最好是 SP2)上。我没有尝试在 Windows 2000 上使用它,但我很确定它看起来不会对。更不用说 NT4 和 Win9x 了!

Using the Code

这再简单不过了

  • BalloonMsg.hBalloonMsg.cpp 添加到您的项目中。
  • 确保您的 stdafx.hWINVER_WIN32_WINNT 设置为 0x0501 或更高版本,并确保您的 *.rc2 文件中有一个清单(请查看演示项目!)
  • 最后,使用 ShowShowForCtrl static 方法之一来显示气球消息,例如:

    CBalloonMsg::ShowForCtrl( _T("Test Title"), _T("Test Text"),
        &my_ctrl, hIcon );

Show/ShowForCtrl 方法会在指定的窗口位置或控件上方显示工具提示。默认情况下,工具提示会显示 10 秒钟然后自动关闭。如果有焦点变化或鼠标明显移动,它会提前关闭。您可以轻松更改“显示时长”或自动弹出时间(稍后会详细介绍),并且可以使用 RequestCloseAll() static 方法随时关闭或“弹出”气球。

它是如何工作的(简而言之)

当您调用 Show 方法之一时,会创建一个单独的用户界面线程。该线程反过来会在当前鼠标位置创建一个自己的小型透明窗口,然后开始处理该窗口的消息队列。该窗口充当工具提示控件的父窗口,并调用 CToolTipCtrl::RelayEvent 以确保工具提示优先处理所有相关的 Windows 消息。工具提示的初始延迟为零毫秒,因此一旦激活就会立即显示。当工具提示最终关闭时,会调用 PostQuitMessage,该函数会销毁透明父窗口并终止线程。包装器线程会自行删除以确保完整性。

大致就是这样。您可以在代码的注释中找到有关内部机制的更详细信息。

关注点

SafeShowMsg 和 BalloonsEnabled

默认情况下,Windows 允许使用气球工具提示。但是,有一个注册表修改可以阻止气球的显示。这通常用于禁用 Windows 偶尔弹出的那些冗余且分散注意力的托盘气球。

该修改涉及在 HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced 下将 DWORDEnableBalloonTips 设置为零(感谢 DerMeister 的报告!)。

为了解决这个问题,最新版本的类现在包括了两个额外的方法:BalloonsEnabledSafeShowMsg

BalloonsEnabled() 如果用户没有禁用气球工具提示,则返回 TRUE。在此基础上,SafeShowMsg() 会在启用了气球的情况下使用气球显示消息,并在用户关闭气球的情况下回退到 AfxMessageBox。演示项目展示了此函数的功能 - 请查看 CTTTestDlg::DoDataExchange

图标

您可以为 Show 调用提供自己的图标,或者使用 Windows 内置图标,方法是使用 MSDN 文档中关于 TTM_SETTITLE 的特殊值:1 表示信息,2 表示警告,3 表示错误。

您还可以通过设置其 static 成员的新值来更改 CMessageBalloon 的许多默认值

s_nTimerStep 默认为 30 毫秒。确定检查气球终止状态的频率(请参阅版本 2 的历史记录更改)。
s_nAutoPop 气球自动关闭之前的时间(以毫秒为单位)。默认为 10 秒。设置为 0 表示气球将一直显示,直到通过焦点更改、用户操作等方式关闭。
s_nMaxTipWidth 最大提示宽度(以像素为单位)。使气球使用换行。
s_nToolBorder 鼠标移动多少像素距离后气球才会弹出

GetGUIThreadInfo

最后,请注意使用了方便的函数 GetGUIThreadInfo,它允许我们在另一个线程中检查焦点窗口。

Damir Valiulin 适配的 VC6 特殊版本

文章顶部的第三个下载是 VC6 版本的演示项目。它包含与原始代码的一些细微差别,如下所示:

  • 为能在 VC6 下编译进行的小修改
  • 检查 Win32(没有气球提示)和注册表禁用技巧
  • 对函数调用进行了简化(移除了带有 string ID 的调用)

非常感谢 Damir 的贡献!

历史

版本 1:2008 年 3 月 6 日


版本 2:2008 年 3 月 16 日

改为使用跟踪工具提示,以对抗当对话框靠近屏幕边缘时气球及其指针定位的异常。使用 TTF_TRACK 需要其他更改。

  • 现在通过 TTM_TRACKPOSITION 消息重新定位气球,而不是使用 SetWindowPos
  • 现在使用 TTM_TRACKACTIVATE 来激活工具提示,而不是 MFC 的 Activate() 方法。
  • 还创建了一个定时器,设置为每大约 30 毫秒触发一次(可通过上面的 s_nTimerStep 配置)。定时器使我们有机会发现主线程拥有的窗口状态的变化,这些变化应导致气球关闭。它还允许我们在 s_nAutoPop 不为零时实现自动关闭间隔(自动弹出)。

版本 3:2008 年 3 月 23 日

  • 添加了 SafeShowMsgBalloonsEnabled,用于检测用户是否通过注册表修改禁用了气球,并回退到 AfxMessageBox
  • 添加了重载,以同时接受用于目标控件的 HWNDCWnd*

版本 4:2008 年 3 月 30 日

  • 添加了 VC6 版本,由 Damir Valiulin 亲切适配。
© . All rights reserved.