Introduction: Error Handling in a C# Windows Forms Application
In Lesson 25 of the “C# App From Start to Finish” series, Tim Corey focuses on a critical but often misunderstood topic: error handling in a C# Windows Forms application. Tim explains that error handling is not just about throwing try-catch blocks everywhere, but about intentionally designing how your application responds to invalid input, unexpected situations, and user mistakes.
In this lesson, Tim walks through real examples using the Tournament Viewer form built earlier in the series. By watching how errors occur and how they should be handled, we get a deeper, practical understanding of when to let an application fail, when to stop execution, and when to guide the user with meaningful feedback. Let’s take a detailed look at Tim’s explanation, step by step, directly from the video.
Understanding the Problem: Unhandled Exceptions
At the start of the lesson, Tim introduces the goal: adding basic error handling to the existing Tournament Viewer form. He immediately demonstrates a real issue—when both teams are given the same score and the “Score” button is clicked, the application throws an exception.
Tim explains that while this behavior is visible in Visual Studio, the situation is worse for end users. If the application were running as an .exe, the error message would appear and then the application would crash once the message box is closed. That, Tim emphasizes, is unacceptable behavior for a user-facing application.
Why Blanket Try-Catch Blocks Are a Bad Idea
Tim then discusses a common mistake developers make: wrapping entire methods in a try-catch block and calling that “error handling.” He strongly criticizes this approach, calling it closer to “error eating” than real handling.
At around this point, Tim explains an important philosophy: If an application fails in an unexpected way, it should fail spectacularly. Silently hiding errors makes debugging harder and allows corrupted state to spread. The only time errors should be intercepted is when they are expected and caused by the user.
Targeted Try-Catch in the UI Layer
Instead of wrapping everything, Tim shows how to apply a try-catch block only around the line of code that might fail. He demonstrates surrounding the scoring logic with a try block and catching an Exception with a named variable.
Tim stresses two best practices here:
Always name your exception variable so you can access its details.
- Never rethrow using throw ex; because it destroys important stack trace information. Instead, use throw; if rethrowing is required.
In this case, since the error occurs in the UI, Tim chooses to handle it right there by showing a MessageBox with the exception message.
Improving User Feedback with MessageBox
Tim adds a MessageBox.Show call that displays a clear error message to the user. When the tied score is entered again, instead of crashing, the application now shows:
“The application had the following error: We do not allow ties in this application.”
Tim points out that this is already a big improvement. The error is handled, the database is not updated, and the application continues running safely.
Never Trust the User: Input Validation
One of Tim’s core principles is repeated clearly here:\ Never trust the user.
At this stage, the application assumes users will enter valid numeric scores. Tim explains why this is dangerous and introduces the idea of validating user input before attempting to process it.
He creates a private method called IsValidData that checks:
Whether both score inputs are valid numbers
Whether both scores are zero
- Whether the scores are tied
Initially, this method returns a bool, allowing the calling code to stop execution and display a generic error message.
From Boolean Validation to Descriptive Errors
Tim is not satisfied with a generic “You need to enter valid data” message. He explains that good error handling should tell the user exactly what went wrong.
To improve this, he changes the validation method to return a string instead of a boolean. An empty string means no error; otherwise, the string contains a specific message such as:
“Score 1 value is not a valid number”
“You did not enter a score for either team”
- “We do not allow ties in this application”
This allows the UI to show targeted, meaningful messages instead of vague warnings.
Fixing Logic Errors with Else-If Chains
After testing, Tim notices a logic flaw: invalid numeric input sometimes triggers the “ties not allowed” message. He explains why this happens—failed numeric parsing sets values to zero, and separate if statements allow later conditions to overwrite earlier messages.
To fix this, Tim converts the validation checks into an else-if chain. This ensures that once one error condition is met, the rest are skipped. Tim explains that this makes the logic clearer, safer, and easier to maintain.
Error Handling Is Not Just Try-Catch
Tim takes a step back and clarifies a key takeaway:\ Error handling doesn’t always mean using try-catch blocks.
Manual validation—checking user input before processing—is just as important. By validating early, the application prevents bad data from ever reaching the database or business logic.
He also explains that not everything needs validation. Closed systems like dropdowns and list boxes already restrict input. Free-text fields, however, must always be validated.
Where Error Handling Should Live
Toward the end of the lesson, Tim answers a common question: Where should error handling go?
His rule of thumb:
Validation should exist throughout the application, including the backend.
- Exceptions should usually be caught in the front end, because that’s where the user can be informed.
Tim notes that backend exception handling makes sense only when the system can recover—such as switching from a SQL database to a text file if SQL is unavailable.
Final Thoughts on Error Handling
Tim concludes by reinforcing that good error handling improves application stability, user experience, and long-term maintainability. He warns against blanket try-catch blocks and encourages developers to think intentionally about validation and exception flow.
This lesson sets the foundation for building resilient Windows Forms applications—ones that guide users, protect data, and fail safely when necessary.

