Click here to Skip to main content
12,064,498 members (54,859 online)
Click here to Skip to main content
Add your own
alternative version

Stats

25.3K views
1.7K downloads
16 bookmarked
Posted

Roll Your Own MsgBox

, 6 May 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
How to create a custom message box from first principles, including system sounds and print capability.

Demo.gif

Introduction

This article demonstrates how to write a replacement for Visual Basic's MsgBox. The library allows you to display informational and interactive dialog boxes with a variety of standard icons and system sounds, as well as different button combinations for obtaining user feedback. The form also has a link which allows users to print the dialog.

Why Roll Your Own?

I started working on this because the standard MsgBox bugged me. I had little control over the actual image used on the dialog, and no control over how the text was formatted. I was also getting tired of users coming to me with "I got an error message, but I don't remember what it said, and I clicked OK, so now it's gone." By writing my own, I can select icons that fit in with my application's overall design and make it easier to get user feedback.

The Dialog Form

Dialog.gif

The form itself is quite simple. The border style is set to FixedDialog, and the minimize and maximize buttons are hidden. The choice of border style automatically hides the form's system icon; I will set it manually in the form's constructor. In the upper left hand corner is a PictureBox control sized at 48 x 48, the same as the images I will be using. Main and Question are Label controls that will hold different pieces of text; Print this message looks like a link, but is actually just another Label. The three Button controls are ordered right to left.

To accommodate text of varying length, Message and Question are set to AutoSize = True. We don't want the message box to be uncomfortably huge, though, so we set the MaximumSize property:

MaximumSize.gif

With this, the label can grow to a maximum width of 450 pixels; setting the max height to zero means the label can be as high as it needs to be. The Framework is polite enough to break words on spaces, so the text will remain readable.

The code for the form is pretty basic. There is a Sound property which holds the SystemSound played from the form's OnShown event. The SizeForm method arranges the controls and resizes the form so it looks nice, and the Load and FormClosing events are captured to manage the dialog stuff.

Imports System.Drawing
Imports System.Drawing.Printing
Imports System.Media

Friend Class ShowMsgForm

#Region " Storage "
 
    Private _Sound As SystemSound

#End Region

#Region " Properties "
 
    Friend Property Sound() As SystemSound
        Get
            Return _Sound
        End Get
        Set(ByVal value As System.Media.SystemSound)
            _Sound = value
        End Set
    End Property

#End Region

#Region " Constructors "
 
    Public Sub New()
        InitializeComponent()
        Me.Text = ""
        Me.Icon = My.Resources.DefaultIcon
    End Sub

#End Region

#Region " Event handlers "
 
    Private Sub ShowMsgForm_FormClosing(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.FormClosingEventArgs) _
    Handles Me.FormClosing
        'If the user clicks on the Close Form button in the upper right,
        'it will have the same effect as clicking the accept button
        If e.CloseReason = CloseReason.UserClosing _
        AndAlso Me.AcceptButton IsNot Nothing Then
            Me.DialogResult = CType(Me.AcceptButton, Button).DialogResult
        End If
    End Sub

    Private Sub ShowMsgForm_Load(ByVal sender As Object, _
            ByVal e As System.EventArgs) _
            Handles Me.Load
        'Give the focus to the accept button, providing a 
        'visual clue as to which button it is.
        Dim Btn As Button = TryCast(Me.AcceptButton, Button)

        If Btn IsNot Nothing AndAlso Btn.Visible Then
            Btn.Select()
        Else
            Me.Button1.Select()
        End If
    End Sub

#End Region

#Region " Methods "
 
    Protected Overrides Sub OnShown(ByVal e As System.EventArgs)
        'Intercept OnShown to play the sound
        If _Sound Is Nothing Then
            SystemSounds.Asterisk.Play()
        Else
            _Sound.Play()
        End If

        'Then carry on as usual
        MyBase.OnShown(e)
    End Sub

    Public Sub SizeForm()
        Dim NewHeight As Integer = 0
        Dim NewWidth As Integer = 0

        'Icon stays where it is.
        'TextLabel.Left and QuestionLabel.Left are already correct

        NewWidth = Math.Max(TextLabel.Width, QuestionTextLabel.Width) _
        + TextLabel.Left + 4
        If NewWidth < 275 Then NewWidth = 275

        Button1.Left = NewWidth - Button1.Width - 4
        Button2.Left = Button1.Left - Button2.Width - 4
        Button3.Left = Button2.Left - Button3.Width - 4

        If QuestionTextLabel.Text = "" Then
            PrintLabel.Top = TextLabel.Top + TextLabel.Height + 8
        Else
            QuestionTextLabel.Top = TextLabel.Top + TextLabel.Height + 8
            PrintLabel.Top = QuestionTextLabel.Top + _
            QuestionTextLabel.Height + 8
        End If

        Button1.Top = PrintLabel.Top + PrintLabel.Height + 8
        Button2.Top = Button1.Top
        Button3.Top = Button1.Top

        NewHeight = Math.Max(Button1.Top + Button1.Height, _
        MessagePictureBox.Top + MessagePictureBox.Height) + 4

        Me.SetClientSizeCore(NewWidth, NewHeight)
    End Sub

