In this article, I will introduce three tools which should help any forms developer. They are:
- Dialog Box – an improvement on Message Box.
- Extended Error Provider – adds some additional functionality to the error provider.
- Tool Strip Error Drop Down – A control that hooks onto the Extended Error Provider.
Extended Error Provider
The Error Provider is a great tool. And, anyone who has used it with data binding knows how amazing it is. However, it amazes me how it leaves out so many simple things. For example, how many errors are on the error provider? And, my biggest issue with it – the lack of events. The two events I want are when an error is set and when the errors are cleared.
The error provider is also really hard to inherit from. Nothing useful is declared as virtual, so we can’t override. This means that we will need to use Reflection a lot. I believe the performance issues of this are insignificant; however, the greater problem is that we rely on the internal implementation instead of the public contract. If they were to change the implementation, the control would stop working. But, this is unlikely, and if we want to add extra functionality, it is the only way.
The implementation of the extended error provider can be put into three sections – firing events when using data binding and firing events when not using data binding, and the rest.
Firing events when not using data binding
This is the easy bit. We simply declare new methods for
Clear, call the base, and fire the event. No Reflection required. The problem is, of course, if it is referenced as an Error Provider and these methods are called, the events will not be called.
Firing events when using data binding
This is much harder, and requires an understanding of how the error provider is implemented. The error provider has a field called
errorManager of type
BindingManagerBase. This handles the binding. We need to hook into these events and then fire our own. To see this, look at the
WireEvents method. The other problem is where to subscribe to these events. We want them subscribed when
DataMember change, so we need to declare new methods of this.
A problem we have to face is if the component is initializing, we have to delay subscribing until after initialization. This is a bit I had trouble with. The error provider implements the
ISupportInitialize interface. However, it again is not a virtual method. .NET does give us a solution. By specifying that the Extended Error Provider also implements
ISupportInitialize and writing an
EndInit method, our
EndInit will be called instead of the error provider’s. We also want the base
EndInit to be ran before we wire our events; however, to do this, we must use Reflection.
A few other things to do
The last thing is to implement three properties:
HasErrors. Their implementation is quite simple, but we have to know a tiny bit about the error provider’s implementation. The error provider keeps the list of controls and their error messages in a
items, so by using Reflection to get this
Hashtable, we can easily implement the three properties.
So, we now have our implementation of the extended error provider. But, how useful is it? Well, when closing a form, you can quickly check to see if there are errors on it and display them. But, your business object might be able to do that already. Well, you could use the
ErrorSet event to flash the control when an error appears on it. But, the best example to show how the extended error provider can be used is the Tool Strip Error Drop Down.
Tool Strip Error Drop Down
This is more than a tool to show off the extended error provider, it truly is a tool which you will want to use whenever you have an error provider. The error provider provides a nice UI for errors, but what happens if you have tabs? My point is that not all the errors will be visible at once. The Tool Strip Error Drop Down (if someone can think of a better name, I would be grateful, I think I’ve renamed it about five times and I’m still not happy with it) combats this problem. It sits in the status bar and provides a summary of the errors. Also, when you click an error, it focuses the control and makes it flash. This makes it a really useful way to fix errors.
So, how do you use it? Simply add it to a statusbar and set its
ErrorProvider property. Done! It’s ready to go.
A few more interesting things
ErrorTerminology property is used to change the message from “5 errors” to “5 warnings”. It allows us to use it to display not only errors but warnings as well.
There is an
AutoSort property which sorts by control
TabIndex. It is
false by default so that the latest error always appears at the bottom. There is also a
Sort method. It is a good idea to call this on load after the initial errors are set.
The image used on the component is the icon on the error provider.
When you click on "Should be Number 1", the first textbox will be selected and will flash.
I’ve always been very disappointed by Windows’ Message Box. It lacks so much useful functionality. So many times, when I’ve been sent an error message from someone, it’s a screen capture of a Message Box. Would it really be too hard for Microsoft to allow the text to be selected and copied?
Another issue is how much information do you display in a Message Box? Too much and it confuses the users, too little and it’s too general to figure out what’s happening. And, it can be impossible to find a balance. For example, the developers might want a stack trace, but showing that as your message tells the user nothing and will confuse them. The solution I have developed for the Dialog Box is very simple. It provides a message and an extended message. The extended message is hidden by default, and shown by clicking “More Details”.
I have only used a subset of the Icons - Error, Information, Warning, and Question. I also don’t allow “None”. In my opinion, all messages can be put into the four given types, and not putting in an Icon is lazy and provides a worse interface. A problem with the Message Box is that this happened too often, and a major reason is the message box made it annoying to put in an icon. To put an icon, you have to not only specify the icon but the buttons as well (because the button parameter is before the icon parameter). To make this easier, the dialog box not only has
Display methods but
The Dialog Box also has a copy button to give the user an easy way to copy everything.
My final problem is size. It’s a bit hard to explain the reasons, but I want it to be resizable.
The Dialog Box also has a
UseStandardDialogs variable which can be used to use the Message Box instead.
Dialog 1: Simple and does not confuse the end user, but gives us no information and we have no idea what is going on.
MessageBox.Show(this, "An unknown error occured",
Dialog 2:Provides the developer with useful information which will help them figure out what happened. But for the end user, it is meaningless and they should not be forced to see this.
MessageBox.Show(this, ex.StackTrace, "Error",
Dialog 3, 4, and 5:A Dialog Box by default shows the message (Dlg3). The user can click More Details and get more info (Dlg 4). Also note the nicer shape of the dialog compared to Dialog 2. The dialog can then be resized if the user thinks it will make the data more readable (Dlg 4).
DialogBox.DisplayError(this, "An unknown error has occured", ex.StackTrace);
I would like to add to the Extended Error Provider an
ErrorDoubleClick event which gets fired when an error icon gets double clicked. It would allow me to show a dialog box with the error in it. However, I have no idea how to do this, and I have a feeling its impossible. But, if anyone knows how to do this and would like to show me how, I would be really grateful.
Also, the icons used in the
DialogBox are obviously from Windows Vista. I'm not sure what copyright applies to this. If I'm not meant to use them, please tell me and I'll find some replacement ones.
Finally, if you're going to give this a negative vote, can you please leave feedback? I just find it weird that people say "I don't like this" and then not give a reason. If reasons are given, I can improve the components.