Windows Forms (WinForms) is Microsoft’s classic UI framework for building rich‑client desktop apps on Windows. Since .NET 6 the stack is fully open‑source, designer tooling was rebuilt, and .NET 8 added modern DPI‑aware controls and faster data‑binding.
.NET 8 LTS
or .NET 9 Preview
(download)Visual Studio: File ▸ New ▸ Project ▸ Windows Forms App (.NET)
CLI:
dotnet new winforms -n HelloWinForms
cd HelloWinForms
dotnet run
The template’s Program.cs
now contains the one‑liner bootstrapper introduced in .NET 6:
ApplicationConfiguration.Initialize();
Application.Run(new MainForm());
📝 Why? ApplicationConfiguration.Initialize()
enables high‑DPI, default fonts, and modern‑style rendering before the first form appears.
Every window inherits System.Windows.Forms.Form
.
Override lifecycle methods or handle events to inject logic at UI milestones:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent(); // autogenerated designer code
Text = "WinForms 101";
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
MessageBox.Show("Form loaded!");
}
}
InitializeComponent()
is generated by the designer; edit safely in the visual editor.OnLoad
, OnShown
, OnClosing
let you run code at key moments.var btn = new Button {
Text = "Click me",
Location = new Point(20,20), // x,y
AutoSize = true
};
btn.Click += (_, _) => lblMessage.Text = "Hello!";
Controls.Add(btn); // attach to form
The Visual Studio Designer serializes properties to .Designer.cs
.
Prefer the Designer for static layout; use code when you need runtime‑generated UI.
TableLayoutPanel
gives grid‑style layout; FlowLayoutPanel
auto‑wraps controls.UI interaction is event‑driven. Subscribe using +=
and a lambda or method group:
btnSave.Click += HandleSave;
void HandleSave(object? sender, EventArgs e)
{
// validate & persist
}
Long‑running I/O? Use async
/await
or BackgroundWorker
to keep the UI responsive:
private async void btnFetch_Click(object sender, EventArgs e)
{
btnFetch.Enabled = false;
var json = await http.GetStringAsync(apiUrl);
txtResult.Text = json;
btnFetch.Enabled = true;
}
Windows Forms offers two‑way binding via BindingSource
. .NET 8 sped up list‑change propagation and added strong‑typed IBindingList
.
bindingSource1.DataSource = LoadCustomers();
dataGridView1.DataSource = bindingSource1;
For testability adopt an MVP or MVVM‑lite pattern: keep business logic in presenter/view‑model classes and make the form a dumb view.
.resx
; reference via Properties.Resources.Logo
.Localizable = true
, then switch designer culture to create per‑culture *.resx
files.ApplicationConfiguration.Initialize()
is present; make sure images are high‑resolution.A minimal Form that adds tasks to a ListBox
and persists them to disk:
public partial class TodoForm : Form
{
List tasks = new();
const string SavePath = "tasks.txt";
public TodoForm()
{
InitializeComponent();
if(File.Exists(SavePath))
listBox1.Items.AddRange(File.ReadAllLines(SavePath));
btnAdd.Click += (_, _) => {
tasks.Add(txtTask.Text);
listBox1.Items.Add(txtTask.Text);
txtTask.Clear();
};
FormClosing += (_, _) => File.WriteAllLines(SavePath, tasks);
}
}
Concepts Used: Control events, collections, file I/O, FormClosing
event.
dotnet publish -r win-x64 -c Release ^
-p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true
Use ClickOnce
or MSIX
for auto‑updates.
WinForms is Windows‑only. For cross‑platform consider:
Need advanced controls? Explore the curated list of OSS WinForms libraries.
dotnet/winforms
– source, roadmap, issues.