(Print this page)

A custom MessageBox
Published date: Sunday, January 1, 2006
On: Moer and Éric Moreau's web site

Every once in a while, someone is asking in a newsgroup somewhere how he could change the fonts used by the MessageBox, or how he could translate the buttons, or how he could use his own icons, or how he could have the MessageBox disappear after 10 seconds, or how … I think you already know that the MessageBox is not very flexible.

The options we have

The first option we have is to specialize the MessageBox to fits our needs. If you Google long enough to find any info on any of these requests, you will probably find answers to most of them. Very often, it will be obscure API calls to user32.dll (where the real MessageBox resides). I found many of these requirements (but not all) and I never found them all together.

The second option we have is to create our own message box. It may seem tedious at first but I hope I will give you enough materials to have you start quickly.

The base form

Before being able to add our new features, we first need to reproduce the base of the message box. Sadly we cannot simply inherit from the existing one to add our new features. That would have been too easy. Because we cannot inherit, we have to redo all the basis of this dialog. Namely, we have to handle the title, the message itself, the icon, the buttons, the default button, the size and the location. This requirement had stopped the ambitions of many programmers. But guess what? I have done that for you.

The files included into the demo application

I strongly suggest that you download the demo I have created for you. I will not copy all the code here as it would be a waste of space.

Figure 1: The demo application in action.

This demo is made of these files:

  • MessageBoxEx.vb: contains a class with a single Show method that is used to display an instance of fMessageBox.
  • fMessageBox.vb: contains the new message box dialog as shown in figure 2.
  • fDemo.vb: contains the demo form as shown in figure 1. This form is a simple way to test all the parameters of the new dialog.

The fMessageBox class

All the interesting code is in the fMessageBox class. The code is pretty straight forward.

You will first find a series of properties to affect the various parts of the dialog. The longest property is the Buttons property. This property has to handle the number of buttons to show (varying from 1 to 3), the caption of each one of these buttons and finally result that will return when the button is pressed. An example of these settings:

Case MessageBoxButtons.OKCancel
    With Button1
        .Text = "OK"
        .DialogResult = DialogResult.OK
    End With
    With Button2
        .Text = "Cancel"
        .DialogResult = DialogResult.Cancel
    End With
    Button3.Visible = False
Another important portion of this form is its Load event. Here is the code it contains:
Me.FormBorderStyle = FormBorderStyle.FixedToolWindow

'Select the default button
Select Case DefaultButton
    Case MessageBoxDefaultButton.Button1
        Button1.Focus()
    Case MessageBoxDefaultButton.Button2
        If Button2.Visible Then
            Button2.Focus()
        Else
            Button1.Focus()
        End If
    Case MessageBoxDefaultButton.Button3
        If Button3.Visible Then
            Button3.Focus()
        Else
            Button1.Focus()
        End If
End Select

'Resize the form to fit the message
SetMessageSize()
SetFormSize()
You may ask yourself why is the focus of the default button is set here instead of in its property. The reason is simple, you have to do it here if you want it to work! If you try to do it before the form really loads, the focus cannot be set.

The method SetMessageSize called at the bottom of the Load event is responsible of resizing the RichTextBox control to fits the content and display a vertical scroll if required.

The last method to be called, SetFormSize is responsible of resizing the form to fits the content and also re-centers the dialog to the center of the screen.

Figure 2: Your new message box dialog.

The MessageBoxEx class

This class has a single method: the Show method. It is declared as Shared so that you will not need to create an instance of this class to call this method. This method is only responsible of feeding the various properties of the fMessageBox form, display the form and return the result.

You will surely want to modify this class to create overloads of the Show method (in the MessageBoxEx class). The real MessageBox dialog has 12 Show overloads. If you want to easily convert your existing call to MessageBox, you should create these OverLoads or abuse of the Optional keyword of VB.Net to limit the number.

Fixing some problems of the MessageBox

Have you ever try to display a very long message into a message box? What happened to your buttons at the bottom of the dialog? Where were they? I seriously hope that these very long messages are for your debugging use (and not for your users to read).

Also, have you ever wanted to copy the text of the message?

To fix these 2 problems, I have used a (read-only) RichTextBox control to display the text. This allows us to be able to scroll messages that are too long to fit in the screen (so you will never miss the bottom of your message again). This control, even if it is read-only, allows us to copy the message.

Changing the caption of the buttons

Now that the basic MessageBox has been reproduced, we can start adding new features.

The first, and probably the easiest feature to add, is to handle different language for the buttons of the dialog. This is a common requirement for me as many users are using an English version of Windows but want to have their application in French. It is a bit irritating for them to have everything in French except the Cancel, Abort and the Retry buttons.

To handle this feature, a new enumeration (enuLanguage) has been created into the fMessageBox class and the Buttons property that we have already talked about has been slightly modified like this:

.Text = IIf(Language = enuLanguage.English, "Retry", "Ré-essayer").ToString
You could easily handle as many languages as you need by adding values to the enumerations and setting the right translation into the Buttons property. Ensure that your buttons are large enough to handle the largest text value.

Timing out a message box

Another easy feature to add is the timer. To handle that, I have added a Timer control to the fMessageBox form and a TimeOut integer property (representing the number of seconds we keep the dialog on the screen). When a value greater then 0 is passed to this property, the Timer’s Interval property is set to 1000 and the Timer’s Enabled is set to True. As you already know, doing this will cause the Timer control to raise the Tick event every 1 second. In the Tick event, the TimeOut value is decremented and when it reaches 0, the DialogResult is set to Cancel so the dialog unloads returning a cancel value. A message also indicates how much time left before the dialog closes by itself. Notice that I display the message in the appropriate language.

Do not show again

This feature requires a bit more code. We need a way to store a flag saying that the user doesn’t want to see this message again. The place you will store this flag will greatly vary depending on the scope of this flag.

For example, if you are showing tips at startup (like many application) and you offer the “do not show again” feature, the answer will have to be persisted somewhere. A good place for that maybe the registry (see the article “Applications Settings in VB.Net” I have written in May 2003 for more details on this).

Another example is when you loop through many records and you want to warn users about something, you may want to offer a kind of “yes to all” or “no to all”. That kind of answer doesn’t need to be persisted pass the scope of the loop so a simple Boolean variable is required.

For the needs of my demo, I have simulated that second scenario. If you take a look at the Show method, you will find a static HashTable. This HashTable is used to keep the codes that have been selected not to be shown again. You can test it through the testing GUI using the field titled “Code Don’t show again”.

Other features you could add

Now that you have a good base of a new MessageBox on which you can build on and because this base is a custom form is a real form, you can customize it even more. Here are some other ideas I had but haven’t took the time to implement them:

  • Adding a back color to the form.
  • Since the message is displayed in a RichTextBox control, you could also show bold, italic, colored, … text.
  • You could also change the icons.
  • You could also add icons to the buttons.

Conclusion

Maybe someday we will be able to inherit a MessageBox dialog to fits our particular needs. Meanwhile, we have to use workarounds like this one.

That was my first column for 2006 and also my first column under the new name Level Extreme.Net magazine.

I hope we will all have very good projects for this New Year, that we will all be able to migrate to the latest version of Visual Studio. In the following months, I will give you articles on Visual Basic 2005.

I hope you appreciated the topic and see you next month.


(Print this page)