#End Region

End Class

For brevity, I have cut the code that prints the dialog; you will need to download the source code linked above. Basically, it takes a snapshot of the whole form, including the title bar, and prints it out. It is boilerplate code that can be found easily on the web.

Support Code

Along with the form, there is some support code.

Public Enum ShowMsgImage
    Info
    Alert
    Confirm
    Critical
    Security
    UnderConstruction
End Enum

Public Enum ShowMsgButtons
    OkOnly
    ContinueCancel
    YesNo
    YesNoCancel
End Enum

Public Enum ShowMsgDefaultButton
    Button1
    Button2
    Button3
End Enum

ShowMsgImage indicates which icon will be displayed on your message box. ShowMsgButtons provides information on what buttons will be displayed, and ShowMsgDefaultButton flags the button that will be the default button on the form. If the message box is closed without any of the buttons being clicked, the dialog will return the result of the default button. In the source code, you will also find some additional code that supports printing the form.

ShowMsg - Simple Messages

HelloWorld.gif

So finally, we come to the main course, the ShowMsg function. The eight overloads will configure and display instances of ShowMsgForm and return the dialog result.

The first set of overloads generate an information only dialog. With the work method, the developer provides the text to be displayed, selects an icon, and provides text for the dialog's title bar.

Public Function ShowMsg(ByVal Text As String, _
      ByVal Icon As ShowMsgImage, ByVal Title As String) _
      As DialogResult
    Dim SMF As New ShowMsgForm

    'Set the title bar
    SMF.Text = Title

    'Select an image and sound based on the Icon parameter
    Select Case Icon
        Case ShowMsgImage.Alert
            SMF.MessagePictureBox.Image = My.Resources.Warning
            SMF.Sound = Media.SystemSounds.Asterisk
        Case ShowMsgImage.Confirm
            SMF.MessagePictureBox.Image = My.Resources.Confirm
            SMF.Sound = Media.SystemSounds.Question
        Case ShowMsgImage.Critical
            SMF.MessagePictureBox.Image = My.Resources.NotAllowed
            SMF.Sound = Media.SystemSounds.Hand
        Case ShowMsgImage.Info
            SMF.MessagePictureBox.Image = My.Resources.Info
            SMF.Sound = Media.SystemSounds.Asterisk
        Case ShowMsgImage.Security
            SMF.MessagePictureBox.Image = My.Resources.Lock
            SMF.Sound = Media.SystemSounds.Beep
        Case ShowMsgImage.UnderConstruction
            SMF.MessagePictureBox.Image = My.Resources.UnderConstruction
            SMF.Sound = Media.SystemSounds.Asterisk
    End Select

    'Set other properties
    SMF.TextLabel.Text = Text
    SMF.QuestionTextLabel.Text = ""
    SMF.Button1.Visible = True
    SMF.Button1.Text = "OK"
    SMF.Button1.DialogResult = DialogResult.OK
    SMF.Button2.Visible = False
    SMF.Button3.Visible = False

    'Resize the form
    SMF.SizeForm()

    'Set its starting position
    SMF.StartPosition = FormStartPosition.CenterScreen

    'Display the form modally and return its DialogResult
    Return SMF.ShowDialog
End Function

Note that the choice of icon determines the sound that will be played when the dialog is shown. Because this method is for simple messages, we hide the Question label and buttons 2 and 3. A call to SizeForm arranges the visible elements and resizes the form. It is set to display in the center of the screen, then displayed as a modal dialog. The result of the user's click on the form provides the function's return value.

There are two simpler overloads, one without the Title parameter and one without Title and Icon. In these cases, the dialog's title bar will display a default value and the icon is set to the Alert image.

You don't need to worry about having very long messages: because we set the MaximumSize property on the message label, the Framework will insert line breaks where needed:

LoremIpsum.gif

ShowMsg - Interactive Messages

Bathtub.gif

The next set of overloads start with two String parameters, the message and a question. By having two separate parameters, we can create a visual break between the information and the request for feedback. The programmer then indicates which pattern of buttons to use, which of those buttons will be the default, and what text, if any, will appear in the dialog's title bar.

