Last update: 2010-10-20
The Problem
Windows applications often use message boxes for asking the user for some actions or for displaying information messages. From week to week, more applications are getting published, which contain nice message boxes with checkboxes like "Do not display this message again" or "Do not ask this question" again, with which the user is able to customize the application behavior and to get rid of message boxes, which he always wants to answer in the same way.
I also wanted to offer this to the users of my applications. What features should be supported?
- The message boxes should support some new style (the
MB_???
flags used in a call of AfxMessageBox
).
- Such message boxes should be entirely written in MFC to enable other MFC developers to make quick and easy customization without having to know much about Windows API calls, just by using the standard MFC classes.
- The new message boxes should support the checkboxes mentioned above and should manage automatically to save the state of the checkbox and the answer of the user and not to display the message box again, if the user didn't want it to appear again.
- The message boxes should also be easy to integrate into existing applications. In existing applications, I didn't want to change every line of code containing a call to
AfxMessageBox
to something else. I'd just like to add a few lines of code and all messages boxes should appear in the new way.
Existing Solutions
At CodeProject, there are currently two different interesting solutions for modifying and enhancing the standard Windows message boxes, which can be invoked from MFC based applications by using the AfxMessageBox
method:
Although these solutions work fine, there were a few problems, because of which I was forced to offer a third solution:
- Both solutions can be used in MFC based applications, but offer only little or even no support for the standard MFC classes. For example, in one application, all dialogs are not derived from the MFC
CDialog
class directly, but from another class, which draws a custom skin for the dialog instead of the standard skin. If I want to use the message boxes provided by the two solutions mentioned above in this case, I have to apply many changes in the source code. My solution is entirely based upon MFC classes. It can therefore be easily modified and is for people like me, who enjoy using MFC and do not want to get deep into Windows API calls and is more easy to understand.
- The
XMessageBox
class supports "Do not ask again" or "Do not display again" checkboxes in the displayed message boxes. This feature is also supported by my CMessageBoxDialog
class. The problem with the XMessageBox
class from my point of view has been, that the state of the checkboxes, which has been stored in the registry, was stored there in a way, which is not the standard way by using the WriteProfileInt
or GetProfileInt
methods of the CWinApp
class. This might seem to be only a little problem, but I'd like to get the values stored in the same places as all other profile values are stored automatically by the MFC framework.
- At last, the layout of the message boxes generated by the two classes mentioned above didn't seem to me quite the way I'd like it to be, therefore I changed it a little bit.
How to Use CMessageBoxDialog
The Simple Way
The simple way to use the new message box is to change nothing in your application. All your calls to AfxMessageBox(...)
remain just the way they are already. You just have to overwrite the method DoMessageBox
in your application class, which is derived from CWinApp
. Use the following code to overwrite the method:
int CYourApplication::DoMessageBox ( LPCTSTR lpszPrompt, UINT nType,
UINT nIDPrompt )
{
CWnd* pParentWnd = CWnd::GetActiveWindow();
if ( pParentWnd == NULL )
{
pParentWnd = GetMainWnd()->GetLastActivePopup();
}
CMessageBoxDialog dlgMessage(pParentWnd, lpszPrompt, _T(""), nType,
nIDPrompt);
return (int)dlgMessage.DoModal();
}
Now every time you use AfxMessageBox
anywhere in your application, the new message box will appear. The new feature can simply be used by adding the new message box styles (the MB_???
flags) to your already existing message box flags. Your example, if you do a call like this:
int nResult = AfxMessageBox(IDS_ARE_YOU_SURE_TO_QUIT,
MB_YESNO | MB_ICONQUESTION);
To add the checkbox "Don't ask again" to the message box, simply change the call to:
int nResult = AfxMessageBox(IDS_ARE_YOU_SURE_TO_QUIT,
MB_YESNO | MB_ICONQUESTION | MB_DONT_ASK_AGAIN);
The Other Way
The other way of using the new message box is to directly create a dialog object. For details, please take a look at the header file of CMessageBoxDialog
. The constructor will take the same parameters as the AfxMessageBox
method. If you create the message box this way, you can also change the displayed icon to some other icon by using the SetMessageIcon
method.
It's also required to create the message box this way, if you want to use the timeout feature, which can be activated by calling the SetTimeout
method. A timeout is a countdown, which starts, when the message box is displayed. There are two modes for a timeout: The "un-disabled" or "enabled" timeout means, that the user can choose any button, but if he doesn't choose one, the default button will be assumed as being chosen, when the countdown is finished. The other mode, a "disabled" countdown is something like a nag screen. All buttons will be disabled, until the countdown is finished. After that, the user can click any button.
The New Styles and Return Values
Besides the standard MB_???
flags provided by Windows, the CMessageBoxDialog
supports the following new flags:
MB_CONTINUEABORT
: Display two buttons in the message box: "Continue" and "Abort".
MB_SKIPSKIPALLCANCEL
: Display three buttons in the message box: "Skip", "Skip all" and "Cancel".
MB_IGNOREIGNOREALLCANCEL
: Display three buttons in the message box "Ignore", "Ignore all" and "Cancel".
MB_DONT_DISPLAY_AGAIN
: If you set this flag, a checkbox "Don't display this message again" will be displayed in the message box. If the user checks this checkbox, the result of the message box will automatically be stored in the registry. The next time, this dialog will be called through AfxMessageBox
or the DoModal
method of the CMessageBoxDialog
directly, the former result will be returned. You as a developer do not have to care, whether the message box is actually displayed or not. You will receive the result, the user wants you to receive - no matter whether it's a direct input or it's the input stored in the registry, because the user didn't want to be asked again.
MB_DONT_ASK_AGAIN
: This flag does the same as MB_DONT_DISPLAY_AGAIN
, only the text of the checkbox will be another one: "Don't ask this question again" instead of "Don't display this message again".
MB_DEFAULT_CHECKED
: If a checkbox is displayed, this box is not checked by default. If you add this flag, the checkbox will be marked checked as soon as the message box is displayed.
MB_YES_TO_ALL
: If this flag is set and your message box contains a "Yes" button, an additional button with the text "Yes to all" will be added to the message box.
MB_NO_TO_ALL
: This flag does the same as MB_YES_TO_ALL
, but it will add the button "No to all", if your message box contains a button named "No".
MB_RIGHT_ALIGN
: If you specify this flag, the button in the dialog will be aligned on the right side of the message box (e.g. as it is done in the Windows Explorer) and will not get centered in the dialog.
MB_NO_SOUND
: If you specify this flag, no standard sound will be played, if you open a message box.
MB_DEFBUTTON5
and MB_DEFBUTTON6
: Because now there can be more than four buttons in a message box, these flags have been added to enable you to select another default button.
Because of the new buttons, there are also some new return values: IDYESTOALL
, IDNOTOALL
, IDSKIP
, IDSKIPALL
and IDIGNOREALL
. They represent the new buttons and will be returned, if one of these buttons is clicked.
Other Methods
There are also some other public
methods for the CMessageBoxDialog
class, which are explained in the header file or the source code and should be easy to understand and use.
Just one method might be interesting: ResetMessageBoxes
. If you call this method, all message box results, which have been stored in the registry, because the user checked the "Don't display again" or "Don't ask again" checkbox, are removed. Therefore all message boxes will be displayed again.
How to Use It with VC6/MFC6
To use the CMessageBoxDialog
with VC6/MFC6, some minor changes need to be applied. Please take a look at this message for more information. Because I do not have VC6/MFC6 installed, I cannot provide a full download for this case, but I think it's very easy to customize and should be no problem.
History
2010-10-20: Version 1.2 lite
- Added the feature that one can add buttons without needing a
static string
resource
- Added the feature that the dialog is now dynamic (instead of having add a custom dialog resource)
- Removed the feature of using a checkbox to remember choice (required access to settings/registry)
- Removed the feature of using a timer to automatically choose (too complicated code)
- Removed the feature of imitating the
AfxMessageBox
(Messsagebox
styles require access to user32
resource to retrieve "Ok", "Cancel", etc.)
2004-03-01: Version 1.1a (Minor update)
- License: Source code now may be used under the terms of the LGPL license, too
- License: Added the full licenses to the ZIP archives
2004-02-29: Version 1.1 (Minor update)
- Bug Fix: Added missing files to the ZIP archive
- Bug Fix: Added support for the standard Windows flags
MB_RIGHT
and MB_RTLREADING
, which were still missing
- New Feature: Added the
ResetMessageBoxes
method to reset the message boxes
- New Feature: Added the
MB_DEFAULT_CHECKED
flag
- New Feature: Added the enabled and disabled timeout for the message boxes
2004-02-28: Version 1.0 (Initial release)