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:
[ReflectionPermission(SecurityAction.Demand, MemberAccess=true)]
in front of the ResetExceptionState
method.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
Sorry, this blog does not support comments. Why not?
I used various blog hosting services since this blog was established in 2005, but unfortunately they turned out to be unreliable in the long term and comment threads were lost in unavoidable transitions. At this time I don't want to enable third-party services for comments since it has become obvious in recent years that these providers invariably monetize information about their visitors and users.
Please use the links in the page footer to get in touch with me. I'm available for conversations on Keybase, Matrix, Mastodon or Twitter, as well as via email.