Most people working with WinForms have probably encountered that red X that is drawn over a control at some point and just doesn’t go away as long as the application is running. Originally, I had a look at the source of this some months ago and now, when I saw a relating question again, I thought I might document my findings here.
Note that I did that research with .NET 1 and I haven’t checked for .NET 2 yet, so in the latter case YMMV. So where does the red X come from? Simple: The System.Windows.Forms.Control
has an internal state flag for this that gets set when an exception is thrown in the control’s drawing code. So if you’ve never seen the red X but you want to, just throw a panel on a form and create a Paint
event handler like this:
private void panel1_Paint(object sender, System.Windows.Forms.EventArgs e) {
throw new Exception("Boom");
}
Now, the really interesting thing about the red X is that you can’t easily get rid of it once it’s popped up. The only “official” way is to restart the application. Lucky though that .NET has powerful reflection… that makes it possible to use the following method to reset the state:
void ResetExceptionState(Control control) {
typeof(Control).InvokeMember("SetState", BindingFlags.NonPublic |
BindingFlags.InvokeMethod | BindingFlags.Instance, null,
control, new object[] { 0x400000, false });
}
So you can get that panel in the example above to have another go at drawing itself by going
ResetExceptionState(panel1);
panel1.Invalidate(); // invoke redraw
Of course, if the same exception is still thrown from the paint handler, there won’t be much to see as the state is immediately set back to show the X again. Generally, of course, you should have a very close look at the reason why there’s an exception thrown in the paint handling code at all. But there are situations where you might want to control the Control’s behaviour in detail and in these cases it’s nice to be able to handle that internal state yourself. Two additional notes:
- You do need certain permissions to get that reflection code to run. If you want to configure your application for exact permission sets, you should use
[ReflectionPermission(SecurityAction.Demand, MemberAccess=true)]
in front of theResetExceptionState
method. - As I got a request for this at the time, I have a translation of the method in VB.NET, too. I don’t usually use VB, so there may be more elegant ways to do this, but here goes:
Private Sub ResetExceptionState(ByVal control As Control)
Dim args() As [Object] = {&H400000, False}
GetType(System.Windows.Forms.Control).InvokeMember("SetState", _
BindingFlags.NonPublic Or BindingFlags.InvokeMethod Or _
BindingFlags.Instance, _
Nothing, control, args)
End Sub