Describes a variant of the MessageBox that allows some customization.
Introduction
I'm old. Really old. And as a result of increasing age (or maybe just because of bad choices made in my intemperate youth), my eyesight is beginning to fail me. Everything is out of focus, I can't find quite the right distance at which to hold a book, and if it weren't so annoying, I might find the kaleidoscopic color halos that seemingly surround everything somewhat pretty.
A particular annoyance related to failing eyesight is the infinitesimally small text that Windows uses unless you increase the font size in 10% increments. The problem with this is that even at 110%, the text is WAY bigger than it really needs to be. In any case, this tiny text is used in the standard WPF message box, and it cannot be changed. In fact, NOTHING about the standard WPF message box can be changed. This realization finally got the better of me, and I was forced to come up this code.
But First, Complaints
When I started researching the standard WPF message box, I was surprised and appalled that it wasn't a WPF-specific object. It turns out that it's entirely implemented by the Win32.Interop functionality, and doesn't have a single drop of WPF goodness thrown in. I suppose a Microsoft apologist could try making a case about how their approach probably reduced the overall .Net code footprint by a few thousand bytes, but I ain't buying it. There was a true opportunity here, and Microsoft squandered it. By the way, this is the reason that it's not configurable in any way - it uses Windows system properties and that's that. You're not allowed to stray from those parameters. BAD PROGRAMMER! BAD! BAD!
What I Wanted to Achieve
My primary goal was to develop a replacement for the standard message box that was reasonably configurable, while being totally WPF-based, as well as presenting an interface that was as close as possible to the original message box. Through some static
wizardry, I was able to accomplish this without too much heartburn.
Emulating Standard WPF Message Box Functionality
My first task was to emulate the standard WPF MessageBox
. Originally (in the previous iterations of the code), I provided an overload of the Show()
method for every possible parameter combination, but the sheer number of overloads was becoming untenable. What really pushed me over the edge was providing functionality to specify the default button - it was simply a bridge too far, so I decided to do this:
MessageBoxEx.Show(string msg, params object[] parameters)
This allows the user to specify the message text, and any other parameter he desires. Yes, using this strategy allows the dev to specify more than one of any parameter, but the method has some protection against stupid developer tricks. The method starts out with variables set to reasonable defaults. As the parameters are processed, a given variable is set until its value is no longer equal to the default value. Granted, this is fairly minimal in nature, and could be taken to the extreme of throwing an exception if a given variable is set more than once, but I prefer not to interrupt processing unless something truly catastrophic has happened, and allowing the programmer to be a moron isn't a catastrophe as much as it is a comment on the elasticity of today's already-loose moral standards.
In short, be an idiot if you want to, because the method will use what it can, and throw away the rest. Anything more than the parametersthat are expected will result in a waste of (and excessive) processing time, which has been proven to adversely affect the already tenuous fabric of space and time. The true benefit of this approach is to me (which is admittedly all that really matters), and that benefit is that there is only ONE Show()
method to maintain, and the code becomes MUCH more simple. This method can optionally accept the title, desired buttons, the icon image, and the default button, and those parameters can be specified in any order. Any other parameter is not supported, and will be ignored.
The standard Wpf MessageBox (and MessageBoxEx
) accepts the following objects:
MessageBoxButton.OK
MessageBoxButton.OKCancel
MessageBoxButton.YesNo
MessageBoxButton.YesNoCancel
MessageBoxImage.Information
(can also use MessageBoxImage.Asterisk
) MessageBoxImage.Question
MessageBoxImage.Warning
(can also use MessageBoxImage.Exclamation
) MessageBoxImage.Error
(can also use MessageBoxImage.Hand
or MessageBoxImage.Stop
)
Note - If you specify an icon, the system sound associated with that icon is also played. However, you can optionally turn off the playing of the sound via a configuration method.
While the Wpf MessageBox
uses the MessageBoxDefaultResult
enumerator to specify the default button, it's fairly limited in scope, so MessageBoxEx
provides a new MessageBoxButtonDefault
enumerator that expands the support of default buttons. You can specify a specific button, the button's ordinal left-to-right position, the default used by Windows.Forms.MessageBox
, the most/least positive button in the specified buttons, or even no default at all.
Added Features
Beyond the standard features, I also added support for altering the following items. This is the primary reason for writing this code in the first place.
- Font family
- Font size
- Message area text and background color
- Button panel background color
- Custom button template support
- Enhanced default button support
- Set maximum possible message box width
Notable Missing Features
If you've ever written code for Windows.Forms
, you've probably had reason to use the Windows.Forms.MessageBox
, and you may have noticed that Microsoft decided not to support MessageBoxButton.AbortRetryIgnore
or MessageBoxButton.RetryCancel
. This also means these buttons aren't available for the default button setting either. MessageBoxEx
adds these buttons (and the associated defaults) back to the playing field - see the next article section. Huzzah!
Extended Functionality
When creating the MessageBoxEx
class, I decided I wanted to add functionality not seen in the standard WPF message box. This included the afore-mentioned missing buttons, a checkbox, a clickable URL, a clickable icon (to execute external code), and more default button options. To delineate the standard stuff from the extended functionality, you have the MessageBoxEx.ShowEx()
method, which adds support for the MessageBoxButtonEx
enumerator, and the MsgBoxExtendedFunctionality
class, which allows you to add the checkbox, url, and clickable icon.
The following buttons are available:
MessageBoxButtonEx.OK
MessageBoxButtonEx.OKCancel
MessageBoxButtonEx.YesNo
MessageBoxButtonEx.YesNoCancel
MessageBoxButtonEx.AbortRetryIgnore
MessageBoxButtonEx.RetryCancel
The following extended functionality has also been implemented:
- Clickable icon - When specified, the icon becomes clickable, and allows you to perform an action defined in what I call an "error delegate" object. This allows the message box to perform some action when the icon is clicked. This feature is available for all image icons. When the icon is clickable, the cursor will change to a hand, and the associated tooltip (if specified) will be displayed. This feature idea was spurred by a discussion in the CodeProject lounge regarding screen shot generation for emailing to the support team.
- Details - The message box can display both a message, and "details". This could be useful for those times when you want to display an
Exception.Message
, as well as an Exception.StackTrace
, but you don't want to have a huge message box. The details panel is an expander, and is initially collapsed when the message box is displayed.
- Checkbox - this displays a
CheckBox
with a prompt (content) that you specify. Some possible uses for this would be to suppress the display of the message box in certain instances, or affect some other functionality in your application. It is up to the developer as far as handling the checkbox value. The messagebox does not care beyond presenting it. See the BtnClickme3c_Click()
method in the sample application for an example of how this feature might be used.
- Clickable URL - this displays a clickable URL that will automatically open the default browser when you click it. When you hover over the link, the cursor will change to the System "hand", and a tooltip is displayed with the actual URL to which the user will be browsing. I couldn't find a screen capture tool that would capture both the cursor AND the tooltip (I tried the snipping tool and Irfanview, so my efforts were substantially less than "exhaustive"), so the screenshot below doesn't show the tooltip..
- AbortRetryIgnore and RetryCancel buttons - WPF
MessageBox
does not support the Windows Forms buttons AbortRetryIgnore
or RetryCancel
. The MessageBoxEx
gives you the opportunity to add them back (via the extended functionality object). It baffles me as to why Microsoft would half-ass something like this.
- Default Button - you can specify any button as the default by one of a number of ways - the specific button, the button according to its left-to-right ordinal position, the standard Windows Forms default (depending on buttons used), the most/least positive, or none at all.
NOTE: The checkbox and the URL are not included in the scrollable message area. This keeps them in view at all times.
The Code
The amount of code needed to implement this message box started out surprisingly small. So small that it only took me a few hours to implement it, and then move it to a sample project for this article.
NOTE: The original version of this article included several code dumps, but with the frequency of change, it became tedious to try to keep them synced up with what's really going on in the code, so I deleted them from the article. If you want to see what I did, either browse the code here on CodeProject, or just download the ZIP file and check it out (this is a lot more fun).
Basic Design Goals
As you know, the standard MessageBox
is a static object that doesn't need to be instantiated to use. This new MessageBoxEx
is static as well, or at least "static-ish". In order to implement it, I created a standard WPF Window that contains both static and non-static properties and methods. The idea is that the programmer would call one of the static MessageBoxEx.Show...()
methods, which would then instantiate a non-static version of the window. I also wanted to make the outward-facing experience as identical as possible to the standard MessageBox
, so the return values are the same as what you get from the standard message box.
Implementation - XAML
The XAML for this form is fairly limited because we're not really doing anything fancy. I think the comments in the XAML provide sufficient enough documentation as to not require an accompanying narrative.
Clickable Icon - Since images aren't "clickable", I had to resort to handling the MouseLeftButtonUp
event. On top of that, I wanted to make the visual appearance of the icon change when the mouse hovered over it, so I set the default opacity to 0.85, and when the message box is displaying an error, I add a custom style (in the code behind) to the image that reacts to mouse hover, which changes the opacity to 1.0 and changes the cursor to a hand. I also use the code behind to add a tooltip to the image (actually the image's parent grid because I could not get a bound tooltip to work on the image element), so the user gets some sort of indication that the icon is indeed clickable.
Clickable URL - The clickable URL is pretty much the same. The underlying control is a TextBlock
and doesn't support the Click
event, so once again, I had to handle the MouseLeftButtonUp
event instead. I also created a custom style that triggers on MouseEnter
so I could change the cursor to a hand. On hind-soght, I suppose I could have used a RichTextBox
to display the URL, but I considered it too late, and besides, I didn't consider the juice to be worth the squeeze.
Implementation - C#
The code-behind really isn't anything special or technically difficult to understand after a cursory inspection. As stated earlier, the code is essentially broken up into static, and non-static fields, properties, and methods. The message box itself needs to be instantiated, but the class has static properties and methods that can be used for easy configuration and rendering of the message box. Just like MessageBox
, MessageBoxEx
provides several overloads of the Show()
method (and its variants) to display itself.
As far as code organization is concerned, I did something I don't normally do, but it was done in the interest of making it easy to copy the files into your own code. First, all of the classes related to the MessageBoxEx
class are located in the MessageBoxEx.xaml.cs file. Second, I took advantage of the fact that the class is a partial class, and separated the static parts from the non-static parts. It might seem weird, but it helps me, and that's the most important thing.
To make the form size itself appropriately, I utilized the window SizeToContent
property (in the XAML). This property allows the window to grow/shrink to fit its content, allowing the window to only be as big as it needs to be, given the minimum and maximum width/height limitations specified in the XAML. A curious side-effect of the SizeToContent
property is that it takes affect AFTER the window has been positioned on the screen. This means that if the window needs to display more than a single line message, the window won't be "centered" on the screen as intended. To mitigate this behavior, I had handle the window's SizeChanged
event, and manually re-center the form on the screen. The code was simple enough, but I figured I'd cite it here.
Extended Functionality
JAN 08 - See update notice at the end of this section!
The extended functionality required different method names (and requisite overloads) because parameter collisions were induced. Beyond that, some additional classes were required to make implementation and use as seamless as possible. All of the extended features described below can be combined into a single message box.
CheckBox
The checkbox allows you to present a checkbox on the message box. The content of the checkbox is specified by you, and can really be anything that you find appropriate. This feature requires that you instantiate an MsgBoxExCheckBoxData
object in order to both specify the checkbox content, and provide property to determine the "checked" state of the checkbox. The MsgBoxExCheckBoxData
class can be found near the bottom of the MessageBoxEx.xaml.cs. It looks like this:
public class MsgBoxExCheckBoxData : INotifyPropertyChanged
{
#region INotifyPropertyChanged
private bool isModified = false;
public bool IsModified { get { return this.isModified; } set { if (value != this.isModified) { this.isModified = true; this.NotifyPropertyChanged(); } } }
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
if (propertyName != "IsModified")
{
this.IsModified = true;
}
}
}
#endregion INotifyPropertyChanged
private string checkBoxText;
private bool checkBoxIsChecked;
public string CheckBoxText
{
get { return this.checkBoxText;}
set
{
if (value != this.checkBoxText)
{
this.checkBoxText = value;
this.NotifyPropertyChanged();
}
}
}
public bool CheckBoxIsChecked
{
get { return this.checkBoxIsChecked; }
set
{
if (value != this.checkBoxIsChecked)
{
this.checkBoxIsChecked = value;
this.NotifyPropertyChanged();
}
}
}
}
To use implement this feature in your own code, you need to instantiate the checkbox data object. The code shown below was pulled from the sample application.
MsgBoxExCheckBoxData checkboxData = new MsgBoxExCheckBoxData()
{
CheckBoxIsChecked = false,
CheckBoxText = "Don't show this message anymore"
};
Once you have your checkbox data object instantiated, you need to do something like the following.
if (this.checkboxData != null && !this.checkboxData.CheckBoxIsChecked)
{
MessageBoxEx.ShowWithCheckBox("This is possibly pointless and can be permenantly "+
"dismissed by clicking the checkbox below."
, this.checkboxData, "Checkbox Sample");
}
else
{
MessageBoxEx.Show("But you said not to show the message anymore. Make up your mind.","Hypocrite Notice");
}
In essence, the messagebox doesn't care what the checkbox is for, and it's on the developer to handle all the associated persistence issues that might be associated with the checkbox's intended functionality, as well as the scoping of the data object's implementation. In the example shown above, the data object is declared in the MainWindow
class so that subsequent attempts to display that message box can be controlled by the last value of the CheckBoxIsChecked
property.
Details
This feature allows you to display large amounts of text in a shorter message box, and is handy for separating augmenting information from the actual message. An example would be specifying an Exception.Message
as the message text, and then specifying th Exception.StackTrace
as the "details". Useage requires that you use one of the ShowWithDetails
method overloads.
MessageBoxEx.ShowWithDetails("Here's some source code. Click the Details expander below."
, Testmsg()
, "Really Long Message, With Deatils"
, MessageBoxButton.OK
, MessageBoxImage.Information);
The Testmsg()
method returns a long block of source code to serve as content for the details pane.
Clickable Error Icon
This feature allows you to make the icon clickable, ostensibly to perform some ancillary action. It's limited to the error icon, because I figured that's really the only place you would need something like that. I envision it being used to send an email to your support team, or maybe to correct some setting that may have adversely affected execution in some way. In any case, if you want it available for every icon, modifying the code to do so should be fairly trivial.
The first thing you need to do is create a class that inherits from MsgBoxExDelegate
. That class provides everything you need to react to a clicked icon, but requires you to override the PerformAction
method.
public abstract class MsgBoxExDelegate
{
public string Message { get; set; }
public string Details { get; set; }
public DateTime MessageDate { get; set; }
public virtual MessageBoxResult PerformAction(string message, string details=null)
{
throw new NotImplementedException();
}
}
An inheriting class may look like this:
public class ErrorMsgDelegate : MsgBoxExDelegate
{
public override MessageBoxResult PerformAction(string message, string details=null)
{
this.Message = message;
this.Details = details;
this.MessageDate = DateTime.Now;
MessageBoxResult result = MessageBoxEx.Show("You're about to do something because this is an "+
"error message (clicking yes with play a beep sound). "+
"Are you sure?",
"Send Error Message To Support",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
SystemSounds.Beep.Play();
}
return result;
}
}
The extent of the code that is contained in the PerformAction
methods depends entirely on the architecture of the rest of your application. The sample above merely exercises the feature within the context of the sample application.
When the messagebox closes, the reference to the delegate object in the message box is set to null
to avoid inadvertently triggering the clickable icon in subsequent calls to the message box.
To use this feature, do the following:
MessageBoxEx.SetErrorDelegate(this.errorDelegate);
MessageBoxEx.SetExitAfterErrorAction(true);
MessageBoxEx.Show("Show an error message. The icon is clickable.", "Error"
, MessageBoxButton.OK, MessageBoxImage.Error);
After writing this part of the article, it occurred to me that I could have combined the two "Set..." methods into one since they're associated. Ahh well, it's easy enough to change for even a lightly experienced dev, so have at it if you feel the need.
JAN 08 UPDATE!
While I was implementing the new URL code, it became obvious that the extended parameters were getting out of hand (the URL was the fourth extended functionality to be added), so I created a single additional object that is passed into the ShowExt method (along with the message, titlem, buttons, and image). This object is called MsgBoxExtendedFunctionality
, and in it, you place one or more of the extended functionalities that you want to use in a given messagebox. This serves a couple of purposes - a) reduce the number of "special" versions of the Show
method (with each one having eight overloads), and b) reduced maintenance time if I felt like adding more extended functionality. The old methods (ShowWithCheckBox
, ShowWithDetails
, and ShowWithCheBoxAndDetails
) are still there, but they've been modified to call the new ShowExt
methods, and the intellisense has been updated to show that they are deprecated.
To see examples of its usage, look for the ClickMe_Nc
methods (they handle the button clicks for the buttons on the right side of the sample app window).
Useage
Somewhere in your app startup (I recommend using the constructor in the MainWindow
because at that point you can specify the parent window from which to inherit the appropriate font properties. In the sample app, I created a method called InitMessageBox()
containing the following statements.
MessageBoxEx.SetParentWindow(this);
MessageBoxEx.SetMessageForeground(Colors.White);
MessageBoxEx.SetMessageBackground(Colors.Black);
MessageBoxEx.SetButtonBackground(MessageBoxEx.ColorFromString("#333333"));
MessageBoxEx.SetButtonTemplateName("AefCustomButton");
MessageBoxEx.SetMaxFormWidth(500);
After you've done this once in the application, using MessageBoxEx
is identical to using the original MessageBox
.
MessageBoxResult result = MessageBoxEx.Show("Your message. Continue?", "Caption", MessageBoxButton.YesNoCancel);
Of course, the button specification defaults to MessageBoxButton.OK in no button group is specified.
The Sample Application
MessageBoxEx Class Documentation
The following is a method by method description of the class. Since all of the configuration is handled via static methods, those are the methods I will be discussing here. There will be few, if any, code dumps.
The Show...()
Methods
The Show()
method presents a message box that represents a feature-similar version of the standard WPF MessageBox. This method accepts the following parameters.
string msg | This is the text of the message to be displayed. All desired formatting must be performed in the calling method. At the very least, this parameter MUST be specified. |
params object[] parameters | This parameter allows the developer to add whatever parameters are applicable to the given message box instance. This is done to eliminate the dozens of overloads to the Show method that would otherwise be required. The following parameters will be evaluated by this method.
string - evaluated as the intended title of the message box window. If not specified, the window title is determined by the icon (if specified), or the default caption.
MessageBoxButton - evaluated as the intended buttons to display. If not specified, the default value of MessageBoxButton.OK is used. The following buttons are available:
OK OKCancel YesNo YesNoCancel
MessageBoxImage - evaluated as the intended icon to display. If not specified, the default value of MessageBoxImage.None is used. The following images are available:
Information (same icon for Asterisk ) Question Warning (same icon for Exclamation ) Error (same icon for Stop or Hand )
MessageBoxButtonDefault - evaluated as the intended default button. If not specified, the default value of MessageBoxButtonDefault.Forms is used (uses the same deafult button strategy as the Windows.Forms.MessageBox . The following defaulst are available:
OK - specific button Cancel - specific button Yes - specific button No - specific button Button1 - 1st button by ordinal left-to-right position Button2 - 2nd button by ordinal left-to-right position Button3 - 3rd button by ordinal left-to-right position Forms - the same as the Windows.Forms.MessageBox defauilt MostPositive - the button that indicates the most positive result LeastPositive - the button that indicates the least positive result None - no default button
Window - evaluated as the owner window. If not specified, the applications main window will be used.
While it's possible to specify more than one of any of the parameters, the Show method will stop evaluating parameters after it encounters one that does not use the default value of the given parameter type. For instance, if your parameter list looks like this:
MessageBoxEx.Show("My message text", MessageBoxButton.YesNo, MessageBoxButton.OK);
...the message box will display the Yes/No buttons, and the OK button will.be ignored. Conversely, if your parameter list looks like this:
MessageBoxEx.Show("My message text", MessageBoxButton.OK, MessageBoxButton.YesNo);
...the message box will again display the Yes/No buttons, because the OK button is the default. Finally, if you do this:
MessageBoxEx.Show("My message text", MessageBoxButton.OkayCancel, MessageBoxButton.YesNo);
...the message box will display the Okay/Cancel buttons because that value was the first value encountered that was not the default.
I certainly can't control what you do, but it's pointless and stupid to specify multiple instances of the same parameter, and it just forces the Show method to handle useless parameters, and takes more time to decide what to do
|
The Show()
method returns a MessageBoxResult
value. This is used to indicated which button was clicked to dismiss the message box window. If the user clicks the window's close button, the return value will be selected based on the following:
- For
MessageBoxButton.OK
, the result will be MessageBoxButton.OK
- For
MessageBoxButton.OKCancel
, the result will be MessageBoxButton.Cancel
- For
MessageBoxButton.YesNo
, the result will be MessageBoxButton.No
- For
MessageBoxButton.YesNoCancel
, the result will be MessageBoxButton.Cancel
The ShowEx()
method is similar to the Show()
method, except that it presents a message box that implements extended functionality. This method accepts the following parameters.
string msg | This is the text of the message to be displayed. All desired formatting must be performed in the calling method. At the very least, this parameter MUST be specified. |
params object[] parameters | This parameter allows the developer to add whatever parameters are applicable to the given message box instance. This is done to eliminate the dozens of overloads to the Show method that would otherwise be required. The following parameters will be evaluated by this method.
string - evaluated as the intended title of the message box window. If not specified, the window title is determined by the icon (if specified), or the default caption.
MessageBoxButtonEx - evaluated as the intended buttons to display. If not specified, the default value of MessageBoxButtonEx.OK is used. The following buttons are available:
OK OKCancel YesNo YesNoCancel AbortRetryIgnore RetryCancel
MessageBoxImage - evaluated as the intended icon to display. If not specified, the default value of MessageBoxImage.None is used. The following images are available:
Information (same icon for Asterisk ) Question Warning (same icon for Exclamation ) Error (same icon for Stop or Hand )
MessageBoxButtonDefault - evaluated as the intended default button. If not specified, the default value of MessageBoxButtonDefault.Forms is used (uses the same default button strategy as the Windows.Forms.MessageBox . The following defaults are available:
OK - specific button Cancel - specific button Yes - specific button No - specific button Abort - specific button Retry - specific button Ignore - specific button Button1 - 1st button by ordinal left-to-right position Button2 - 2nd button by ordinal left-to-right position Button3 - 3rd button by ordinal left-to-right position Forms - the same as the Windows.Forms.MessageBox defauilt MostPositive - the button that indicates the most positive result LeastPositive - the button that indicates the least positive result None - no default button
Window - evaluated as the owner window. If not specified, the applications main window will be used.
While it's possible to specify more than one of any of the parameters, the Show method will stop evaluating parameters after it encounters one that does not use the default value of the given parameter type. For instance, if your parameter list looks like this:
MessageBoxEx.Show("My message text", MessageBoxButtonEx.YesNo, MessageBoxButtonEx.OK)
...the message box will display the Yes/No buttons, and the OK button will.be ignored. Conversely, if your parameter list looks like this:
MessageBoxEx.Show("My message text", MessageBoxButtonEx.OK, MessageBoxButtonEx.YesNo)
...the message box will again display the Yes/No buttons, because the OK button is the default. Finally, if you do this:
MessageBoxEx.Show("My message text", MessageBoxButtonEx.OkayCancel, MessageBoxButtonEx.YesNo)
...the message box will display the Okay/Cancel buttons because that value was the first value encountered that was not the default.
I certainly can't control what you do, but it's pointless and stupid to specify multiple instances of the same parameter, and it just forces the Show method to handle useless parameters, and takes more time to decide what to do
|
The MsgBoxExtendedFunctionality
Class
The extended functionality class (MsgBoxExtendedFunctionality
) provides a sort of one-stop-shop for any/all of the available/future extensions to the standard message box. It's simply a container for all of the extended functionality objects, and makes using the extended functionality much simpler. The idea is that the user will populate this object with whatever is required in a given situation. Indeed, only the objects that are needed for a given instance of the message box are necessary to be included. The messagebox will simply use its current property values if they are possible affected by the unpopulated objects.
string DetailsText | Get/set the text that will be displayed in the details expander. If this property is populated (not null/empty), the details expander will be displayed automatically. This property is not associated with any of the other extended functionalities. |
MsgBoxExCheckBoxData CheckBoxData | Get/set the CheckBoxData object. This property is not associated with any of the other extended functionalities. |
MsgBoxExDelegate MessageDelegate | Get/set the message delegate object. This object allows the message box icon to be clickable, and provides a mechanism for executing code in response to the click. |
bool ExitAfterAction | Get/set the flag that indicates that the message box should be dismissed after running the message delegate action. |
string DelegateToolTip | Get/set the tooltip displayed when the user hovers the mouse over the message box icon |
MsgBoxUrl URL/b> | Get/set the message box URL object, which causes a clickable URL to be displayed in the message box. |
MessageBoxButtonDefault ButtonDefault | Get/set the default button to use on the message box. If no button is specified, it uses the button that is typically used in a Windows.Forms.MessageBox . |
Using this class is fairly straightforward, in that you use object initializers to populate it, like so:
MsgBoxExtendedFunctionality ext = new MsgBoxExtendedFunctionality()
{
staticButtonDefault = this.ButtonDefault;
MessageDelegate = this.errorDelegate,
ExitAfterAction = true,
CheckBoxData = this.checkboxData,
DetailsText = this.Testmsg(),
URL = new MsgBoxUrl()
{
URL = new Uri("http://www.google.com"),
DisplayName = "Google",
Foreground = Colors.LightBlue
}
};
When you look at the constructor, you can see that everything is set to some default value to make sure each call to MessageBoxEx.ShowEx()
uses its own settings.
public MsgBoxExtendedFunctionality()
{
this.ButtonDefault = MessageBoxButtonDefault.Forms;
this.DetailsText = null;
this.CheckBoxData = null;
this.MessageDelegate = null;
this.URL = null;
this.DelegateToolTip = "Click this icon for additional info/actions.";
}
If you want to add new extended functionality, create your extended functionality object, and add it to this class as a property. You'll probably want to add more static configuration methods inside MessageBoxEx
to handle settings for the new functionality as well.
The MsgBoxExCheckBoxData
Class
This object allows you to create a bindable object that can be evaluated externally of the message box (in the code that showed the message box), and is most likely going to be used to allow the app's user to elect not to see a given message box.
public class MsgBoxExCheckBoxData : INotifyPropertyChanged
{
#region INotifyPropertyChanged
private bool isModified = false;
public bool IsModified { get { return this.isModified; } set { if (value != this.isModified) { this.isModified = true; this.NotifyPropertyChanged(); } } }
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
if (propertyName != "IsModified")
{
this.IsModified = true;
}
}
}
#endregion INotifyPropertyChanged
private string checkBoxText;
private bool checkBoxIsChecked;
public string CheckBoxText
{
get { return this.checkBoxText; }
set
{
if (value != this.checkBoxText)
{
this.checkBoxText = value;
this.NotifyPropertyChanged();
}
}
}
public bool CheckBoxIsChecked
{
get { return this.checkBoxIsChecked; }
set
{
if (value != this.checkBoxIsChecked)
{
this.checkBoxIsChecked = value;
this.NotifyPropertyChanged();
}
}
}
}
A possible usage would something like this:
MsgBoxExCheckBoxData checkboxData = new MsgBoxExCheckBoxData()
{
CheckBoxIsChecked = false,
CheckBoxText = "Don't show this message any more"
};
MsgBoxExtendedFunctionality ext = new MsgBoxExtendedFunctionality()
{
CheckBoxData = this.checkboxData
};
MessageBoxEx.ShowEx("Message text", ext);
When the message box returns, the ext.CheckBoxData
object contains the current checked state of the checkbox that was set in the message box. How you persist this value is entirely up to you.
The MsgBoxExDelegate
Class
public abstract class MsgBoxExDelegate
{
public string Message { get; set; }
public string Details { get; set; }
public DateTime MessageDate { get; set; }
public virtual MessageBoxResult PerformAction(string message, string details = null)
{
throw new NotImplementedException();
}
}
This class allows you make the message box icon clickable, and when it's clicked, executes the code contained in this object (the PerformAction
method). This (abstract) class MUST be inherited, and the PerformAction
method is executed when the icon is clicked.
The MsgBoxUrl
Class
public class MsgBoxUrl
{
public Uri URL { get; set; }
public string DisplayName { get; set; }
public System.Windows.Media.Color Foreground { get; set; }
public MsgBoxUrl()
{
this.Foreground = MessageBoxEx.DefaultUrlForegroundColor;
}
This class allows you to specify a url in the message box that the user can click.
Public Static Configuration Methods
All of the ShowEx()
methods described in this section are intended to provide extended functionality over and above what the standard message box offers. The same overloads are available as for the standard Show()
methods. These methods require that you specify an extended functionality object as the first parameter.
SetMessageBackground(System.Windows.Media.Color color) | Sets the message background color. |
SetMessageForeground(System.Windows.Media.Color color) | Sets the message foreground color. |
SetButtonBackground(System.Windows.Media.Color color) | Sets the button panel background color. |
SetFont() | ASets the font family/size based on the application main window |
SetFont(ContentControl parent) | Sets the font family/sizebased on the specified content control |
SetFont(string familyName, double size) | Sets the font family/size. |
SetButtonTemplateName(string name) | Specify the desired button template name. |
SetMaxFormWidth(double value) | Sets the maximum window width. |
ResetToDefaults() | Resets all configuration items to their default values. |
SetAsSilent(bool quiet) | Toggles system sounds (associated with icons) on/off. |
SetDefaultButton( | Sets the button default. |
There are other public configuration and Show...()
methods, but they are all deprecated, and shouldn't be used.
string DetailsText | Get/set the text string that represents the message details. |
MsgBoxExCheckBoxData CheckBoxData | Get/set the object that enables the display of a checkbox. |
MsgBoxExDelegate MessageDelegate | Get/set the object that makes the message box icon clickable and provides an action to be performed hen the icon is clicked. |
bool ExitAfterAction | Get/set the flag that indicates that the message box should be dismissed after the message box delegate action has been executed. |
string DelegateToolTip | Get/set the clickable icon tooltip text. |
MsgBoxUrl URL | Get/set the object that allows a clickable URL to be displayed. |
History
- 2021.01.15 - Change list:
Bugs Fixed
- Fixed the issue where the
LargestButtonWidth
was "not working". The actual problem was that I was calling this method BEFORE setting the font family/size.
- Fixed the issue that caused an exception if you tried to show a message box from a window's constructor.
- Fixed an issue where no buttons will display if you specify a custom button template name that does not exist.
Added Features
- Added a
ResetToDefault()
method to reset all MessageBoxEx
properties to their default values.
- Added an executable intended to be used from Powershell. See this article -
- When I was working on the previous item, I discovered that the WPF message box is NOT the same as the
Windows.Forms
version. I added buttons missing from the WPF version of message box (that the Windows.Forms
version had) - AbortRetryIgnore
, and RetryCancel
. This applies to both the Powershell message box app and the regular MessageBoxEx
code.
- Added the ability to specify the default button.
Maintenance Stuff
- Moved the
MessageBoxEx
class into a DLL. This allows you to include it in your app without having to add references that your app would not normally include, as well as allowing me to separate the various classes into their own files.
- Deleted the deprecated
ShowWithCheckBox
, ShowWithDetails
, and ShowWithCheckBoxAndDetails
methods, so if you're still using them, change over to the ShowEx
methods.
- I put a compiler definition around all of the old
Show()
and ShowEx()
overloads (see article content for details). - Added code to validate the specified font family name (if you use the
SetFont(familyName, size)
overload).
- Added a default color (
Blue
) for the clickable URL.
- Removed unused
using
statements in all files.
- Modified the way the
maxFormWidth
is handled. The default value is now the width of the primary screen work area minus 100 pixels.
- Added a class reference section to the article so you can see what methods are available.
- Moved the project into VS2019 because VS2107 doesn't allow linebreaks in intellisense comments, and I had a lot of important stuff to say in the comments. Since the code is using .Net Framework 4.72, you can still load the solution in VS2017 without issue.
Article Stuff
- Added a class reference section to the article so you can see what methods are available.
- Fixed a bunch of spelling errors.
- 2021.01.08 - Added the ability to present a clickable URL, removed the arbitrary error-only restriction on the message delegate functionality, and refactored the status
Show
method variants to simplify maintenance as well as ease adoption of the new functionality.
- 2021.01.07 - Edited the article to include more info about the checkbox, details, and clickable error icon. The download ZIP file did not change.
- 2021.01.06 - Added details, checkbox, and clickable Error icon, and removed code dumps from article body. There is, of course, a new downloadable source ZIP file. Finally, I fixed the return value when the user closes the message box with the close button in the title bar.
- 2021.01.04 - Made the
MessageBoxIcon
alignment the same as the standard MessageBox
. I also took a stab at fixing the issue that caused the message box to be replaced as the top-most window of an application. Finally, I updated the screen shots and included screen shots from the standard MessageBox
for a more immediate visual comparison within the article. Once again, I did NOT update the source code displayed in the article, but there is a new ZIP file download.
- 2021.01.03 - Added support for
MessageBoxIcon
and system sounds, changed to using a TextBox
for the message display so you can copy the message to the clipboard, and changed some control alignment to make it "better". I did NOT update the source code displayed in the article.
- 2020.12.25 - Initial publication