I am a 'programmer' who cares much about localization. As we work all over the world, this is
not always easy. Fortunately most of our clients are highly educated and speak English very well.
Nevertheless, I do care about localizing our applications.
One of the things that kept irritating me was the fact that, at least under Windows™ XP,
MessageBoxes didn't care about the
Thread.CurrentThread.CurrentUICulture. Whatever I set it to, the messagebox buttons
would use the language of the installed version of Windows. So, having a bit of time, I decided to
search for a solution. I 'Googled' and 'Googled', but none of the articles I found handled the
problem to my satisfaction. So after some deliberation I decided to write a
And to thank you all for the many tips and tricks found on CodeProject I decided to write this
article as well.
The replacement should satisfy a number of requirements:
- It should offer transparent
replacement for all the
MessageBox methods. That is to say: if I
used search and replace to change
name I decided on) and added the correct reference, there should never be a problem because someone
called a messagebox method my code did not support.
- In the normal configuration, it
should look as close to the real thing as possible.
- It should detect the
Thread.CurrentThread.CurrentUICulture and be able to change the button captions
And, having decided to write my own messagebox, I liked to include a number of additions that
were missing from Microsoft's version. They were:
- The possibility to centre the message on the calling form, rather than in the centre of the
- It should be possible to specify a time-out, after which the default action would be taken.
Since some of our programs are very computation intensive, we tend to start them when we leave the
office. When you come back the next morning, it's very frustrating to see that instead of having
completed, the program has been waiting for some user confirmation all night.
- Why have only the four icons from the Microsoft version? So I would like to specify my own,
more appropriate ones.
- Sometimes one would like to give users the option to stop reporting the same problem over and
over again. So a checkbox where one could indicate "do not show this message again" would be
- And while I was busy, why not include the possibility to change the font or the colours?
And the opacity?
Future addition might be the possibility to define your own captions.
So I started the project. Much of it was relatively straightforward. But a number of
interesting problems, or better challenges, soon came to light.
Sizing the Buttons
I don't know how Microsoft does it, but when you support multiple languages (and multiple fonts),
you cannot simply give the form and buttons a constant size. On the other hand I didn't want to
have the button size varying with the buttons shown. Once a language was selected, all the buttons
should have the same size. Luckily the number of texts is fairly small, so the solution was to
measure all the possible captions and pick the widest to determine the button size. To give a nice
appearance I discovered that a size relative to the measured size worked best. I could have
chosen to apply a fixed margin instead, but this looks better.
The code that does this:
private static System.Drawing.SizeF measureButtons(Font usedFont)
SizeF maxSize = new Size(1,1);
size = measureString(Deltares.Controls.Properties.Resources.buttonTextAbort, usedFont);
if (size.Width > maxSize.Width) maxSize = size;
maxSize = new SizeF((int)(1.6f * maxSize.Width), (int)(1.75f * maxSize.Height));
The next problem was displaying the help. There are a number of
Show methods which
cause a messagebox to add a help button. Adding the button was no problem, but how did the
messagebox function without knowing which file to display? After some experimenting I discovered
that the original box probably used the information from a
HelpProvider on the main
form. If I did not supply one, pressing the help button did nothing. If I had one and gave it the
HelpNamespace, a press on the help button would show the file. Okay, now I
needed to mimic this behaviour. This took me deep into the
namespace. But I did solve it! Just loop over all types in the EntryAssembly (that's the one
likely to contain a
HelpProvider). Then check if the type is a form. If it is, loop
over it's fields to find if any of them is a
HelpProvider. If it is, use
Activator to create an instance and
GetValue on the field to get access to
HelpProvider. Check if the namespace is provided, and if it is, we are done:
Assembly ass = Assembly.GetEntryAssembly();
Type types = ass.GetTypes();
foreach (Type type in types)
if (type.BaseType.Equals( typeof(System.Windows.Forms.Form)))
FieldInfo fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (FieldInfo fi in fields)
object inst = Activator.CreateInstance(type);
System.Windows.Forms.HelpProvider hp = fi.GetValue(inst) as System.Windows.Forms.HelpProvider;
if ((hp != null) && (hp.HelpNamespace != null))
You will find above code in the
getHelpFilePath method of the
Getting the Extra Information in (And Out)
To pass all the extra information to a messagebox that required it (font, colours, etc), I could
have chosen to create a multitude of new overloads. Added to the 21 already existing, that would
have created too many variations. So I decided to only add 21 new overloaded methods, each having
one extra structure added to the list of arguments. The structure would then contain all the
information needed for the extra functionality. Thus the
Using a Time-Out
To specify a time-out, you can enter the number of milliseconds to wait. It has to be a value
between 1 and 86400000 (one day). To inform the user that the box will disappear, I display a
count-down progressbar. As soon as it reaches 0, the default action as defined by the
defaultButton parameter, will be taken. However the system progressbar is so ugly ...
so I added a simple 'subclassed' progressbar. The whole progressbar is provided via the
MessageBoxExtras, and you can use the system bar as well as the replacement. How
prominent the bar is you can decide by setting the height to the correct value before passing it
To count down from the specified time I used a timer. But if you use a
System.Windows.Forms.Timer, the thread that is running the screen updates will be
blocked so frequently, that the progress bar is not updated often enough. So I use a
System.Threading.Timer. But because that timer can be running on any thread from the
threadpool, you have to take extra precautions when updating the progress bar. So the code executed
each tick checks if an
Invoke is required and if so, uses a callback to the method
There were some other difficulties, but you can inspect the code to find out how I solved
The Sample Application
Even before starting the code for the
MessageBoxEx class, I created a sample
application to test both the original version and my new version. I simply had to know how the
original behaved to be able to create my own version. I could (and should?) have made an
application that tested all 42 overloaded
Show methods, but decided only to test 4 of
In the application you can select which messagebox to use: the system one or my version. Of
course some options are only available when using the extended version: to use other icons, you can
select two additional icons when using the extended box. Of course this is just an example, in real
life you can supply your own 32 * 32 pixel icon. To test various fonts and colours, click on the
font button or the colour labels. A dialogue will help your select your favourite. Time out (in
seconds) is given via the timeout numeric input. The other two checkboxes help you specify that a
checkbox should be presented or the form should centre on the owner. And then click on "Try".
After selecting an option, you can see that the correct values are returned next to the button.
And if a checkbox was present, the state of that checkbox is also displayed.
I hope this messagebox replacement helps you create even nicer programs. Any suggestions,
criticism and additions (more languages!) are welcome.
And don't forget: have fun coding!
- September 6, 2012 - Created the article.
- October 8, 2012 - Fixed a number of bugs reported by various readers.