The Windows API provides a basic means of obtaining information from the user in the form of the MessageBox() API call. Calling MessageBox() produces a simple dialog box containing a text message and from one to four buttons which the user can select. Generally the calling program’s message loop stops while the message box is displayed (the dialog is modal).
MessageBox() has its uses, but it suffers from a somewhat archaic look (the MessageBox() API call hails all the way back to Windows 1.0!) and has a very limited number of options regarding the buttons shown and the overall appearance of the resulting dialog. So, what’s a poor programmer to do if the options provided by MessageBox() are inadequate or inappropriate for his/her information gathering requirements? This article will explain some of the built-in VCL message-display functions and shows how to create your own unique message boxes.
Of course, when using a RAD tool like C++Builder, it’s always possible (and relatively painless) to design a custom form from scratch or from a template to produce the desired effect. Even with RAD capabilities, though, it can still be a lot of work to create individual forms if you have a large variety of message dialogs. Coming up with a generic mechanism also involves a fair bit of work, especially when you find out you’d be re-inventing the wheel (as you will soon see).
Step into the VCL and its MessageDlg() family of functions. The Dialogs unit of the VCL exports these functions. If you have not yet looked at them you owe it to yourself to do so as they can make your information gathering chores remarkably easier. (Obviously, the MessageDlg() source is written in Pascal, but examining how the dialog functions are implemented is a great tutorial in dynamic form and component creation, not to mention potentially giving further ideas for customization as shown by this article.)
One of the functions available is MessageDlg(). MessageDlg() is quite similar to MessageBox() in its overall operation, but the “look” is somewhat different. In addition, you have more options regarding the buttons to be placed on the dialog. There are also variants that allow positioning of the dialog on the screen (MessageDlgPos()), and one that includes help context information (MessageDlgPosHelp()). Using MessageDlg() is pretty straightforward:
MessageDlg("Hello World!",
mtError, TMsgDlgButtons() << mbOK, 0);
The first parameter is the message text. The second parameter is an enumeration value that indicates the icon that appears on the dialog. The third parameter is a set of TMsgDlgButtons() that determines the buttons that appear on the dialog. The final parameter is for an optional help context ID (generally set to 0). MessageDlg() will return the ID of the button that was selected as one of the mrXXX constants (see the ModalResult topic in the VCL help for a list of these constants).
If you’re only in need of getting a quick and dirty text message to the user (yourself when you’re debugging, for example), this same family includes the incredibly simple ShowMessage() function. ShowMessage() takes just one parameter—the text of the message to show. (The ShowMessagePos() function provides a variant that allows you to position the dialog.)
The Dialogs unit also exports two functions that provide simple data entry forms. InputBox() and InputQuery() return strings entered in edit boxes on the forms. Since these are generated in a different way from the others they aren’t of direct interest here (though certainly useful in their own rights). The following code shows an example using a combination of InputBox() and ShowMessage():
String S = InputBox("Name Entry Form",
"Please enter your name", "");
if (S != "")
ShowMessage("Hello " +
S + ", how are you today?");
Having access to these fancier replacements for MessageBox() is all well and good, but sometimes it’s still necessary or desirable to show a message dialog, some part of which doesn’t conform to one of the pre-defined solutions provided by MessageDialog() and its cousins.
So, then, are we back to creating Forms from scratch again? Fortunately not, because Borland in its (remarkable in this case) foresight and wisdom, has provided access to the matriarch of the MessageDialog clan, the CreateMessageDialog() function.
All of the VCL dialog functions rely on CreateMessageDialog() for instantiating the basic model of dialog that they all share. You can do the same.
The CreateMessageDialog() function returns an instance of the TForm class (the same class that is ancestor of all VCL forms). The instance is the form as built using (and thus appearing) essentially the same as the forms produced by the standard functions. With the form instance in hand you can perform all kinds of magic on the dialog before it is displayed.
Here is the prototype for CreateMessageDialog() (I’ve taken the liberty of removing the namespace qualifiers for clarity):
TForm* CreateMessageDialog(
const AnsiString Msg,
TMsgDlgType DlgType,
TMsgDlgButtons Buttons);
The Msg parameter is the message text that will be displayed on the dialog. DlgType is an enumeration that determines which of the several types of standard dialogs is shown (see the on-line docs if you need more details on this). Buttons is a set of the desired buttons that will appear on the form.
To create custom effects in a message dialog, the basic process goes like this:
Provide an instance variable of type TForm*
Call CreateMessageDialog() and assign its return value to the TForm* variable
Access the form’s properties or individual components on the form
Once you have the TForm* for the dialog, you can locate and modify the components owned by the form to fit the needs at hand. You can even change characteristics of the form itself! After modifying the form or its components, you show the dialog by calling the ShowModal() method. When ShowModal() returns, free the TForm instance and proceed normally.
As a simple first example, Listing A shows how you can use this method to create a message dialog with custom button captions. It also shows how you might respond according to the return value of the message dialog. For this simple example the intermediate TButton instance variables I used are extraneous, but using them like this is a nice segue into more complex examples.
The method for accessing the individual buttons requires some explanation. When the form instance is created, it adds as many buttons as requested. It assigns a value to each button’s Name property equivalent to the associated TMsgDlgButton constant, without the “mb” in front. If you specified mbYes for the dialog buttons, for example, then the Yes button’s Name property will be “Yes.”
One caveat with regard to the buttons; they are sized according to the length of the original captions. If you give the buttons new captions, it’s easy to make the captions overrun the available space. Once you have a reference to a button you can, of course, move or resize it, but that takes a bit of work to get right.
Similarly, the message text is a TLabel with a Name of “Message”, and the dialog’s icon is a TImage with the name “Image.”
You can get a pointer to a particular component on the form using the FindComponent() method. Once you have the pointer, you can access and manipulate the component using any of its properties and methods. For example, you can do things like change the fonts of individual buttons or of the message, or change the image and location of the dialog’s icon. At the form level you can modify the dialog’s title bar caption, change the color of the background, position the form, or even add a bitmap background to the form.
Listing B goes a bit overboard in showing some of the possibilities. As I mentioned earlier, it’s easy to change the captions to something that no longer fits (like the “All Please” button in Listing B). You’ll have to be careful not to exceed the original button width or you’ll have to write code to move and resize the buttons. I use ExtractIcon() to get the icon for Windows Notepad and display that icon on the dialog. I’ve hard coded the path to NOTEPAD.EXE so you’ll have to change the path if you run this code.
Listing C is a set of several new MessageDlg() style functions. The first simply adds the ability to change the dialog’s caption. Note that I’ve set it up so that passing an empty string results in the default caption being displayd.
The second function is an enhancement of the simple ShowMessage() function, but allows you to set the caption and to show more than just the OK Button provided by default (since this is a void function and doesn’t return the modal result, picking a button other than OK may be of limited usefulness).
The third function lets you specify an icon (via a TIcon instance) to replace the icon on the standard dialog. Passing 0 for the TIcon parameter causes the function to use the standard icon, much as passing an empty string does for the message dialog’s caption.
Of course you can mix and match these features in your own versions to suit the needs of your applications. I don’t include examples of calling these new functions, but you can download the source for the example program from the Bridges Publishing Web site.
Using the techniques presented in this article, you can easily create customizable informational dialogs. Your display and information gathering chores will surely be a lot easier.
Listing A: A simple CreateMessageDialog() example
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
TForm* Dlg = CreateMessageDialog(
"Purge Warp Core?", mtConfirmation,
TMsgDlgButtons() << mbYes << mbNo);
TButton* yb = dynamic_cast<TButton *>
(Dlg->FindComponent("Yes"));
if (yb)
yb->Caption = "Affirmative";
TButton* nb = dynamic_cast<TButton *>
(Dlg->FindComponent("No"));
if (nb)
nb->Caption = "Negative";
int Rslt = Dlg->ShowModal();
switch (Rslt) {
case mrYes: ;// do "Yes" stuff
case mrNo: ;// do "No" stuff
}
}
Listing B: This example modifies several components on the message dialog
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TForm* Dlg = CreateMessageDialog(
"My Message", mtConfirmation,
TMsgDlgButtons() << mbYes << mbNo << mbAll);
// change the caption
Dlg->Caption = "Please Confirm";
// shift the position
Dlg->Top =+ 300;
Dlg->Left =+ 300;
// Get the button instances
TButton* yb = dynamic_cast
<TButton *>(Dlg->FindComponent("Yes"));
TButton* nb = dynamic_cast
<TButton *>(Dlg->FindComponent("No"));
TButton* ab = dynamic_cast
<TButton *>(Dlg->FindComponent("All"));
// change the button texts
yb->Caption = "Indeed!";
nb->Caption = "Surely Not!";
ab->Caption = "All Please!";
// change the button fonts
yb->Font->Name = "Times New Roman";
nb->Font->Name = "Arial";
ab->Font->Name = "Courier New";
// change the Message's appearance
TLabel* lb = dynamic_cast
<TLabel *>(Dlg->FindComponent("Message"));
lb->Font->Name = "Courier New";
lb->Font->Size = 16;
lb->Font->Color = clRed;
// supply a custom icon
TImage* img = dynamic_cast
<TImage *>(Dlg->FindComponent("Image"));
img->Picture->Icon->Handle = ExtractIcon(
HInstance,"C:\\WIN95\\NOTEPAD.EXE", 0);
// change its position
img->Left = 200;
img->Top = 15;
int Rslt = Dlg->ShowModal();
switch (Rslt) {
case mrYes: ;// do "Yes" stuff
case mrNo: ;// do "No" stuff
case mrAll: ;// do "No to All" stuff
}
}
Listing C: Customized message dialog functions
int MessageDlgCaption(
const AnsiString Caption, const AnsiString Msg,
TMsgDlgType DlgType, TMsgDlgButtons Buttons)
{
TForm* dlg =
CreateMessageDialog(Msg, DlgType, Buttons);
if (Caption != "")
dlg->Caption = Caption;
return dlg->ShowModal();
}
void ShowMessageCaptionBtn(const AnsiString Caption,
const AnsiString Msg, TMsgDlgBtn Btn)
{
TForm* dlg = CreateMessageDialog(
Msg, mtCustom, TMsgDlgButtons() << Btn);
if (Caption != "")
dlg->Caption = Caption;
dlg->ShowModal();
}
int MessageDlgIcon(const AnsiString Caption,
const AnsiString Msg, TIcon* Icon,
TMsgDlgType DlgType, TMsgDlgButtons Buttons)
{
TForm* dlg =
CreateMessageDialog(Msg, DlgType, Buttons);
if (Caption != "")
dlg->Caption = Caption;
if (Icon) {
TImage* image = dynamic_cast<TImage *>
(dlg->FindComponent("Image"));
if (image)
image->Picture->Icon = Icon;
}
return dlg->ShowModal();
}