Public Function ShowMsg(ByVal Text As String, ByVal Question As String, _
      ByVal Buttons As ShowMsgButtons, _
      ByVal DefaultButton As ShowMsgDefaultButton, _
      ByVal Title As String) As DialogResult
    Dim SMF As New ShowMsgForm

    SMF.MessagePictureBox.Image = My.Resources.Confirm
    SMF.Text = Title
    SMF.TextLabel.Text = Text
    SMF.QuestionTextLabel.Text = Question
    SMF.Sound = Media.SystemSounds.Question

    Select Case Buttons
        Case ShowMsgButtons.ContinueCancel
            SMF.Button1.Visible = True
            SMF.Button1.Text = "Cancel"
            SMF.Button1.DialogResult = DialogResult.Cancel
            SMF.Button2.Visible = True
            SMF.Button2.Text = "Continue"
            SMF.Button2.DialogResult = DialogResult.OK
            SMF.Button3.Visible = False

        Case ShowMsgButtons.YesNo
            SMF.Button1.Visible = True
            SMF.Button1.Text = "No"
            SMF.Button1.DialogResult = DialogResult.No
            SMF.Button2.Visible = True
            SMF.Button2.Text = "Yes"
            SMF.Button2.DialogResult = DialogResult.Yes
            SMF.Button3.Visible = False

        Case ShowMsgButtons.OkOnly
            SMF.Button1.Visible = True
            SMF.Button1.Text = "OK"
            SMF.Button1.DialogResult = DialogResult.OK
            SMF.Button2.Visible = False
            SMF.Button3.Visible = False

        Case ShowMsgButtons.YesNoCancel
            SMF.Button1.Visible = True
            SMF.Button1.Text = "Cancel"
            SMF.Button1.DialogResult = DialogResult.Cancel
            SMF.Button2.Visible = True
            SMF.Button2.Text = "No"
            SMF.Button2.DialogResult = DialogResult.No
            SMF.Button3.Visible = True
            SMF.Button3.Text = "Yes"
            SMF.Button3.DialogResult = DialogResult.Yes
    End Select

    Select Case DefaultButton
        Case ShowMsgDefaultButton.Button1
            SMF.AcceptButton = SMF.Button1
        Case ShowMsgDefaultButton.Button2
            SMF.AcceptButton = SMF.Button2
        Case ShowMsgDefaultButton.Button3
            SMF.AcceptButton = SMF.Button3
    End Select

    SMF.SizeForm()
    SMF.StartPosition = FormStartPosition.CenterScreen
    Return SMF.ShowDialog()
End Function

By default, the dialog is set to the Question icon and its corresponding system sound. The desired buttons are displayed and labeled, then the form's AcceptButton is set. A call to SizeForm arranges things, the dialog is set to display in the center of the screen, then it is shown.

There are two overloads that are similar, one without the Title parameter and one without Title and DefaultButton. With these, the title bar will display a default title and the right-most button will be set as the default.

ShowMsg - Exceptions

DBError.gif

The last two versions of ShowMsg take an Exception parameter and an optional ShowMsgImage; you can find the source in the downloadable project. The work method looks at the type of the exception to provide formatting: my code recognizes DataException, COMException, and "Other". Having a message box that provides exception information in a friendly, easy to understand (and printable!) format has proven to be an amazingly useful bit of functionality.

ShowMsg Variations

Access.gif

It has also been useful to create standard message boxes, such as "Access denied" and "Under construction". These are simple wrappers around calls to an appropriate overload of ShowMsg.

Public Function ShowAccessDenied() As DialogResult
    Return ShowMsg("You do not have sufficient permission to perform requested operation.", _
    ShowMsgImage.Security, "Access violation")
End Function

Room for Improvement

I designed this to be a library with all calls being made through ShowMsg or one of the specialty wrappers, and not by creating and configuring an instance of ShowMsgForm. This was mostly laziness on my part. It should be pretty easy to create a public version of the form, or simply copy it into your application directly; this way, you can tinker with other properties like background color and button styles.

Another nifty improvement would be to replace the Label controls for the message and question with RichTextBoxes. You would need to format your text as RTF, but this would let you do selective bolding, underlining, and color changes.

As written, the message box will show up in the Task Bar. It might be useful to add code to OnShown that checks to see if the dialog is hidden behind something else and, if so, flash its Task Bar object.

Conclusion

There are probably bugs in my code; if you find any, please let me know and I will get them fixed. Comments and critiques are always welcome and, as always, please vote this article up if you find it useful.

History

  • Version 1 - May 6, 2010: Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Gregory Gadow
Software Developer (Senior)
United States United States
Gregory Gadow lives in Seattle, Washington and has been writing code for almost 25 years in more than a dozen programming languages. He works for a mid-size brokerage firm and holds the Series 7 and Series 66 brokerage licenses, but much prefers working as the company's programming department doing VB6, VB.Net, ASP, HTML, XML and SQL.

You may also be interested in...

Comments and Discussions

 
GeneralVote 5 Pin
N. Henrik Lauridsen3-Apr-15 21:59
memberN. Henrik Lauridsen3-Apr-15 21:59 
GeneralMy vote of 5 Pin
IgaBaro27-Mar-13 12:12
memberIgaBaro27-Mar-13 12:12 
QuestionSource Code Pin
w4uoa13-Dec-12 6:13
memberw4uoa13-Dec-12 6:13 
AnswerRe: Source Code Pin
Gregory.Gadow13-Dec-12 7:10
memberGregory.Gadow13-Dec-12 7:10 
GeneralMy vote of 2 Pin
SledgeHammer016-May-10 19:36
memberSledgeHammer016-May-10 19:36 
GeneralAddition Pin
William Winner6-May-10 13:28
memberWilliam Winner6-May-10 13:28 
GeneralRe: Addition Pin
Gregory.Gadow6-May-10 15:23
memberGregory.Gadow6-May-10 15:23 
QuestionFluent interface? Pin
supercat96-May-10 8:59
membersupercat96-May-10 8:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.160204.4 | Last Updated 6 May 2010
Article Copyright 2010 by Gregory Gadow
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid