C# Interface: Understanding the Prize Form Wire-Up (Tim Corey, Lesson 09)
In Tim Corey’s “C# App Start To Finish” series, Lesson 09 focuses on wiring up a prize form. On the surface, this form seems simple—just gather user input, validate it, create a model, and save it. But Tim explains that the real complexity lies in deciding where to save the data: a database, a text file, or both. Tim’s video walks us through the solution by introducing a core concept in C# programming: interfaces.
In this article, we will take a deeper look at interfaces through Tim’s explanation so you can better understand how they help make scalable, maintainable applications.
The Problem: Where Do We Save the Data?
Tim begins by stating the purpose of the prize form: it accepts input, validates it, and saves it to storage. But he warns that the tricky part is deciding where to store the data. He emphasizes that tutorials often skip this because it is not easy, but he wants learners to tackle it head-on.
He explains that initially, you might attempt a simple solution: check if you are using SQL or a text file, then execute the correct saving process. But Tim quickly shows how ugly and unmaintainable that becomes. If every form has to check which storage type to use, the code becomes duplicated, messy, and difficult to change.
The Ugly Way: Hardcoded Conditions
Tim sketches out a pseudo-code example. He explains that you might start by checking a Boolean value like usingSQL == true, then opening a database connection, saving the model, and returning it with an ID. Then you might do the same for text files, manually generating an ID because text files don’t do it automatically.
He points out that this quickly becomes repetitive. Multiple forms need this logic, and every time you add a new data source like MySQL, you must update every form. Tim calls this “not scalable” and emphasizes it violates the “DRY” principle (Don’t Repeat Yourself). He states clearly: “There’s got to be a better way.”
Pulling the Thread: The Better Approach
Tim introduces his strategy: pulling the thread. He starts by asking what information the code needs and where it comes from. He identifies two key questions:
How do we know which data source to use?
How do we connect to two different data sources to do the same task?
Tim explains that the actual act of saving is the only thing that differs. From the form’s perspective, it only needs to say: “Here is the model. Save it.” The form should not care whether it’s saving to SQL or a text file.
The Solution: Global Configuration + Interface
Tim suggests a global configuration system. He says that to know which data source to use, the application needs globally accessible data, and he proposes using a static class to store this information. He acknowledges that global variables are usually avoided, but in this case, global data is exactly what is needed.
Next, Tim explains the key concept: interfaces. He defines an interface as a contract—a promise that any class implementing it will contain certain methods or properties. Tim emphasizes that this allows the application to call the same method regardless of the data source. The form doesn’t care if it’s SQL or text file; it only cares about calling the method.
Tim says, “If you need to do the same task but behind the scenes it will be done two different ways, you need an interface.”
Creating the Interface
Tim moves to the practical implementation by creating an interface in the Tracker Library. He names it IDataConnection and explains the convention of prefixing interfaces with “I”. He highlights that this is important to clearly identify it as an interface.
Tim adds a single method to the interface:
PrizeModel CreatePrize(PrizeModel model);He explains that this method is a contract: it must exist in any class that implements IDataConnection. The form will call this method and expect to get back a PrizeModel with an ID. Tim explains that this is how the form stays agnostic to the storage type.
Creating the Global Config Static Class
Next, Tim creates a static class named GlobalConfig. He explains that a static class cannot be instantiated and is globally accessible. This is where the application will store the list of available data connections.
He defines a property:
public static List<IDataConnection> Connections { get; private set; }Tim explains the use of private set so that only the class itself can modify the list, while other parts of the application can only read it.
He then creates the method:
public static void InitializeConnections(bool database, bool textFiles)This method sets up the available data connections. Tim emphasizes that the list allows multiple connections, which means the application can save to SQL, text files, or both.
Understanding Interfaces: A Real-World Example
Tim pauses to reassure learners that this is complex material, but achievable. He recommends watching the video once, then re-watching while coding along.
He explains that the interface is a contract, and any class implementing it must follow the contract. He demonstrates this by creating a SQLConnector class that implements IDataConnection.
When the class is created, Visual Studio warns that the contract is not fulfilled. Tim shows how to use “Implement Interface” to automatically generate the method CreatePrize. He also explains the NotImplementedException scaffold and why it exists—it allows the code to compile without pretending the method works.
Creating the SQL and Text Connectors
Tim adds the SQLConnector class and the TextConnector class, both implementing IDataConnection. He explains that even though saving to a SQL database and saving to a text file are very different processes, they both satisfy the same interface contract.
He adds a simple sample return value for now and places TODO comments to remind himself to implement the real saving logic later. This keeps the application functional while still progressing through the lesson.
Final Setup: Wiring the Global Config
Tim returns to the GlobalConfig class and wires up the actual connections. He shows how to initialize the Connections list and add instances of SQLConnector and TextConnector to it.
He explains why two separate if statements are necessary instead of if-else—because the user may want to save to both data sources simultaneously.
Where to Call InitializeConnections?
Tim explains that InitializeConnections must be called at the start of the application. He modifies Program.cs and calls:
GlobalConfig.InitializeConnections(true, true);before launching the form. This ensures the connection list is ready and accessible throughout the application.
He then changes the startup form to CreatePrizeForm so he can test the functionality immediately.
Validating the Prize Form
Tim opens the form and explains the first task: validating the four fields. He prefers to keep event handlers clean, so he creates a private method named ValidateForm().
Tim explains that this method can be called from anywhere, not just the button click. It returns a Boolean indicating whether the form is valid or not. He shows his pattern of using an output variable:
bool output = true; return output;He says he likes starting with true because it’s easier to change to false when something is wrong than it is to set it to true after every check.
Checking Place Number
Tim explains the first validation: the place number must be an integer greater than zero.
He uses int.TryParse to convert the PlaceNumberValue.Text (a string) into an integer. Tim breaks down how TryParse works:
It takes a string and tries to convert it into a number.
It returns a Boolean indicating success or failure.
- It uses an out parameter to output the converted value.
Tim emphasizes that TryParse is safer than Parse because it doesn’t crash on bad input—it returns false and sets the output to zero.
He then explains the logic:
If placeNumberValidNumber is false, set output = false.
- If placeNumber < 1, set output = false.
Tim warns against using else statements here because this method has multiple checks. If one check fails, the method should still evaluate the others to collect all errors.
Validating Place Name
Tim moves to the next validation: the place name must not be empty.
He checks:
if (placeNameValue.Text.Length == 0) { output = false; }Tim explains that in a real application, you would display error messages for each failed validation. But for now, he keeps it simple and only returns true/false.
Validating Prize Amount vs Prize Percentage
Tim explains that the form must contain either a prize amount or a prize percentage (one of them must be greater than zero). He notes the important difference:
Prize percentage is a whole number (int)
- Prize amount is a decimal (decimal) because money can include cents.
He creates variables:
decimal prizeAmount = 0; int prizePercentage = 0;Then he uses TryParse for both:
bool prizeAmountValid = decimal.TryParse(prizeAmountValue.Text, out prizeAmount); bool prizePercentageValid = int.TryParse(prizePercentageValue.Text, out prizePercentage);Tim explains that both must be valid numbers. If either is invalid, the form is invalid.
Next, he checks that at least one is greater than zero:
if (prizeAmount <= 0 && prizePercentage <= 0) { output = false; }Tim also adds a check to ensure percentage is between 0 and 100:
if (prizePercentage < 0 || prizePercentage > 100) { output = false; }He explains why: 150% would mean you’re giving away more than the prize pool, which is impossible.
Using the Validation Result
After all checks are done, Tim explains how to use the result:
if (ValidateForm()) { // create model and save } else { MessageBox.Show("This form has invalid information. Please check it and try again."); }Tim notes that you could return early on the first failure, but he chooses to run all checks so users can see all validation errors at once. This reduces frustration because they can fix everything in one go.
Creating the PrizeModel
Tim explains that once the form is valid, the next step is to create a PrizeModel.
He demonstrates how to instantiate a model:
PrizeModel model = new PrizeModel(); model.PlaceName = placeNameValue.Text; model.PlaceNumber = placeNumberValue.Text; // problem: this is a stringTim highlights the issue: PlaceNumber is an int, but the form value is a string. To solve this, he explains two options:
Parse each value again in the form (repetitive).
- Add a constructor overload in PrizeModel.
Tim chooses option 2.
Overloaded Constructor in PrizeModel
Tim adds an overloaded constructor that accepts four strings:
public PrizeModel(string placeName, string placeNumber, string prizeAmount, string prizePercentage)
{
PlaceName = placeName;
PlaceNumber = int.TryParse(placeNumber, out int placeNumberValue) ? placeNumberValue : 0;
PrizeAmount = decimal.TryParse(prizeAmount, out decimal prizeAmountValue) ? prizeAmountValue : 0;
PrizePercentage = double.TryParse(prizePercentage, out double prizePercentageValue) ? prizePercentageValue : 0;
}Tim explains that he doesn’t care if the parsing fails because it will default to zero, which is the default value for numbers anyway.
This constructor allows the form to create a PrizeModel directly with string inputs and let the model handle parsing.
Saving the Model Using IDataConnection
Now that the model exists, Tim explains how to save it using the global list of connections.
He uses a foreach loop:
foreach (IDataConnection db in GlobalConfig.Connections) { db.CreatePrize(model); }
Tim explains that this loop calls CreatePrize() on each connection (SQL and text file). Even though the methods aren’t implemented yet, the form works and pretends to save the data. This proves that the interface and global configuration pattern is working.
Testing the Form
Tim stresses the importance of testing early. He adds breakpoints and runs the application.
He first tests an empty form.
He steps through ValidateForm().
He sees the output is false and the validation fails as expected.
Then he fills in valid data and confirms the constructor populates the model correctly.
- He also confirms the loop runs through both connections.
Tim demonstrates that the form is functional and the pattern is validated.
Final Cleanup: Clearing the Form
Tim makes a couple of final tweaks:
After successfully creating a prize, clear the form fields.
- Set default values of 0 for prize amount and percentage so users don’t have to type zero every time.
He then confirms the form clears correctly after a valid submission.
What Comes Next?
Tim ends the video by saying the next step is to wire up the SQL and text connection classes so they actually save the data.
He reminds viewers to stay tuned for the next lesson, where he will implement the SQL connector and actually connect to the database.

