Windows Forms: The Code Behind the Form
Lab 1, Your First Desktop Form, built a form by dragging controls and writing a few event handlers. This section looks behind the design surface: the code the designer writes, why the designer sometimes refuses to open and how to fix it without starting over, why a form is just a class, and the habits that keep a Windows Forms project clean from the first minute. Every program here was built and run on Windows, and every screenshot, message and number below comes from those runs.
Objectives
By the end of the section you should be able to:
- Explain what rapid application development is, and when Windows Forms is a good or a poor choice.
- Read the code the designer writes in
InitializeComponent, and fix a designer that no longer opens. - Remove an event handler the right way, through the Events list in the Properties window.
- Explain that a form is a class that inherits from
Form, and change control properties while the program runs. - Choose the first form in
Program.cs, open a second form, and pass it data through its constructor. - Add a TabControl, a MenuStrip with keyboard shortcuts, and a StatusStrip.
- Tell which events a mouse click, a key press and a line of code raise.
- Start a project the right way: rename the form, set its Text and Font first, name every control before you use it, check the tab order, and keep the logic out of the form.
Part 1: why Windows Forms
Rapid application development
Windows Forms (WinForms) is one of the original .NET project types. It came with the first .NET in 2001 and 2002, and it is a rapid application development environment: you drag controls from the Toolbox, set a few properties, double-click to add a handler, and you already have a working desktop program.
That speed is its strength and its trap. It makes a proof of concept very cheap: a few hours, or a couple of days, are enough to see whether an idea works. But a program built only by dragging and double-clicking, without understanding the code behind it, becomes hard to change later. The rest of this section is about that code.
Windows Forms and the other desktop project types
| Project type | How you design the window | Status |
|---|---|---|
| Windows Forms | Drag and drop on a design surface | Since the first .NET; still very common in companies |
| WPF | XAML, a markup language for the interface | The usual choice for a new desktop application |
| UWP | XAML | Tied to the newest Windows versions; deprecated |
| .NET MAUI | XAML | Desktop and mobile from one project; young |
WPF, UWP and .NET MAUI all describe the window in XAML, which is more powerful but more complex than dragging controls onto a form.
When to use it
- A quick prototype to test an idea.
- A small utility for yourself or your team: a little tool that does one job.
- A simple application that one person must build and maintain.
- Existing business applications: many companies built their desktop tools in Windows Forms and still extend them, so you are likely to meet it at work.
For a large new desktop application, WPF is usually the better choice, and the next example shows one reason.
A warning about prototypes: a working prototype looks finished. If you show a customer a form that already clicks and responds, they may ask for it tomorrow, when it is only a demo. Be careful who sees a prototype and what you promise.
A form does not rearrange itself
Predict: the form below was designed at 420 by 235 pixels. What happens to its controls when the window is made smaller, or maximized?

We ran the same form twice, once with its Size set to 300 by 200 and once maximized:


The controls stay where they were placed. A smaller window cuts them off, with no scroll bars; a bigger one leaves empty space. A Windows Form is "what you see is what you get". Making it adapt to the window size takes extra work, and this is one reason WPF is usually preferred for a new application.
Part 2: a quick demo, and the code behind it
Build it in two minutes
This is how fast a rapid prototype is. Start a new Windows Forms App project and, without renaming anything:
- Drag a Label and a TextBox onto the form. Set the label's Text to
First Name. - Select both (click one, then Ctrl+click the other), press Ctrl+C and Ctrl+V twice: you get two more label and text box pairs. Alignment lines (snaplines) appear while you drag and help you line them up.
- Drag a Button and a CheckBox.
- Drag a ProgressBar and set its Value property to
45. The bar shows 45 out of 100.
Run it: you can type, press Tab to move between fields, and tick the check box. It does nothing yet, but it looks like a real program.
Wire the button
Double-click the button. Visual Studio creates the handler button1_Click and opens it. Add one line:
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show($"Hello {textBox1.Text} {textBox2.Text}");
}textBox1is the first text box; its default name is shown at the top of the Properties window.- A text box is an object, and
Textis a property of that object: the text the user typed. $"..."is string interpolation: each expression in braces is replaced by its value.
Predict: the user types Lab in the first box and Student in the second, then clicks the button. What does the message box say?

