8

I found something that I do not really get:

protected override void OnPaint(PaintEventArgs e)
{
    DrawChar(e.Graphics);
    base.OnPaint(e);
}

void DrawChar(Graphics g)
{
    if (body != null)
    {
        g.DrawImage(body, X, Y);
    }
}

Suppose the "body" is empty - If I remove the condition in the DrawChar, the program never draws anything and I found out that the onPaint is not even raised anymore (e.g. when resizing or minimazing and restoring the window).

EDIT: The point is - if the DrawImage fails (which you do not know from the debugger, it simply does not draw the image e.g. when the Image is null), the OnPaint event in the application ceases to be raised.

John V
  • 4,855
  • 15
  • 39
  • 63
  • 2
    I may be wrong on this, but I believe the OnPaint event is raised only if there is something to redraw. So you either need to invalidate the control, or to give it a reason to be redrawn (for example resizing it) – Kevin Gosse Aug 14 '18 at 15:40
  • @KevinGosse Yes, if course, I mean that the onPaint is not raised anymore when e.g. switching the tabs or restoring the window. It happens as soon as the drawImage does not find the picture to draw. – John V Aug 14 '18 at 15:42
  • 1
    @Pietrossek "Switching the tabs" seems like something where the control contents might have been saved. Ditto for "restoring the window". But it should be easy to check if this is indeed the problem: add a button which does nothing but explicitly invalidate your control. See what happens when you click it. –  Aug 14 '18 at 15:45
  • @hvd Yes, I tried - onPaint is not raised. I do not get it. As if the failed DrawImage caused some error state. – John V Aug 14 '18 at 15:48
  • What do you mean by "failed"? Does it error out of the `DrawChar` method and therefore not call `base.OnPaint`? – DavidG Aug 14 '18 at 15:49
  • Interesting find and explanation! It would warrant an answer and upvote to keep in the archives, imo. – TaW Aug 14 '18 at 15:55

1 Answers1

14

As if the failed DrawImage caused some error state

Yes, that is exactly what it does. An exception in a Paint event handler is extremely awkward, it makes continuing to debug a program hard to do. If nothing were done, such an exception would be raised over and over again when you continue debugging, making too difficult to diagnose another exception in the program. This concern is dated, goes back to the Win2000/XP days when Aero did not yet exist.

The method that does this is the one that calls OnPaint(), it is an internal method named Control.PaintWithErrorHandling(). Have a look-see, the error state you correctly postulated is named STATE_EXCEPTIONWHILEPAINTING. When it is set once then it always falls back to PaintException(), it draws the Red Cross of Failure. Thus avoiding the risk of raising the exception again. I'll copy/paste the comment:

    // Exceptions during painting are nasty, because paint events happen so often.
    // So if user painting code ----s up, we make sure never to call it again,
    // so as not to spam the end-user with exception dialogs.

The dashes used to spell the f-word, sanitizing the source code before open-sourcing it took quite a while and caused a fair amount of damage when they tried to automate it :)

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    also anectotal: instead of the "red cross of death", they initially attempted some [convoluted masterpiece](https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs,9632) where they would paint the exception text, using advanced font rendering with `SetMeasurableCharacterRanges` and the like. this probably ----s up more than the user's exception, so they put it into a `#if false` region... :) – Cee McSharpface Sep 18 '18 at 21:17