Creating A Custom Message Box In C#: Part 2
This article is part 2 of "Creating A Custom Message Box In C#". In part 1, I briefly explained how to create a simple custom message box, which consisted of OK/Cancel buttons. In this article I will go one step further by explaining how to add more buttons such as Abort/Retry/Ignore and how to display an Icon as well as the differnt beep tones for the differnt types of messages.
First a brief step back into my first article "Creating A Custom Message Box In C#". The technique behind the buttons in the MessageBox was to return a string id for each button clicked on the MessageBox. The MessageBox only had two buttons (OK/Cancel) therefore the ShowBox() method would return either 1 or 2 as a string. By using the returned string value I could then use an if statement to check which button was clicked. Below is the "How To Use The MessageBox" code from my last MessageBox article.
|string btnClicked = MyMessageBox.ShowBox("Do you want to exit");|
if (btnClicked == "1")
//User clicked OK button. Do some processing
//User clicked Cancel button. Do some processing.
The above code has a problem, that is how do we know that the returned string value 1 stored in the btnClicked variable corresponds to the OK button. The answer is, we dont. If the MessageBox had more buttons with each button assigned a string id, then we would lose track of which number corresponds to which buton. To solve this problem, we need to look at how the Windows C# MessageBox returns a button click.
The code below demonstrates how to display a C# Windows MessageBox with Yes/No buttons.
|MessageBox.Show("Sample data in a message box", "MessageBox Title", MessageBoxButtons.YesNo);|
Focus your attention on the code highlighted in bold. MessageBoxButtons is an enumeration. An enumeration is a distinct type consisting of a set of named constants called the enumerator list. Each enumerator in the enumerator list has a value. By default the first enumerator has the value 0, the second enumerator has the value 1 and so on. The values can be changed to suit your needs. Also by default the underlying type of the enumeration elements is int. This type can be changed to another type (Except char).
Knowing this we can use the MessageBoxButtons enumeration to set which buttons we want to display on our custom MessageBox. In our MessageBox Class we can check the value of the selected enumerator using an 'if statement'. For example if the selected enumerator is YesNo, then we can display the Yes/NO buttons by adding the buttons to a panel at runtime.
The following code below creates a MessageBox, this code is identical to the code in Listing 1.1 with the exception that we are using our custom MessageBox Class - CYMessageBox.
|CYMessageBox.Show("Are you sure you want to exit?", "Exit", MessageBoxButtons.YesNo);|
In our CYMessageBox Class we need to check, which enumerator was selected. The following private static method checks which enumerator was selected using an 'if statement'.
|static private void ButtonStatements( MessageBoxButtons MButtons)|
if (MButtons == MessageBoxButtons.YesNo)
The ButtonStatements() method takes an enumerator in its parameter. The enumerator is then checked to see which enumerator was selected. In Listing 1.2 we selected the YesNo enumerator. The code in Listing 1.3 will check the enumerator value and find it to be true. It will then execute the ShowNoButton() and ShowYesButton() methods. These two methods are responsible for creating the Yes and No buttons and adding them to a FlowLayoutPanel on the MessageBox. The creation of each button is placed into it's own method. This allows us to reuse the methods when needed. For example if we have the following methods listed in Listing 1.4, we can mix and match the buttons we need.
We can use the ShowYesButton() and ShowOKButton() methods to display the Yes and OK buttons on the MessageBox. Or use the ShowNoButton() and ShowCancelButton() methods to display the No and Cancel buttons on the MessageBox. Naturally in both given examples we would not do this as it makes no sense to have Yes/OK and No/Cancel buttons on the MessageBox. The correct combination should be ShowYesButton() and ShowNOButton() to display Yes/No buttons, ShowOKButton() and ShowCancelButton() to display OK/Cancel buttons. Using this approach we can add other buttons if needed such as a 'Help' button or a 'Go Online' button. Let's take a look at the code behind a button method. The following code in listing 1.5 is the ShowYesButton() method code.
|static private void ShowYesButton()|
btnYes = new Button();
btnYes.Text = "Yes";
btnYes.Size = new System.Drawing.Size(80, 25);
btnYes.BackColor = System.Drawing.Color.FromArgb(255, 255, 255);
btnYes.< = new < ("Tahoma", 8, < Style.Regular);
btnYes.Click += new EventHandler(btnYes_Click);
This method simply creates the Yes button. The btnYes button is declared in the Class. After creating the button, it is added to the flpButtons panel. This is a FlowLayoutPanel.
So far we know, we can use the MessageBoxButtons enumeration to display the desired buttons. This has been a simple case of using the 'if statement' to determin which enumerator was selected and displaying the appropriate buttons. But we have not solved the problem of determining which button was clicked without returning a string id such as 1 for the button OK or 2 for the Cancel button.
We need to look at how the Windows C# MessageBox Class returns the clicked button on the MessageBox. The code in listing 1.6 below displays a MessageBox with the Yes/No buttons.
|DialogResult button = MessageBox.Show("Sample data in a message box", "MessageBox Title", MessageBoxButtons.YesNo);|
if (button == DialogResult.Yes)
MessageBox.Show("Yes button clicked");
if (button == DialogResult.No)
MessageBox.Show("No button clicked");
Focus your attention on the code highlighted in bold. DialogResult is also an Enumeration. The Show() method is returning a value. This value is an Enumerator. If the Show() method's return value evaluates to Yes, a MessageBox displaying "Yes button clicked" is displayed". If it evaluates to No then a MessageBox displaying "No button clicked" is displayed. The DialogResult Enumeration consists of the following Enumerators in Listing 1.7.
Table data takken from http://msdn2.microsoft.com/en-us/library/system.windows.forms.DialogResult.aspx
|Abort||The dialog box return value is Abort (usually sent from a button labeled Abort).|
|Cancel||The dialog box return value is Cancel (usually sent from a button labeled Cancel).|
|Ignore||The dialog box return value is Ignore (usually sent from a button labeled Ignore).|
|No||The dialog box return value is No (usually sent from a button labeled No).|
|None||Nothing is returned from the dialog box. This means that the modal dialog continues running.|
|OK||The dialog box return value is OK (usually sent from a button labeled OK).|
|Retry||The dialog box return value is Retry (usually sent from a button labeled Retry).|
|Yes||The dialog box return value is Yes (usually sent from a button labeled Yes).|
So now we know that the C# MessageBox Class returns an Enumeration value, which is then evaluated using the DialogResult in an 'if statement'. This means we need to return a DialogResult value from our overloaded Show() method. But how do we know what the value is? Each button shown on our custom MessageBox has a Click event. When a button is clicked we set the value(Enumerator) on an instance of the DialogResult. The instance is declared in the CYMessageBox Class. The code for the Yes button is shown below in Listing 1.8.
|static void btnYes_Click(object sender, EventArgs e)|
CYReturnButton = DialogResult.Yes;
When the Yes button is Clicked the value of CYReturnButton, which is an instance of DialogResult is set to 'Yes'. Our overloaded Show() method simply returns this value. The MessageBox is then disposed using the Dispose() method. Below is the code for the Show() method in Listing 1.9.
|static public DialogResult Show(string Message, string Title, MessageBoxButtons MButtons)|
BuildMessageBox(Title); // BuildMessageBox method, responsible for creating the MessageBox
frmTitle.Text = Title; // Set the title of the MessageBox
frmMessage.Text = Message; //Set the text of the MessageBox
ButtonStatements(MButtons); // ButtonStatements method is responsible for showing the appropreiate buttons
newMessageBox.ShowDialog(); // Show the MessageBox as a Dialog
return CYReturnButton; // Return the button click as an Enumerator
I have supplied the link to a sample MessageBox. For a better understanding, have a look at the code. The sample code does not use the MessageBoxButtons Enumeration. I have declared my own Enumeration called CYButtons.
The icons are also displayed using an Enumeration. We can use 'if statements' to determin which icon was selected. I wont explain how do do this as the concept is similar to displaying buttons. What I will explain is the use of Icons. At first I though of using images for the icons. Rather than jumping into this idea I decided to investigate into using the Systems Icons. I knew that the Shell32.dll had icons, but I needed a way to get them and use them in my Custom MessageBox. Thankfuly I found some umanaged C# code on the internet that did exactly what I wanted. Listing 2.0 below shows the code used to extract the icons from Shell32.dll.
public extern static int ExtractIconEx(string libName, int iconIndex, intPtr largeIcon, intPtr smallIcon, int nIcons);
private intPtr largeIcon;
private intPtr smallIcon;
private Icon frmIcon;
private PictureBox pIcon;
largeIcon = new intPtr;
smallIcon = new intPtr;
ExtractIconEx("shell32.dll", 0, largeIcon, smallIcon, 250);
frmIcon = Icon.FromHandle(largeIcon);
Image imageIcon = new Bitmap(frmIcon.ToBitmap(), 38, 38);
pIcon = new PictureBox();
pIcon.Image = imageIcon;
The most important part in the code above is in bold. This gets the icon stored at the given index position in 'largeIcon'. You can change the value to get other icons.
Finally, we come to the MessageBox beep tone. If you haven't noticed, the standard MessageBox sounds a beep when displayed. It has different beep tones for different types of MessagesBox's. For example, there is a beep tone used for normal MessageBox's and there is a different beep tone for Warning MessageBox's. So now that we know, the question is how do we sound the different beeps? Once again we use unmanaged code. Listing 2.1 shows the code to sound a beep.
|[DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern bool MessageBeep(uint type);|
MessageBeep(0); //Standard Beep tone.
MessageBeep(30); //Warning beep tone.
This brings me to the end of this tutorial. I hope you have enjoyed reading it. If I have not explained anything correctly please leave a message and I will try my best to correct it. You can download the source code and play around with the code.
Points of interest
I have yet to figure out a way of expanding the messagebox according to the message text. The sample MessageBox code, does not expand the MessageBox like a Windows MessageBox does.