It says Hello Lab Student. The labels still read label2 and label3, and the button button1: this is a quick demo, not a finished program.
Where that code lives
Right-click Form1.cs and choose View Code: the class is declared public partial class Form1 : Form. The word partial means the class is written in two files, and the compiler joins them into one class. The other half is Form1.Designer.cs, under Form1.cs in the Solution Explorer. Its method InitializeComponent holds everything the designer did for you. This is part of ours:
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.textBox1 = new System.Windows.Forms.TextBox();
// ... one line like these for every control ...
this.button1 = new System.Windows.Forms.Button();
this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(120, 140);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(100, 30);
this.button1.TabIndex = 6;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
// ...
this.progressBar1.Value = 45;
// ...
this.Controls.Add(this.button1);
this.Controls.Add(this.progressBar1);
// ...
}Read it with Lab 1 in mind:
new System.Windows.Forms.Button()creates an object of the classButton. Every control on the form is an object stored in a field of Form1.- Each
this.button1.Something = ...line is one value from the Properties window. this.button1.Click += new System.EventHandler(this.button1_Click);wires the button's Click event to the methodbutton1_Clickin the other half of the class, Form1.cs.this.Controls.Add(...)puts the control on the form.
The file's header says "do not modify the contents of this method with the code editor". That is right 99 times out of 100. The next example is the exception.
The designer is just code being rendered
The design surface is not a picture that Visual Studio stores somewhere. To show the form, the designer reads and runs this code. If the code is broken, there is nothing to show, and the designer stops with an error instead of the form.
Worked example: the designer breaks
A classic accident:
- You double-click an empty part of the form by mistake. Visual Studio creates the handler for the form's default event, Load: a method
Form1_Loadin Form1.cs, and one wiring line in Form1.Designer.cs. - You did not want it, so you delete the method
Form1_Loadfrom Form1.cs.
Predict: the wiring line is still in the designer file. What happens next?
The program no longer builds. This is the compiler's message from our build (file path shortened):
Form1.Designer.cs(129,55): error CS1061: 'Form1' does not contain a definition for 'Form1_Load' and no accessible extension method 'Form1_Load' accepting a first argument of type 'Form1' could be found (are you missing a using directive or an assembly reference?)And the designer refuses to open the form. In the lab video it showed:
The designer cannot process unknown name 'Form1_Load' at line 153. The code within the method 'InitializeComponent' is generated by the designer and should not be manually modified. Please remove any changes and try opening the designer again.Many beginners restart Visual Studio, then delete the project and start over. There is no need: read the message. It names the problem (Form1_Load) and the line. Line 129 of our Form1.Designer.cs is:
this.Load += new System.EventHandler(this.Form1_Load);The handler was deleted in one place (the method in Form1.cs) but not in the other (the wiring line in Form1.Designer.cs), so this line refers to a method that no longer exists.
The fix: click Go to code on the designer's error page (or open Form1.Designer.cs), delete that one line, save, and open the designer again. The form is back. This is the one time you edit the generated code by hand: you remove the line that the designer itself cannot fix.
Remove a handler the right way
Do not delete a handler by deleting its method. Remove it where it was created:
- Open the form in the designer and select the control (or the form itself).
- In the Properties window, click the lightning bolt button: the Events list of that control.
- Find the event (here Load), select the handler name next to it, and delete it.
- The wiring line is gone. Visual Studio usually removes the empty method as well; if it is still in Form1.cs, delete it: nothing refers to it any more.
The Events list is also where you see every event a control has, not just its default one: a button has Click, MouseClick, MouseHover, DragDrop and many more. Double-clicking a control only creates the handler of its default event (Click for a button, Load for a form).
Interactive: break and fix the designer
The widget below shows a form in the designer next to its two code files. Double-click the form, delete the method the wrong way, build, and watch the designer break; then fix it from the error, or remove the handler through the Events list. It also shows why you should name a control before you double-click it. Play runs the whole story one step at a time.
Part 3: a form is a class
Inheritance: what Form gives you
Form1 : Form means Form1 inherits from the class Form. Put the cursor on Form and press F12 to see its definition; do the same on its base class, and again. We printed the whole chain from a running program:
WinFormsDemo.Form1
System.Windows.Forms.Form
System.Windows.Forms.ContainerControl
System.Windows.Forms.ScrollableControl
System.Windows.Forms.Control
System.ComponentModel.Component
System.MarshalByRefObject
System.ObjectEvery level adds something. That is why an empty form already has a title bar, an icon, the minimize, maximize and close buttons, a background, the ability to be resized, and the ability to draw the controls you add. Nobody writes that code again for each program: you inherit it. Building a window from scratch without this base class means writing all of it yourself: the title bar, the close button, the background, the right-click menu.
Properties in the designer and at run time
Everything in the Properties window is an ordinary C# property of an object. The value you set in the designer is only the starting value: your code can read it and change it while the program runs.
In the form's constructor, after InitializeComponent(), we changed two labels:
public Form1()
{
InitializeComponent();
label2.Text = "Last Name";
label3.Text = "Full Name";
}and the button now fills the third text box and moves the progress bar:
private void button1_Click(object sender, EventArgs e)
{
textBox3.Text = $"{textBox1.Text} {textBox2.Text}";
progressBar1.Value += 10;
}
The designer still shows label2 and label3: the new texts exist only while the program runs. For a fixed text, setting it in the designer is simpler; code is for values that change.
Predict: the user types Lab and Student and clicks the button once. What is in the third box, and where is the bar?

textBox3.Text is now Lab Student, and progressBar1.Value went from 45 to 55.
Worked example: a property with rules
A ProgressBar has three related properties. We read their defaults on Windows: Minimum is 0, Maximum is 100, and Value must stay between them.
Predict: the bar starts at 45 and each click adds 10. What happens on the sixth click?
| Click | Value before | Value after |
|---|---|---|
| 1 | 45 | 55 |
| 2 | 55 | 65 |
| 3 | 65 | 75 |
| 4 | 75 | 85 |
| 5 | 85 | 95 |
| 6 | 95 | 105, not allowed |
The sixth click tries to set 105, which is above Maximum. The program run outside Visual Studio shows this:

Value of '105' is not valid for 'Value'. 'Value' should be between 'minimum' and 'maximum'. (Parameter 'value')
Actual value was 105.A property can refuse a value. Code that changes a property at run time must respect that property's rules.
A form is an object: many windows
If Form1 is a class, you can create as many objects of it as you like. Show is a method that Form1 inherits from Form. We changed the button's handler to:
private void button1_Click(object sender, EventArgs e)
{
Form1 frm = new Form1();
frm.Show();
frm.Left = this.Left + 430;
}The third line only moves the new window to the right of the first one, so that both are visible. Left is another inherited property.
Predict: the user types Lab and Student in the first window, then clicks button1 twice. What do the new windows show?

Two new windows open, and their boxes are empty. Each window is a separate object of the class Form1, with its own text boxes: typing in one does not change the others.
Part 4: more than one form
Program.cs chooses the first form
When you press Start, how does the program know which form to open? Program.cs:
namespace WinFormsDemo
{
internal static class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
Application.Run(new MainForm());
}
}
}Application.Run(new MainForm()) creates the first form. Put another form's class there, and that form opens first instead. The first form is the main form: the application lives as long as it is open.
Pass data through the constructor
Add a second form with Add, Form in the Solution Explorer: Form2, with one large label, messageLabel. A form's constructor is an ordinary constructor, so it can take parameters:
public partial class Form2 : Form
{
private string formMessage;
public Form2(string message)
{
InitializeComponent();
formMessage = message;
messageLabel.Text = formMessage;
}
}Once the constructor needs a message, new Form2() no longer compiles: the caller must pass a value. The main form's button does:
private void openForm2Button_Click(object sender, EventArgs e)
{
Form2 frm = new Form2("Hello from Form 1");
frm.Show();
}Predict: what appears when the user clicks Open Form 2?

The text travelled from one form to the other through the constructor. You can pass any value this way, even a reference to the first form, so that Form2 can call back to it.
Worked example: close the main form
Predict: Form2 is open. The user chooses File, Exit on the main form, which runs this.Close(). Does Form2 stay open?
No. In our run the whole program ended with exit code 0 right after the Exit click (the run lasted 5.3 seconds and stopped at that step), although Form2 was still open. The main form's lifetime is the application's lifetime: when it closes, the application closes, and every other form with it.
Part 5: containers, menus and the status strip
The Toolbox has more than the Common Controls. Two groups are worth knowing now.
Containers: GroupBox and TabControl
A GroupBox (Lab 1) groups related controls under a caption. A TabControl holds several tab pages: clicking a tab shows a different page, like a different form, but it is one form. Each page is a container: drag controls onto it. Our main form has a Main page with two buttons and an Options page with a check box:

