Forms and controls
How a WinForms application starts, the controls you will reach for first, and the designer files that are generated for you.
How the application starts
A WinForms app is a Windows message loop. Application.Run starts that loop and returns when the main form closes. The [STAThread] attribute on Main is required: Windows common dialogs and the clipboard need a single-threaded apartment.
// Program.cs
using System;
using System.Windows.Forms;
internal static class Program
{
[STAThread]
private static void Main()
{
ApplicationConfiguration.Initialize(); // sets DPI mode and visual styles
Application.Run(new MainForm());
}
}
// MainForm.cs - code only, no designer
using System.Windows.Forms;
public sealed class MainForm : Form
{
public MainForm()
{
Text = "Inventory";
Width = 900;
Height = 600;
StartPosition = FormStartPosition.CenterScreen;
var label = new Label { Text = "Search", AutoSize = true, Location = new System.Drawing.Point(12, 15) };
var box = new TextBox { Location = new System.Drawing.Point(70, 12), Width = 240 };
var grid = new DataGridView { Dock = DockStyle.Bottom, Height = 420 };
AcceptButton = new Button { Text = "Go", DialogResult = DialogResult.OK };
Controls.Add(label);
Controls.Add(box);
Controls.Add(grid);
}
}ApplicationConfiguration.Initialize()replaces the olderApplication.EnableVisualStyles()plusSetCompatibleTextRenderingDefaultpair and also sets the high-DPI mode from the project file.Controls.Addorder matters forDocklayout — the last control added docks first, so add the fill target last if you are not using a designer.- Set
AcceptButtonandCancelButtonso Enter and Escape close a dialog as users expect.
The control map
| Control | Use it for | Watch out for |
|---|---|---|
Label | Static text | Set AutoSize deliberately; the default can clip long text |
TextBox | Single or multi-line input | Multiline plus AcceptsReturn for free text |
Button | Commands | One handler can serve many buttons; check the sender |
ComboBox | Choose one from a list | Set DropDownStyle; SelectedIndexChanged fires during load |
ListBox / CheckedListBox | Simple selection lists | Use BeginUpdate/EndUpdate when adding many items |
DataGridView | Tabular data with editing | Bound mode or virtual mode; never both at once |
TreeView | Hierarchies | Lazy-populate on BeforeExpand rather than building everything |
MenuStrip / ToolStrip | Menus and toolbars | Merge order matters when hosting MDI or plugin children |
PropertyGrid | Editing an object's properties | Great for internal tools; not a user-facing pattern |
FolderBrowserDialog / OpenFileDialog | File and folder picking | Use the modern OpenFolderDialog on .NET 8+ |
// Adding many rows without flicker: suspend layout and painting
listBox1.BeginUpdate();
try
{
foreach (var item in LoadItems())
listBox1.Items.Add(item);
}
finally
{
listBox1.EndUpdate();
}
// A modal dialog returns a result instead of raising events
using var dialog = new SettingsForm();
if (dialog.ShowDialog(this) == DialogResult.OK)
ApplySettings(dialog.Settings);💡
ShowDialog returns a DialogResult and blocks the caller; Show returns immediately and makes the form modeless. A settings dialog should be modal, a log window should not.The generated designer file
The Visual Studio designer splits a form into MainForm.cs (your handler code) and MainForm.Designer.cs (control declarations and layout). The generated file is rewritten on every designer save, so any edit you make there is lost.
// MainForm.Designer.cs - generated, do not hand-edit
partial class MainForm
{
private System.ComponentModel.IContainer components = null;
private System.Windows.Forms.Button saveButton;
private System.Windows.Forms.DataGridView grid;
protected override void Dispose(bool disposing)
{
if (disposing && components != null) components.Dispose();
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.saveButton = new System.Windows.Forms.Button();
this.grid = new System.Windows.Forms.DataGridView();
this.SuspendLayout();
this.saveButton.Text = "Save";
this.saveButton.Click += new System.EventHandler(this.SaveButton_Click);
this.grid.Dock = System.Windows.Forms.DockStyle.Fill;
this.Controls.Add(this.grid);
this.Controls.Add(this.saveButton);
this.ResumeLayout(false);
}
}- Put custom logic in the hand-written partial class, never in
InitializeComponent. - The designer does not understand loops or conditionals in layout; if you need dynamic controls, build them in code at runtime.
- Review designer diffs in a pull request. A stray drag can silently re-parent a control or change a
Dockvalue. - Deleting a control in the designer leaves its event handler method behind as dead code — clean it up.
FAQ
Can WinForms run on macOS or Linux?
No. WinForms is a Windows-only UI stack. For cross-platform desktop use MAUI, Avalonia or Uno; for a browser-first app, use a web UI. The business logic can still be shared through a .NET library.
Why do my controls look blurry on a high-DPI monitor?
The app is not per-monitor DPI aware. Set the application high-DPI mode in the project file and let
ApplicationConfiguration.Initialize apply it, then verify at 150 percent scaling rather than assuming.Related
Events and layout Data binding and when not to use WinForms
Last refreshed 2026-09-18.