XBalloonMsg - a non-MFC balloon-shaped message box

来源:百度文库 编辑:神马文学网 时间:2024/04/26 21:00:03
http://www.codeproject.com/KB/miscctrl/XBalloonMsg.aspx
Download demo project - 245 Kb
Introduction
I was inspired to create XBalloonMsg when I saw great article by Paul Roberts,CBalloonMsg. I like balloon messages because visually they seem to be more lightweight and less obtrusive than standard message box:
Standard
XBalloonMsg

This is ideal way to draw user's attention to a control or area of the screen. I decided to learn more aboutballoon tooltips which are used to show balloon messages, and find out how to integrate them in my projects.XBalloonMsg Features and Behaviors
Here are features I wanted in XBalloonMsg:The balloon message box should display until some user action occurs - clicking mouse, pressing key (including Esc key), changing focus to another window, etc. - or until a specified time interval has passed.
In addition to closing balloon message box after user action mentioned above, there should be close button () on balloon message box, to give user a visual cue as to how to dismiss balloon message.
You should be able to specify title and message strings either embedded or as resource IDs.
It should be possible to display very long message strings - longer than the standard 80-character strings you are limited to with LPSTR_TEXTCALLBACK tooltips.
MFC should not be used, to allow for use in any C++ project.
The balloon message box should work properly on Vista.
In case balloon tips are disabled inregistry, regular (rectangular) tooltip should be displayed.
The programmatic interface to XBalloonMsg is very simple: just two functions, one of which you only need if you want to dismiss message balloon for some particular reason:
Function Description
void Show() Show balloon message
void Destroy() Remove balloon message from screen
Show() Function
Here are details for Show() function:
Show
The Show function creates, displays, and operates a balloon message box. The message box contains an application-defined message and title, a close button, and an optional icon.
void Show( LPCTSTR lpszTitle,
LPCTSTR lpszMsg,
HWND hCtrl,
HWND hParent,
HINSTANCE hInstance,
UINT nIcon = TTI_INFO,
BOOL bUseBalloonTips = TRUE,
UINT nTimeOutSeconds = 0,
LPRECT pRect = NULL
);
Parameters
lpszTitle
[in] Pointer to a null-terminated string that contains the balloon message title. If this parameter is NULL, no title and no icon will be displayed. The special symbol LPCTSTR_DEFAULT_TITLE may be used; it will cause the executable module name to be displayed.
lpszMsg
[in] Pointer to a null-terminated string that contains the balloon message to be displayed. Must not be null. Text callbacks (LPSTR_TEXTCALLBACK) are not used, so the text string can be as long as you want, up to the size of the internal text buffer m_szMsg.
hCtrl
[in] Handle to the control that the message is being displayed for. Must not be null.
hParent
[in] Handle to the parent window of the control. Must not be null.
hInstance
[in] Handle to the instance of the module that contains the string resource. May be null if string resource is not used.
nIcon
[in] Specifies the icon to associate with the balloon message. This can be one of the following values: Value Meaning
TTI_ERROR Use the error icon
TTI_INFO Use the information icon
TTI_NONE Use no icon
TTI_WARNING Use the warning icon
This parameter may also be the handle of an icon obtained fromLoadIcon orLoadImage functions. If not present, this parameter defaults to TTI_INFO.
bUseBalloonTips
[in] Specifies whether balloon tips are to be used to display the message. If this parameter is TRUE, balloon tips will be used unless disabled in the registry. If this parameter is FALSE, regular tooltips will be used, regardless of value of registry key. If not present, this parameter defaults to TRUE.
nTimeOutSeconds
[in] Specifies the number of seconds before the balloon message is automatically closed. If this parameter is zero, the balloon message will not be automatically closed. If not present, this parameter defaults to zero.
pRect
[in] Pointer to a RECT struct that contains position where balloon message is to be displayed. May be null if default position should be used. If not present, this parameter defaults to null.
bSubclassParent
[in] Specifies whether parent is to be subclassed. Not subclassing parent will cause a slight loss in functionality - clicking on another app will not dismiss the balloon message. If not present, this parameter defaults to TRUE.
NOTE: Using XBalloonMsgDll.dll with VB.net apps requires that this parameter be FALSE.
Implementation Notes
I started by trying to get balloon message to display:Collapse Copy Code
m_hWndBalloon = ::CreateWindowEx(0, TOOLTIPS_CLASS, 0,WS_POPUP | WS_VISIBLE | TTS_ALWAYSTIP | TTS_NOPREFIX |TTS_BALLOON | TTS_CLOSE | TTS_NOFADE | TTS_NOANIMATE,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,hParent, 0, 0, 0);This worked, except that close button () did not display. I checked my code, then I checkedMSDN. No joy. OK, so I started looking at USENET forums, and discover that close button will only appear if app has manifest for common controls v6 or later. (I was in a hurry to get this working, so I hadn't bothered with manifest.) OK, I add manifest, and close button appears. The same is also true for VB applications - there must be manifest, or close button will not be displayed.
Now that I have balloon message displayed, how to get rid of it? Clicking close button of course works, but what if user clicks on another window? I have taken care of this in other projects by using TTF_SUBCLASS flag. This time, TTF_SUBCLASS has no effect. After trying several things to get this to work, I give up and decide to subclass the parent window myself, looking for specific messages that would indicate that user is doing something, and so message balloon should be dismissed. After implementing this, I verify that this approach works by clicking on dialog caption, clicking on another control, clicking on dialog background, and clicking on another window. All of these actions now dismiss the message balloon.
However, things are still not working quite right. Some keys (arrow keys, function keys) are just ignored. What's worse, the Esc key causes the balloon tip to disappear, but also closes the dialog! The solution to these problems was to install a keyboard hook using theSetWindowsHookEx() function. This allowed me to catch the keystrokes that a user would expect to dismiss the balloon tip.
XBalloonMsgDLL

The Win32 DLL XBalloonMsgDLL.dll exports function XBalloonMsgShow(), which takes same parameters as CXBalloonMsg::Show() (see above). The download includes Visual Basic projects for both VS6 and VS2005, that show how to use DLL with VB:

The DLL has the XP help icon, Vista help icon, and question icon embedded as resources, so you don't have to manually include those icons. The following table shows available special values for XBalloonMsgShow()'s nIcon parameter:
Value SDK Symbol Meaning
0 TTI_NONE Use no icon
1 TTI_INFO Use the information icon
2 TTI_WARNING Use the warning icon
3 TTI_ERROR Use the error icon
10 — Use the XP help icon
11 — Use the Vista help icon
12 — Use the question icon
Depending on whether your application is running on XP or Vista, you can select the appropriate help icon. Here is code from C++ demo app that checks for Vista:
Collapse Copy Code
BOOL IsVista(){BOOL rc = FALSE;OSVERSIONINFO osvi = { 0 };osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);if (GetVersionEx(&osvi)){if ((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&(osvi.dwMajorVersion >= 6)){rc = TRUE;}}return rc;}
This is how DLL's XBalloonMsgShow() function is called from VB6 (please see Form1.frm for complete code to VB app):
Collapse Copy Code
Private Sub ShowMessage(nIcon As Long)Dim ptCursor As POINTAPIDim hWndOver As LongDim l As LongCall GetCursorPos(ptCursor) ' Get cursor positionhWndOver = WindowFromPoint(ptCursor.X, ptCursor.Y) ' Get window cursor is overl = XBalloonMsgShow("My Title", "This is My Message", _hWndOver, GetParent(hWndOver), 0, nIcon, 1, 0, 0, 1)End Sub
For VB2005, the last parameter (bSubclassParent) must be FALSE:
Collapse Copy Code
Private Sub ShowMessage(nIcon As Integer)Dim ptCursor As POINTAPIDim hWndOver As IntegerDim l As IntegerCall GetCursorPos(ptCursor) ' Get cursor positionhWndOver = WindowFromPoint(ptCursor.X, ptCursor.Y) ' Get window cursor is overl = XBalloonMsgShow("My Title", "This is My Message", _hWndOver, GetParent(hWndOver), 0, nIcon, 1, 0, 0, 0)End Sub
C++ Demo App
Here is what demo app looks like:

If you do not want to use balloon messages (or cannot becauseregistry setting disables them), regular tooltip will be displayed:

XBalloonMsg is also able to display large messages, since text callbacks (LPSTR_TEXTCALLBACK) are not used:

‘What's This’ Help
In the above screenshot, noticein dialog caption. The demo app implements What's This help by using XBalloonMsg - click onand cursor changes to help select cursor (). Then click on a control and you will see help displayed for that control in message balloon:

On Vista you will see similar message balloon:

The tooltip control has built-in support for info, warning, and error icons, but none for help icon (). To display help icon you see in screenshot, I extracted help.ico file from the image library that comes with VS2008, VS2008ImageLibrary.zip. There are two help.ico files, one for XP and one for Vista. (In VS2005, the zip is called VS2005ImageLibrary.zip, but only contains XP icons). This file can be found in Common7 directory.
The help.ico files contain multiple formats - from 16x16x16 to 48x48x32. To reduce the size, I edited them (using excellentArtIcons Pro icon editor) to remove everything but the 32bpp formats. To ensure that correct size (16x16) was loaded, I used LoadImage() rather than LoadIcon(), after first checking whether XP or Vista icon should be used.
This technique allows you to have What's This help for any control without having to create a help file. Here is code that displays What's This help balloon:
Collapse Copy Code
//=============================================================================BOOL CXBalloonMsgTestDlg::OnHelpInfo(HELPINFO* pHelpInfo)//============================================================================={CString s = _T("");// the string resource id is the same as control idif (s.LoadString(pHelpInfo->iCtrlId)){CXBalloonMsg::Show(_T("What's This? Help"),s,::GetDlgItem(m_hWnd, pHelpInfo->iCtrlId),m_hWnd,AfxGetInstanceHandle(),(UINT)m_hHelp);}else if (pHelpInfo->iCtrlId != -1) // ignore clicking on dialog box{s.Format(_T("There is no help for control #%d.\r\n")_T("Please click on another control."),pHelpInfo->iCtrlId);CXBalloonMsg::Show(_T("Help Unavailable"),s,::GetDlgItem(m_hWnd, pHelpInfo->iCtrlId),m_hWnd,AfxGetInstanceHandle(),TTI_INFO);}return TRUE;}
One final note about What's This help: it is customary to use Shift+F1 to invoke What's This help. However, in MFC by default the F1 key (unshifted) will activate What's This help. The following code will restore the Windows standard behaviors for F1 and Shift+F1.
Collapse Copy Code
//=============================================================================BOOL CXBalloonMsgTestDlg::PreTranslateMessage(MSG* pMsg)//============================================================================={BOOL rc = TRUE;// Trap the famous undocumented F1 key message. Windows normally // responds to this message by sending the WM_HELP message (referred // to as WM_HELPINFO in MFC), which we use to enter What's This // (Shift+F1) help mode. This override makes unshifted F1 call// the normal Help function, and makes shifted F1 invoke the// What's This help.if (pMsg->message == 0x004D){if (GetKeyState(VK_SHIFT) & 0x8000)SendMessage(WM_SYSCOMMAND, SC_CONTEXTHELP);elseOnHelp();}else{rc = CDialog::PreTranslateMessage(pMsg);}return rc;}//=============================================================================void CXBalloonMsgTestDlg::OnHelp()//============================================================================={// Here you can implement full-blown F1 help}
If you want to add What's This help to a dialog, you must also select the extended style Context help in dialog properties. One gotcha: you can't have a minimize or maximize box at the same time, or the What's This button won't be displayed.
Balloon Tip Registry Key
On most systems, balloon tips are enabled by default. However, there is registry key that may be set that will disable balloon tips from displaying. If balloon tips are disabled, this has unfortunate effect of preventing any tooltip created with TTS_BALLOON style from displaying - it does not automatically fall back to displaying a regular tooltip.
To handle this situation, XBalloonMsg checks HKEY_CURRENT_USER registry key
Collapse Copy Code
Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced\EnableBalloonTipsand if it is set to 0, TTS_BALLOON style will not be used. This allows regular tooltips to be displayed:

How to use
Step 1 - Add Files
To integrate CXBalloonMsg into your app, you first need to add following files to your project: XBalloonMsg.h XBalloonMsg.cpp
The .cpp file should be set to Not using precompiled header in Visual Studio. Otherwise, you will get error
Collapse Copy Code
fatal error C1010: unexpected end of file while looking for precompiled header directiveXBalloonMsg.h should be included in whatever module you intend to use XBalloonMsg in. Also, if you want to implement What's This help, you should add one or both of the help icon files included with the demo.Step 2 - Call CXBalloonMsg::Show()
The CXBalloonMsg::Show() function displays message balloon, and may be called using embedded strings like this:Collapse Copy Code
CXBalloonMsg::Show(_T("Edit1 Info "),_T("This is an info string for Edit1."),m_ctrlEdit1.m_hWnd,m_hWnd,AfxGetInstanceHandle(),TTI_INFO);or using string resource IDs like this:Collapse Copy Code
CXBalloonMsg::Show(MAKEINTRESOURCE(IDS_EDIT2_INFO_TITLE),MAKEINTRESOURCE(IDS_EDIT2_INFO_MSG),m_ctrlEdit2.m_hWnd,m_hWnd,AfxGetInstanceHandle(),TTI_INFO);Internally there is just one function used for both calls. The function determines whether the calling parameter is resource id or string, by inspection of high-order word of pointer:Collapse Copy Code
// is the message a string or a resource id?if (HIWORD(lpszMsg) == 0){// idUINT nID = LOWORD((UINT)(UINT_PTR)lpszMsg);if (!::LoadString(hInstance, nID, m_szMsg, sizeof(m_szMsg)/sizeof(TCHAR)-2)){XBALLOONMSGTRACE(_T("ERROR - failed to load message string %d\n"), nID);_ASSERTE(FALSE);}}else{// string_tcsncpy(m_szMsg, lpszMsg, sizeof(m_szMsg)/sizeof(TCHAR)-2);}
Revision History
Version 1.3 - 2008 July 11
Added parameter to control parent subclassing Added separate VB and DLL projects for VS6 and VS2005 Added example of timed XBalloonMsg
Version 1.2 - 2008 July 2
Added help (question mark) icons to DLL and expanded VB demo
Version 1.1 - 2008 July 1
Added DLL project and VB demo program
Version 1.0 - 2008 June 23
Initial public release
Usage
This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.
License
This article, along with any associated source code and files, is licensed underThe Code Project Open License (CPOL)
About the Author
Hans Dietrich

Member I attended St. Michael's College of the University of Toronto, with the intention of becoming a priest. A friend in the University's Computer Science Department got me interested in programming, and I have been hooked ever since.
Recently, I have moved to Los Angeles where I am doing consulting and development work.
Occupation: Software Developer (Senior)
Company: Hans Dietrich Software
Location:United States

(#)