StatusStrip: a strip of information at the bottom
From Menus & Toolbars, drag a StatusStrip onto the form: it docks to the bottom edge, like the status bar of Visual Studio. Use its drop-down to add a StatusLabel and a ProgressBar. The designer names them toolStripStatusLabel1 and toolStripProgressBar1; the Work button uses them:
private void workButton_Click(object sender, EventArgs e)
{
toolStripProgressBar1.Value += 5;
toolStripStatusLabel1.Text = "Working...";
}Predict: the status label starts as Ready, the bar at 0. What does the strip show after three clicks on Work?

Working..., and a bar at 15 out of 100. A real program would use the strip to report progress while it works, then say Ready again at the end.
MenuStrip and the & key
Drag a MenuStrip onto the form and type the menu items directly on it. Type an & before the letter that should open the item from the keyboard:
| You type | The menu shows | Keyboard |
|---|---|---|
&File | File, with F underlined | Alt+F opens it |
E&xit | Exit, with x underlined | X, inside the File menu |
&Help | Help, with H underlined | Alt+H opens it |
&About | About, with A underlined | A, inside the Help menu |
The underlines appear when the user presses Alt. Alt, F, X closes the program without touching the mouse: this is how Windows programs are made accessible to users who cannot use a mouse. Follow the standard names: a File menu with Exit on X, a Help menu with About. Two items in the same menu cannot share a letter; items in different menus can.
The designer stores the & in the Text property:
this.fileToolStripMenuItem.Text = "&File";
this.exitToolStripMenuItem.Text = "E&xit";
this.helpToolStripMenuItem.Text = "&Help";
this.aboutToolStripMenuItem.Text = "&About";A menu item is like a button: double-click it to get its Click handler. If you double-click one by mistake, remove the handler through the Events list (Part 2), not by deleting the method.
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show("Created in the OS1 lab");
}this is the current object, the main form, and Close is a method it inherits from Form.

The names fileToolStripMenuItem and toolStripProgressBar1 are the designer's defaults. They work, but they are long and say little. Part 6 is about naming.
Click is more than a mouse click
The Click event of a button means "the button was activated", not "the mouse was pressed". To see exactly what each action raises, we wired a logging handler to several events of a small form and drove it on Windows with the real keyboard and mouse. Each row is one action; the events are in the order they were raised:
| What the user or the code does | Events raised, in order |
|---|---|
Types one letter in firstNameText | KeyPress, TextChanged |
Presses Backspace in firstNameText | KeyPress, TextChanged |
Clicks in firstNameText with the mouse | Click, MouseClick |
Presses Space on the focused sayHelloButton | Click |
Presses Enter on the focused sayHelloButton | Click |
Clicks sayHelloButton with the mouse | MouseDown, Click, MouseClick, MouseUp |
Clicks agreeCheck with the mouse | CheckedChanged, Click, MouseClick |
Presses Space on the focused agreeCheck | CheckedChanged, Click |
Clicks an item of cityList with the mouse | Click, MouseClick, SelectedIndexChanged |
| Clicks the item that is already selected | Click, MouseClick, SelectedIndexChanged |
Presses the Down arrow in cityList | SelectedIndexChanged |
| Presses Alt+H, then A | Click of aboutToolStripMenuItem |
Code runs firstNameText.Text = "Code"; | TextChanged |
| Code sets the same text again | nothing |
Code runs sayHelloButton.PerformClick(); | Click |
We logged KeyPress, TextChanged, Click and MouseClick on the text box; Click, MouseClick, MouseDown and MouseUp on the button; CheckedChanged, Click and MouseClick on the check box; SelectedIndexChanged, Click and MouseClick on the list; and Click on the About menu item.
Three things to notice:
- Space or Enter on a focused button raises Click but not MouseClick. Put the button's work in Click and it runs for the mouse, a finger tap and the keyboard.
- Changing a value from code raises the same event as the user does: setting
Textraised TextChanged. Setting the same text again raised nothing. - Each control raises several events for one action; you handle only the ones you need, and the others do nothing.
Interactive: the events playground
Use the form in the widget with the mouse and the keyboard, and watch each event appear in the log, with the handler that runs for it highlighted in Form1.cs. Play runs the actions from the table above.
Part 6: starting a project the right way
The quick demo works, but it is full of label2 and button1. When you start a real project, do these things, in this order, before you write any code.
Step 1: rename Form1
In the Solution Explorer, select Form1.cs and press F2 (or right-click, Rename). Call it MainForm.cs. Visual Studio asks whether to rename all references to Form1 in the code as well: choose Yes. Now the class is MainForm, and Program.cs says Application.Run(new MainForm());.
Step 2: set the form's Text
The title bar still says Form1. The file and class names changed, but the title is a property, Text, with its own value. Set it to something meaningful, for example Main Form.
Step 3: set the Font first
Select the form and set its Font, for example Segoe UI 14 (the default is Segoe UI 9). Do it before adding controls, for two reasons.
Predict: our form is 360 by 150 pixels with a 100 by 30 button, designed with the default 9 point font. We change the form's font to 18 points. What happens to the sizes?


