WinForms cheat sheet
A scannable WinForms reference: 9 short snippets across 7 topics, each linking back to the lesson it came from.
At a glance
| Topic | What it covers | |
|---|---|---|
| Dialogs, multiple forms and MDI | Show() returns immediately and the form is modeless. ShowDialog() blocks until the form closes and returns a | lesson |
| DataGridView in depth | Chaining BindingSource objects gives you master-detail for free: the child list is re-evaluated from the current master | lesson |
| Async work, threading and progress | Before .NET Core, touching a control from another thread threw InvalidOperationException. Modern WinForms still raises | lesson |
| Files, settings and persistence | Write settings atomically: serialise to a temporary file in the same directory, then rename over the target. A crash | lesson |
| High DPI, accessibility and theming | Check three things on any form: every control reachable with Tab only, no control that steals focus back, and every | lesson |
| Deployment: ClickOnce, MSIX and single-file | ClickOnce's real advantage is that the update path is part of the manifest, not a separate updater you maintain. Its | lesson |
| Migrating from WinForms to WPF, WinUI or Blazor Hybrid | A WinForms application hides its business rules in event handlers. The first job is to find out how much of the code is | lesson |
Quick snippets
Dialogs, multiple forms and MDI
Modal and modeless forms
using (var dlg = new CustomerEditForm(customerId))
{
dlg.StartPosition = FormStartPosition.CenterParent;
if (dlg.ShowDialog(this) == DialogResult.OK)
ReloadCustomer(dlg.SavedCustomer);
}
MDI parents and children
// parent
IsMdiContainer = true;
var child = new DocumentForm { MdiParent = this, Text = "Report 1" };
child.Show();
// arrange what is open
LayoutMdi(MdiLayout.TileHorizontal);
// track the active document
ActiveMdiChildChanged += (s, e) =>
_statusLabel.Text = ActiveMdiChild?.Text ?? "No document";Full lesson: Dialogs, multiple forms and MDI →
DataGridView in depth
Master-detail without extra work
var customers = new BindingSource { DataSource = customerList };
var orders = new BindingSource { DataSource = customers, DataMember = "Orders" };
customerGrid.DataSource = customers;
orderGrid.DataSource = orders; // re-queries whenever the master selection moves
// and a live count in the header
customers.CurrentChanged += (s, e) =>
orderGrid.Columns["Total"].HeaderText =
"Total (" + orderGrid.Rows.Count + ")";Full lesson: DataGridView in depth →
Async work, threading and progress
Cross-thread access and marshalling
// called from any thread
void AppendLog(string line)
{
if (logBox.InvokeRequired)
{
logBox.BeginInvoke(new Action<string>(AppendLog), line); // async, no deadlock
return;
}
logBox.AppendText(line + Environment.NewLine);
}Full lesson: Async work, threading and progress →
Files, settings and persistence
Recent files and safe handling
void RememberFile(string path)
{
var recent = _settings.RecentFiles;
recent.RemoveAll(p => string.Equals(p, path, StringComparison.OrdinalIgnoreCase));
recent.Insert(0, path);
if (recent.Count > _settings.RecentCount)
recent.RemoveRange(_settings.RecentCount, recent.Count - _settings.RecentCount);
AppSettings.Save(_settings);
RebuildRecentMenu();
}Full lesson: Files, settings and persistence →
High DPI, accessibility and theming
Per-monitor DPI
<!-- App.manifest: opt in to per-monitor v2, not just system aware -->
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
</windowsSettings>
</application>Full lesson: High DPI, accessibility and theming →
Deployment: ClickOnce, MSIX and single-file
The channels
# framework-dependent: small, needs the runtime installed
dotnet publish -c Release -r win-x64 --self-contained false -o out
# self-contained single file: no runtime needed, larger output
dotnet publish -c Release -r win-x64 --self-contained true \
-p:PublishSingleFile=true \
-p:IncludeNativeLibrariesForSelfExtract=true \
-p:EnableCompressionInSingleFile=true \
-o out
# trimmed is not supported for WinForms - do not add PublishTrimmed
ClickOnce
<!-- in the .csproj -->
<PropertyGroup>
<PublishUrl>\\fileserver\apps\OrderTool\</PublishUrl>
<InstallUrl>https://apps.example.com/ordertool/</InstallUrl>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<ApplicationRevision>3</ApplicationRevision>
<ApplicationVersion>1.4.0.*</ApplicationVersion>
<SignManifests>true</SignManifests>
</PropertyGroup>Full lesson: Deployment: ClickOnce, MSIX and single-file →
Migrating from WinForms to WPF, WinUI or Blazor Hybrid
Migrating incrementally
// host a WinForms control inside a WPF window while you migrate screen by screen
public partial class LegacyHost : System.Windows.Forms.Integration.WindowsFormsHost
{
public LegacyHost()
{
Child = new OrderGridControl(); // the untouched WinForms control
}
}
// and the reverse: a WPF control inside a WinForms form, via element host
var host = new ElementHost { Dock = DockStyle.Fill, Child = new WpfSummaryView() };
panel.Controls.Add(host);Full lesson: Migrating from WinForms to WPF, WinUI or Blazor Hybrid →
FAQ
Is this WinForms cheat sheet free to use?
Yes. No sign-up and no tracking: the page is static, every example is on the page itself, and you can print it or save it as a one-page reference.
Where do the examples come from?
Every snippet is taken from the 7 lessons of the WinForms course on this site, and each section links back to the lesson it was pulled from.
How do I go deeper than a cheat sheet?
Open the full WinForms course — it carries the worked explanations, the edge cases and the exercises behind every line here.
Related cheat sheets
Last refreshed 2026-09-27.