C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
126: An Overview of WinForms
WinForms is the "old reliable" of the .NET world. It isn't flashy, and it doesn't have the modern styling of WPF or WinUI, but for building internal company tools or quick utilities, it's incredibly efficient. The danger with WinForms, however, is that the Visual Studio Designer makes it too easy to do things the wrong way. It invites you to treat your UI as the brain of your application, which is a recipe for a maintenance nightmare.
The Designer Trap: Putting Everything in the Click Event
Imagine you're building a simple Customer Lookup tool. You drag a TextBox for the customer name, a Button to trigger the search, and a DataGridView to show the results. The naive approach—and the one I see most often from developers new to desktop apps—is to double-click that button and write all the logic right there in the btnSearch_Click method.
// The naive "Smart UI" way
private void btnSearch_Click(object sender, EventArgs e)
{
string name = txtCustomerName.Text;
// Imagine this is a slow database call
var results = database.Customers.Where(c => c.Name.Contains(name)).ToList();
dgvResults.DataSource = results;
}
On the surface, this works. It's three lines of code and you're done. But you've just fallen into the "Smart UI" anti-pattern. By putting your data fetching and filtering logic directly inside the Form class, you've coupled your business logic to your UI. If you ever want to change how customers are searched, or if you want to write a unit test to ensure the search logic handles nulls correctly, you can't—because that logic is trapped inside a private method of a UI class that requires a windowing environment to run.
The Cost of a Frozen Interface
There's a more immediate problem with the code above: the "Not Responding" ghost. WinForms runs on a single UI thread. When you call .ToList() on a slow database query inside a click event, you are blocking that thread. The window stops painting, the user can't move the form, and Windows eventually decides the app has crashed, whitening the screen and putting "Not Responding" in the title bar.
I've spent far too many hours of my career fixing "bugs" that were actually just synchronous database calls on the UI thread. In a console app, a three-second pause is a blip; in a GUI app, it's a failure of the user experience.
Separating Concerns and Going Async
The better way is to treat the Form as a thin wrapper. Its only jobs are to collect input from the user and display the output. The actual "work" should live in a separate service class. Furthermore, we use async and await to ensure the UI thread remains free to keep the window responsive while the data is being fetched.
// A separate service for logic
public class CustomerService
{
public async Task<List<Customer>> SearchCustomersAsync(string name)
{
// Simulate a network/DB delay without blocking the thread
await Task.Delay(1000);
return await _db.Customers.Where(c => c.Name.Contains(name)).ToListAsync();
}
}
// The lean Form logic
private async void btnSearch_Click(object sender, EventArgs e)
{
btnSearch.Enabled = false; // Prevent double-clicking
try
{
var service = new CustomerService();
var results = await service.SearchCustomersAsync(txtCustomerName.Text);
dgvResults.DataSource = results;
}
finally
{
btnSearch.Enabled = true;
}
}
Notice the difference. The CustomerService doesn't know that a DataGridView exists; it just returns data. This means I can test the search logic in a separate project without ever launching a window. In the Form, the await keyword tells the program: "Go do this work on a background thread, and come back here to update the grid once you're finished." The application remains snappy, the window can still be moved, and the user isn't wondering if the program crashed.
📋 Practical Task
Exercise: Building a Responsive Product Inventory Searcher
Your goal is to create a WinForms application that simulates searching a large product inventory without freezing the UI.
- Create a Form with a
TextBox(for the product name), aButton(to search), and aListBoxorDataGridView(to show results). - Create a separate class called
InventoryService. Inside this class, write anasyncmethod calledGetProductsAsync(string query). - Inside
GetProductsAsync, useawait Task.Delay(2000)to simulate a slow network response before returning a hard-coded list of strings (e.g., "Laptop", "Mouse", "Keyboard") that match the query. - In the Form's button click event, call the service using
await. - Crucial: Implement a visual cue (like changing the button text to "Searching..." or disabling the button) that triggers when the search starts and reverts when the results are displayed.
- Test the app by clicking search and attempting to drag the window around while the "delay" is happening. If the window moves freely, you've succeeded.
There are no comments for now.