The measured sizes before and after:
| What | 9 point font | 18 point font |
|---|---|---|
| Form inside size (ClientSize) | 360 x 150 | 669 x 320 |
| Button | 100 x 30 at (120, 60) | 186 x 64 at (223, 128) |
| Text box | 180 x 23 | 331 x 39 |
| Label that uses the form's font | 9 pt | 18 pt |
| Label with its own 10 pt font | 10 pt | 10 pt |
The designer stores the size of the font the form was designed with, AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F): an average character of 7 by 15 pixels at 9 points. At 18 points the same measure is 13 by 32, so every width and position is multiplied by 13/7 and every height by 32/15:
360 x 13 / 7 = 668.6, shown as 669 150 x 32 / 15 = 320
100 x 13 / 7 = 185.7, shown as 186 30 x 32 / 15 = 64(The text box is 39 high, not 49, because a text box sets its own height from its font.)
So the first reason: changing the font of a finished form scales everything, almost doubling it, and you must then drag the form back to a normal size (or type a new Size in the Properties window). Set the font on an empty form and nothing needs fixing.
The second reason is in the last two rows. A control that has no font of its own uses the form's font: every control you drag onto the form after you set the font already has the right size. But a control whose font you changed keeps its own value, and later changes to the form's font no longer reach it. Changing defaults late leaves a form with mixed sizes.
Step 4: name every control, first
A control's Name is the name of its variable in the class. So name it the way you name a variable in C#: camelCase, words that say what it is.
| Old style (Hungarian notation) | This section |
|---|---|
lblFirstName | firstNameLabel |
txtFirstName | firstNameText |
btnSayHello | sayHelloButton |
lstCity | cityList |
Lab 1 used short prefixes such as txtName, a style that was common in the early .NET years and that Microsoft once recommended. Microsoft now recommends against it: code is read by people, and "the first name label" reads naturally, while "L B L first name" does not. Keep pairs consistent too: firstNameLabel goes with firstNameText, lastNameLabel with lastNameText.
Name the control before you double-click it. The handler's name is taken from the control's name at the moment you double-click:
- Double-click a button still called
button1: the handler isbutton1_Click. - Rename the button to
sayHelloButtonafterwards: the handler is stillbutton1_Click. Renaming the control does not rename the method. - Months later, Form1.cs holds
button1_Click,button2_Click,button3_Click, and nobody knows which is which.
To check that nothing is left unnamed, open the drop-down at the top of the Properties window: it lists every object on the form. Any label1 or button2 in that list still needs a name.
Two designer shortcuts help here. Drag a rectangle around several controls (or Ctrl+click them) to move them together, or to set a property they all share: with two controls selected, type in Name straight away. When you place a text box next to a label, drag it until the snapline runs through the text of both: then the text in the box lines up with the text of the label.
Step 5: check the tab order
A user who cannot use a mouse, or who simply types fast, moves through the form with the Tab key. The order follows the TabIndex property of each control, from low to high, skipping controls that cannot take the focus. We read the defaults on Windows: TabStop is True for a TextBox and a Button and False for a Label, so labels are skipped.
The designer gives each new control the next TabIndex, so the tab order is the order in which you added the controls. In our form the button was added before the last name:
| Control | TabIndex |
|---|---|
| firstNameLabel | 0 |
| firstNameText | 1 |
| sayHelloButton | 2 |
| lastNameLabel | 3 |
| lastNameText | 4 |
Predict: the cursor is in the first name. Where does each Tab go?
We pressed Tab on Windows and recorded the focus: firstNameText, then sayHelloButton, then lastNameText, then back to firstNameText. The button comes before the last name, and after the last control the focus wraps to the first. Set the TabIndex of lastNameText to 2 and of sayHelloButton to 3, and the order becomes first name, last name, button.
The numbers do not have to be consecutive. Only their order matters: Windows moves to the next control, in TabIndex order, that can take the focus. With firstNameText at 10, lastNameText at 45 and sayHelloButton at 50, the run gave first name, last name, button, exactly as with 1, 2, 3. Leaving gaps (10, 50) lets you slot a new control in between later (45) without renumbering.
A pitfall: the object instead of its Text
With good names, the greeting becomes easy to read and write. Predict what this version shows when the user types Lab and Student:
private void sayHelloButton_Click(object sender, EventArgs e)
{
MessageBox.Show($"Hello {firstNameText} {lastNameText}");
}
Hello System.Windows.Forms.TextBox, Text: Lab System.Windows.Forms.TextBox, Text: StudentIt builds and runs, but firstNameText is the whole TextBox object, and turning an object into a string gives its class name plus a description. You wanted one property of that object: firstNameText.Text.
Step 6: keep the logic out of the form
It is tempting to put all the code in MainForm.cs, even the code that reads a database. Projects that do this end up with thousands of lines behind one form, and when the time comes to move to WPF, or to a newer .NET, everything must be rewritten because the logic is tangled with the window.
Instead, add a Class Library project to the solution and put every piece of logic there. The form keeps only the code that talks to the user: read the text boxes, call the library, show the result. Even our greeting belongs in the library:
namespace GreetingLibrary;
public static class Greeter
{
public static string Greet(string firstName, string lastName)
{
return $"Hello {firstName} {lastName}";
}
}The library knows nothing about forms, so any interface can use it. A console program that references the same library:
using GreetingLibrary;
Console.WriteLine(Greeter.Greet("Lab", "Student"));
Console.WriteLine(Greeter.Greet("Operating", "Systems"));prints:
Hello Lab Student
Hello Operating Systemsand the form's handler shrinks to one call:
using GreetingLibrary;
namespace BetterWinFormsDemo;
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void sayHelloButton_Click(object sender, EventArgs e)
{
string message = Greeter.Greet(firstNameText.Text, lastNameText.Text);
MessageBox.Show(message);
}
}The first lines show one more small tweak: namespace BetterWinFormsDemo; with a semicolon is a file-scoped namespace. It works in Windows Forms too, and saves one level of indentation.
The better project
Everything from this part together: a form renamed to MainForm with the title Main Form, a 14 point font set before any control was added, camelCase names, a checked tab order, and the logic in GreetingLibrary.

