The "why" is a long and painful story that doesn't fit that well in an SO answer. Golden rule is that an exception must never cause the dispatcher loop to terminate. You get the dispatcher loop to loop by calling Application.Run(), as is done in your Main() entrypoint, or Form.ShowDialog() as is done in this snippet. It is that loop that ensures that ShowDialog() does not return until you close the dialog.
Winforms observes that rule by having a try/catch-em-all statement in the dispatcher loop. It raises the Application.ThreadException event if an event handler throws an exception. If you don't replace or disable the event handler (you should) then you get to see the ThreadExceptionDialog, the one that lets the user gamble at "Continue" or wisely click "Quit".
That catch-em-all handler is however very inconvenient when you debug. It gets in the way of diagnosing unhandled exceptions. So Winforms checks if you debug and if you do will not use the try/catch-em-all and not raise the ThreadException event. That has a big side-effect on the code in your snippet, now that catch statement does seem to work just fine. But as soon as you run your program the way your user will do, without a debugger, then it never catches anything.
So the cold hard fact is that throwing an exception in an event handler is just the wrong thing to do. There isn't anybody around to catch it, other than the wonky ThreadExceptionDialog. Instead of throwing, you must do what an exception handler would do. For a Validating event that ought to be ErrorProvider, giving the user a gentle hint that his data entry is not correct. Or setting e.Cancel = true to force him to enter valid data.