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, the
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
MessageBoxEx (the 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 accordingly.
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 screen.
- 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 nice.
- 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 correct
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
System.Reflection 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 its 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 this
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 the 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 in.
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 them.
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 them.
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 you 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!
- 6th September, 2012 - Created the article
- 8th October, 2012 - Fixed a number of bugs reported by various readers
I am a researcher/advisor with the Dutch knowledge institute Deltares. My specialty is water related ICT development. Although focusing more and more on the project management side of these matters, I still like to write some code now and then. It is fun and helps me keep in touch with our software developers.