The designer file of this form sets the font once, on the form:
this.AutoScaleDimensions = new System.Drawing.SizeF(11F, 25F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(500, 200);
this.Font = new System.Drawing.Font("Segoe UI", 14F);
this.Name = "MainForm";
this.Text = "Main Form";11F, 25F is the character size Windows measured for Segoe UI 14 on our run. When the stored value matches the measured one, the form keeps the size it was designed with: this one ran at its designed 500 by 200. (Our first attempt stored a guessed 10 by 25, and Windows widened the form to 550, by 11/10.)
Quick check
- Which file do you change to make a different form open first?
- You double-clicked a button called
button1, then renamed itsaveButton. What is the handler called? - The user presses Space on a focused button. Which runs: its Click handler, its MouseClick handler, or both?
- An old project calls a text box
txtEmail. What does this section's style call it? - You deleted
button2_Clickfrom Form1.cs, and now the designer will not open. Which line do you remove, and from which file?
Practice
About 30 minutes. Answer on paper first, then build and run to check. The answers are at the end.
Practice 1: predict the tab order (about 6 minutes)
A registration form has these controls (the labels are left out):
| Control | TabIndex | TabStop |
|---|---|---|
| nameText | 3 | True |
| emailText | 7 | True |
| phoneText | 5 | True |
| newsCheck | 1 | False |
| saveButton | 12 | True |
On the form they appear top to bottom as Name, Email, Phone, the news check box, and Save.
- Which control has the cursor when the form opens?
- Write the focus after each of four presses of Tab.
- Why is
newsChecknever reached with Tab, although its TabIndex is the lowest?
Practice 2: the designer will not open (about 6 minutes)
A student double-clicked checkBox1, then deleted the new method from Form1.cs. Form1.Designer.cs still contains:
this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged);- The project no longer builds. Which error code does the compiler report, and what does the message say is missing?
- Give two ways to get a working project back.
- How should the student have removed the handler?
Practice 3: a bar with a smaller maximum (about 6 minutes)
A form has a ProgressBar with Maximum = 50 and Value = 0, and a button:
private void button1_Click(object sender, EventArgs e)
{
progressBar1.Value += 15;
}- What is the Value after three clicks?
- What happens on the fourth click? Write the number that the error message will name.
Practice 4: a welcome form (about 12 minutes)
Build a project with two forms, prepared the right way (named controls, a meaningful title).
- MainForm, titled
Main Form: a labelnameLabelwith the TextName, a text boxnameText, and a buttonwelcomeButtonwith the TextWelcome. - WelcomeForm, titled
Welcome: one labelwelcomeLabelwith a large font. Its constructor takes the user name as a string and showsWelcome,followed by the name and!.
Clicking Welcome opens a WelcomeForm with the name from nameText.
- Write both constructors or handlers you need.
- What does the welcome form show when the user types
Student? - What happens if MainForm tries
new WelcomeForm(), without an argument?
Answers
Quick check
Program.cs: change the form inApplication.Run(new MainForm());.- Still
button1_Click. The handler name comes from the name the control had when you double-clicked it; renaming the control does not rename the method. That is why you name controls first. - Only the Click handler. MouseClick is raised only by the mouse; Space and Enter raise Click.
emailText: a camelCase name that says what it is, with no type prefix.- The wiring line
this.button2.Click += new System.EventHandler(this.button2_Click);in Form1.Designer.cs.
Practice 1
Our run of this form on Windows, pressing Tab four times:
nameTexthas the cursor when the form opens: it is the lowest TabIndex (3) among the controls that take the focus with Tab.phoneText(5),emailText(7),saveButton(12), then back tonameText(3). The order follows TabIndex, not the position on the screen: Phone comes before Email although it is lower on the form.newsCheckhasTabStopset to False, so Tab skips it. The user can still click it with the mouse.
Practice 2
- Our build of exactly this situation reported:
Form1.Designer.cs(104,75): error CS1061: 'Form1' does not contain a definition for 'checkBox1_CheckedChanged' and no accessible extension method 'checkBox1_CheckedChanged' accepting a first argument of type 'Form1' could be found (are you missing a using directive or an assembly reference?)The method checkBox1_CheckedChanged is missing, and line 104 of Form1.Designer.cs is the wiring line that still refers to it.
- Either delete that wiring line from Form1.Designer.cs (the handler was not wanted), or add the method back to Form1.cs (the handler was wanted):
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
}- Through the Events list: select
checkBox1in the designer, click the lightning bolt in the Properties window, and delete the handler next to CheckedChanged. That removes the wiring line together with the method.
Practice 3
- 0, 15, 30, 45.
- The fourth click tries to set 60, above the Maximum of 50. Our run showed the unhandled exception window:

Value of '60' is not valid for 'Value'. 'Value' should be between 'minimum' and 'maximum'. (Parameter 'value')
Actual value was 60.Practice 4
WelcomeForm.cs:
namespace WinFormsDemo
{
public partial class WelcomeForm : Form
{
public WelcomeForm(string userName)
{
InitializeComponent();
welcomeLabel.Text = $"Welcome, {userName}!";
}
}
}The handler in MainForm.cs:
private void welcomeButton_Click(object sender, EventArgs e)
{
WelcomeForm frm = new WelcomeForm(nameText.Text);
frm.Show();
}- The code above, built and run on Windows.
Welcome, Student!:

- It does not compile. The only constructor needs a string, and our build said:
MainForm.cs(12,35): error CS7036: There is no argument given that corresponds to the required parameter 'userName' of 'WelcomeForm.WelcomeForm(string)'