WinForms

Windows Forms and desktop projects

This page focuses on file placement, control names, and button-click logic so you can build forms with less guessing.

Main idea

Create the controls in the Designer, then paste the logic into Form1.cs or the file named in the section.

Files

Where the WinForms code goes

The main goal on this page is simple: you should know where each part belongs.

For almost all tasks here, Program.cs stays standard, the controls are created in the Designer, and the logic goes into Form1.cs.

Program.cs

Keep the default startup file unless you change the first form name.

Designer

Create controls in the Designer and set the Name values exactly.

Form code

Paste the logic into Form1.cs or the file named in the section.

FILE: Program.cs
Program.cs
using System;
using System.Windows.Forms;

namespace StudentForms;

internal static class Program
{
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();
        Application.Run(new Form1());
    }
}
WinForms

Interval checker form

This is the form version of the classic 1 to 100 task. The user types one value and clicks a button.

Track: C# FormsPaste logic into the file shown below

Build steps

  1. Create a new Windows Forms App project.
  2. Put the three controls on Form1 and set their Name values exactly.
  3. Double-click the button or attach the click event in code.
  4. Paste the logic into Form1.cs.
  5. Run the form and test values inside and outside the interval.

Controls to add

  • TextBox txtValue
  • Button btnCheck
  • Label lblResult

Quick test

  • Type 50. The label should say the value is inside the interval.
  • Type 150 or text. The label should show a clear error or outside message.
FILE: Form1.cs
Form1.cs
using System;
using System.Windows.Forms;

namespace StudentForms;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        btnCheck.Click += btnCheck_Click;
    }

    private void btnCheck_Click(object? sender, EventArgs e)
    {
        if (!int.TryParse(txtValue.Text, out int value))
        {
            lblResult.Text = "Please type a whole number.";
            return;
        }

        lblResult.Text = value >= 1 && value <= 100
            ? "The value is inside 1 to 100."
            : "The value is outside 1 to 100.";
    }
}
WinForms

Mouse cursor tracker with pixel and millimeter coordinates

Use a panel as the drawing area. When the mouse moves, update two labels.

Track: C# FormsPaste logic into the file shown below

Build steps

  1. Add one large panel to the form. This is the area you will track.
  2. Add two labels under the panel.
  3. Paste the code into Form1.cs.
  4. Move the mouse inside the panel and watch both labels update.

Controls to add

  • Panel pnlTrack
  • Label lblPixels
  • Label lblMillimeters

Quick test

  • Move to the top-left corner. The pixel coordinates should be close to 0, 0.
  • Move across the panel and check that the millimeter values also change.
FILE: Form1.cs
Form1.cs
using System;
using System.Windows.Forms;

namespace StudentForms;

public partial class Form1 : Form
{
    private const double MmPerPixel = 0.264583;

    public Form1()
    {
        InitializeComponent();
        pnlTrack.MouseMove += pnlTrack_MouseMove;
    }

    private void pnlTrack_MouseMove(object? sender, MouseEventArgs e)
    {
        lblPixels.Text = $"Pixels: X = {e.X}, Y = {e.Y}";
        lblMillimeters.Text =
            $"Millimeters: X = {e.X * MmPerPixel:0.00}, Y = {e.Y * MmPerPixel:0.00}";
    }
}
WinForms

Character classifier: digit, vowel, consonant or special symbol

The user types one character. The form decides what kind of character it is.

Track: C# FormsPaste logic into the file shown below

Build steps

  1. Add one small text box and set MaxLength to 1.
  2. Add a button and one result label.
  3. Paste the event code into Form1.cs.
  4. Run and test digits, vowels, consonants, and symbols.

Controls to add

  • TextBox txtCharacter
  • Button btnClassify
  • Label lblResult

Quick test

  • Type A. The result should be vowel.
  • Type 7. The result should be digit.
  • Type #. The result should be special symbol.
FILE: Form1.cs
Form1.cs
using System;
using System.Windows.Forms;

namespace StudentForms;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        btnClassify.Click += btnClassify_Click;
    }

    private void btnClassify_Click(object? sender, EventArgs e)
    {
        if (string.IsNullOrWhiteSpace(txtCharacter.Text))
        {
            lblResult.Text = "Type one character first.";
            return;
        }

        char ch = txtCharacter.Text[0];
        char lower = char.ToLower(ch);

        if (char.IsDigit(ch))
        {
            lblResult.Text = "Digit";
        }
        else if ("aeiouy".Contains(lower))
        {
            lblResult.Text = "Vowel";
        }
        else if (char.IsLetter(ch))
        {
            lblResult.Text = "Consonant";
        }
        else
        {
            lblResult.Text = "Special symbol";
        }
    }
}
WinForms

Quadratic equation solver form

This task is easier to use as a form because the three inputs stay visible all the time.

Track: C# FormsPaste logic into the file shown below

Build steps

  1. Add three text boxes for a, b, and c.
  2. Add one button and one larger label for the result.
  3. Paste the code into Form1.cs.
  4. Test one case with two roots, one with one root, and one with no real roots.

Controls to add

  • TextBox txtA
  • TextBox txtB
  • TextBox txtC
  • Button btnSolve
  • Label lblResult

Quick test

  • For 1, -3, 2 the roots should be 1 and 2.
  • For 1, 2, 1 there should be one root: -1.
  • For 1, 0, 1 there should be no real roots.
FILE: Form1.cs
Form1.cs
using System;
using System.Windows.Forms;

namespace StudentForms;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        btnSolve.Click += btnSolve_Click;
    }

    private void btnSolve_Click(object? sender, EventArgs e)
    {
        if (!double.TryParse(txtA.Text, out double a) ||
            !double.TryParse(txtB.Text, out double b) ||
            !double.TryParse(txtC.Text, out double c))
        {
            lblResult.Text = "Please enter valid numbers.";
            return;
        }

        if (a == 0)
        {
            lblResult.Text = "a must not be 0.";
            return;
        }

        double d = b * b - 4 * a * c;

        if (d < 0)
        {
            lblResult.Text = "No real roots.";
        }
        else if (d == 0)
        {
            double x = -b / (2 * a);
            lblResult.Text = $"One root: x = {x:0.###}";
        }
        else
        {
            double x1 = (-b + Math.Sqrt(d)) / (2 * a);
            double x2 = (-b - Math.Sqrt(d)) / (2 * a);
            lblResult.Text = $"Two roots: x1 = {x1:0.###}, x2 = {x2:0.###}";
        }
    }
}
WinForms

Driving range / reach distance checker

This version uses fuel, consumption, and trip distance. It is a practical form task with three inputs and one answer.

Track: C# FormsPaste logic into the file shown below

Build steps

  1. Add the three inputs and label them clearly.
  2. Add one button and one result label.
  3. Paste the code into Form1.cs.
  4. Run the app and test both reachable and unreachable trips.

Controls to add

  • TextBox txtFuel for liters left
  • TextBox txtConsumption for liters per 100 km
  • TextBox txtTripDistance for target distance
  • Button btnCheck
  • Label lblResult

Quick test

  • 50 liters, 5 l/100 km, and 400 km should be reachable.
  • 20 liters, 8 l/100 km, and 400 km should not be reachable.
FILE: Form1.cs
Form1.cs
using System;
using System.Windows.Forms;

namespace StudentForms;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        btnCheck.Click += btnCheck_Click;
    }

    private void btnCheck_Click(object? sender, EventArgs e)
    {
        if (!double.TryParse(txtFuel.Text, out double fuel) ||
            !double.TryParse(txtConsumption.Text, out double consumption) ||
            !double.TryParse(txtTripDistance.Text, out double tripDistance))
        {
            lblResult.Text = "Please enter valid numbers.";
            return;
        }

        if (consumption <= 0)
        {
            lblResult.Text = "Consumption must be greater than 0.";
            return;
        }

        double range = fuel / consumption * 100;

        lblResult.Text = tripDistance <= range
            ? $"Reachable. Remaining range is about {range:0.#} km."
            : $"Not reachable. Current range is about {range:0.#} km.";
    }
}
WinForms

Hangman word guessing game

Keep the task small: one hidden word, one guessed-letter box, one button, and two labels.

Track: C# FormsPaste logic into the file shown below

Build steps

  1. Put the two labels at the top so the word and status stay visible.
  2. Add the guess text box and button under them.
  3. Paste the code into Form1.cs.
  4. Start with one hard-coded word. Add random words later if you want.

Controls to add

  • Label lblWord
  • Label lblStatus
  • TextBox txtGuess with MaxLength = 1
  • Button btnTry

Quick test

  • Guess letters from the secret word and check that they appear in the mask.
  • Guess a wrong letter and confirm the remaining attempts go down.
FILE: Form1.cs
Form1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace StudentForms;

public partial class Form1 : Form
{
    private const string SecretWord = "PROGRAM";
    private readonly HashSet<char> guessedLetters = new();
    private int attemptsLeft = 6;

    public Form1()
    {
        InitializeComponent();
        btnTry.Click += btnTry_Click;
        UpdateView();
    }

    private void btnTry_Click(object? sender, EventArgs e)
    {
        if (string.IsNullOrWhiteSpace(txtGuess.Text))
        {
            lblStatus.Text = "Type one letter.";
            return;
        }

        char letter = char.ToUpper(txtGuess.Text[0]);
        txtGuess.Clear();

        if (!char.IsLetter(letter))
        {
            lblStatus.Text = "Letters only.";
            return;
        }

        if (!guessedLetters.Add(letter))
        {
            lblStatus.Text = "You already tried that letter.";
            return;
        }

        if (!SecretWord.Contains(letter))
        {
            attemptsLeft--;
        }

        UpdateView();
    }

    private void UpdateView()
    {
        string masked = string.Join(" ",
            SecretWord.Select(ch => guessedLetters.Contains(ch) ? ch : '_'));

        lblWord.Text = masked;

        if (SecretWord.All(guessedLetters.Contains))
        {
            lblStatus.Text = "You won.";
            btnTry.Enabled = false;
        }
        else if (attemptsLeft <= 0)
        {
            lblStatus.Text = $"You lost. Word: {SecretWord}";
            btnTry.Enabled = false;
        }
        else
        {
            lblStatus.Text = $"Attempts left: {attemptsLeft}";
        }
    }
}
WinForms

ASCII converter

This is a good form task because the user can try both directions without restarting the app.

Track: C# FormsPaste logic into the file shown below

Build steps

  1. Add both text boxes so the user can fill either side first.
  2. Add two buttons, one for each conversion direction.
  3. Paste the code into Form1.cs.
  4. Test both a character input and a number input.

Controls to add

  • TextBox txtCharacter
  • TextBox txtNumber
  • Button btnFromChar
  • Button btnFromNumber
  • Label lblResult

Quick test

  • Type A and click the character button. The code should be 65.
  • Type 66 and click the number button. The character should be B.
FILE: Form1.cs
Form1.cs
using System;
using System.Windows.Forms;

namespace StudentForms;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        btnFromChar.Click += btnFromChar_Click;
        btnFromNumber.Click += btnFromNumber_Click;
    }

    private void btnFromChar_Click(object? sender, EventArgs e)
    {
        if (string.IsNullOrEmpty(txtCharacter.Text))
        {
            lblResult.Text = "Type one character first.";
            return;
        }

        char ch = txtCharacter.Text[0];
        txtNumber.Text = ((int)ch).ToString();
        lblResult.Text = $"Character {ch} = {(int)ch}";
    }

    private void btnFromNumber_Click(object? sender, EventArgs e)
    {
        if (!int.TryParse(txtNumber.Text, out int code))
        {
            lblResult.Text = "Type a valid number.";
            return;
        }

        txtCharacter.Text = ((char)code).ToString();
        lblResult.Text = $"Code {code} = {(char)code}";
    }
}
WinForms

PIN login with second form menu

This is the clearest two-form beginner project: one login form and one menu form.

Track: C# FormsPaste logic into the file shown below

Build steps

  1. Create the first form for login and keep it as the startup form.
  2. Add a second form with a label and one close button.
  3. Paste the first code block into LoginForm.cs.
  4. Paste the second code block into MenuForm.cs.
  5. Change Application.Run(new Form1()) to Application.Run(new LoginForm()) in Program.cs if needed.

Controls to add

  • Login form: TextBox txtPin, Button btnLogin, Label lblStatus
  • Menu form: Label lblWelcome, Button btnClose

Quick test

  • Type the correct PIN and confirm the menu form opens.
  • Type a wrong PIN and confirm the second form does not open.
FILE: LoginForm.cs
LoginForm.cs
using System;
using System.Windows.Forms;

namespace StudentForms;

public partial class LoginForm : Form
{
    private const string CorrectPin = "1234";

    public LoginForm()
    {
        InitializeComponent();
        btnLogin.Click += btnLogin_Click;
    }

    private void btnLogin_Click(object? sender, EventArgs e)
    {
        if (txtPin.Text == CorrectPin)
        {
            MenuForm menu = new MenuForm();
            menu.FormClosed += (_, _) => Close();
            menu.Show();
            Hide();
        }
        else
        {
            lblStatus.Text = "Wrong PIN.";
            txtPin.SelectAll();
            txtPin.Focus();
        }
    }
}
FILE: MenuForm.cs
MenuForm.cs
using System;
using System.Windows.Forms;

namespace StudentForms;

public partial class MenuForm : Form
{
    public MenuForm()
    {
        InitializeComponent();
        lblWelcome.Text = "Login successful.";
        btnClose.Click += (_, _) => Close();
    }
}
WinForms

Train crossing signal

This task is good for timer practice. Use simple colored panels or picture boxes.

Track: C# FormsPaste logic into the file shown below

Build steps

  1. Add three small panels or picture boxes for the lights.
  2. Add one button to start and stop the signal.
  3. Paste the code into Form1.cs.
  4. Click the button and watch the red lights alternate.

Controls to add

  • Panel pnlRedLeft
  • Panel pnlRedRight
  • Panel pnlWhite
  • Button btnStart

Quick test

  • The red lights should alternate, not blink at the same time.
  • The white light should show only when the signal is stopped.
FILE: Form1.cs
Form1.cs
using System;
using System.Drawing;
using System.Windows.Forms;

namespace StudentForms;

public partial class Form1 : Form
{
    private readonly Timer cycleTimer = new() { Interval = 400 };
    private bool leftLightOn;

    public Form1()
    {
        InitializeComponent();
        btnStart.Click += btnStart_Click;
        cycleTimer.Tick += cycleTimer_Tick;
        ShowStoppedState();
    }

    private void btnStart_Click(object? sender, EventArgs e)
    {
        if (cycleTimer.Enabled)
        {
            cycleTimer.Stop();
            ShowStoppedState();
            btnStart.Text = "Start signal";
        }
        else
        {
            cycleTimer.Start();
            btnStart.Text = "Stop signal";
        }
    }

    private void cycleTimer_Tick(object? sender, EventArgs e)
    {
        leftLightOn = !leftLightOn;
        pnlRedLeft.BackColor = leftLightOn ? Color.Red : Color.DarkRed;
        pnlRedRight.BackColor = leftLightOn ? Color.DarkRed : Color.Red;
        pnlWhite.BackColor = Color.DarkGray;
    }

    private void ShowStoppedState()
    {
        pnlRedLeft.BackColor = Color.DarkRed;
        pnlRedRight.BackColor = Color.DarkRed;
        pnlWhite.BackColor = Color.WhiteSmoke;
    }
}
WinForms

Save, load and sort items in a ListBox

This task gives you a very practical small data manager with only a few controls.

Track: C# FormsPaste logic into the file shown below

Build steps

  1. Create the text box, list box, and four buttons.
  2. Paste the code into Form1.cs.
  3. Add a few items and test save, load, and sort in that order.

Controls to add

  • TextBox txtItem
  • ListBox listBoxItems
  • Button btnAdd
  • Button btnSave
  • Button btnLoad
  • Button btnSort

Quick test

  • After Save, the file should appear next to the app.
  • After Load, the list should show the saved items again.
  • After Sort, the list should be alphabetical.
FILE: Form1.cs
Form1.cs
using System;
using System.IO;
using System.Linq;
using System.Windows.Forms;

namespace StudentForms;

public partial class Form1 : Form
{
    private const string FileName = "items.txt";

    public Form1()
    {
        InitializeComponent();
        btnAdd.Click += btnAdd_Click;
        btnSave.Click += btnSave_Click;
        btnLoad.Click += btnLoad_Click;
        btnSort.Click += btnSort_Click;
    }

    private void btnAdd_Click(object? sender, EventArgs e)
    {
        if (string.IsNullOrWhiteSpace(txtItem.Text))
        {
            return;
        }

        listBoxItems.Items.Add(txtItem.Text.Trim());
        txtItem.Clear();
        txtItem.Focus();
    }

    private void btnSave_Click(object? sender, EventArgs e)
    {
        string[] items = listBoxItems.Items.Cast<string>().ToArray();
        File.WriteAllLines(FileName, items);
    }

    private void btnLoad_Click(object? sender, EventArgs e)
    {
        listBoxItems.Items.Clear();

        if (File.Exists(FileName))
        {
            listBoxItems.Items.AddRange(File.ReadAllLines(FileName));
        }
    }

    private void btnSort_Click(object? sender, EventArgs e)
    {
        string[] items = listBoxItems.Items.Cast<string>().OrderBy(item => item).ToArray();
        listBoxItems.Items.Clear();
        listBoxItems.Items.AddRange(items);
    }
}
WinForms

IP address viewer with progress bar

This project feels more alive than a plain message box task because the user sees the progress moving.

Track: C# FormsPaste logic into the file shown below

Build steps

  1. Add the controls and keep the list box fairly large.
  2. Paste the code into Form1.cs.
  3. Click the button and watch the progress bar fill while addresses are shown.

Controls to add

  • Button btnLoad
  • ListBox listBoxAddresses
  • ProgressBar progressBar1
  • Label lblHost

Quick test

  • The list should show at least one address on most machines.
  • The button should disable while loading and enable again at the end.
FILE: Form1.cs
Form1.cs
using System;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace StudentForms;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        btnLoad.Click += btnLoad_Click;
    }

    private async void btnLoad_Click(object? sender, EventArgs e)
    {
        btnLoad.Enabled = false;
        listBoxAddresses.Items.Clear();
        progressBar1.Value = 0;

        string host = Dns.GetHostName();
        lblHost.Text = $"Host: {host}";

        IPAddress[] addresses = await Dns.GetHostAddressesAsync(host);

        progressBar1.Maximum = Math.Max(addresses.Length, 1);

        foreach (IPAddress address in addresses)
        {
            listBoxAddresses.Items.Add(address.ToString());
            progressBar1.Value = Math.Min(progressBar1.Value + 1, progressBar1.Maximum);
            await Task.Delay(250);
        }

        btnLoad.Enabled = true;
    }
}
WinForms

Leap year checker form

This task is short, but it shows the full beginner WinForms flow: input, event, validation, result.

Track: C# FormsPaste logic into the file shown below

Build steps

  1. Add the three controls.
  2. Paste the code into Form1.cs.
  3. Run the form and test 2000, 1900, and 2024.

Controls to add

  • TextBox txtYear
  • Button btnCheck
  • Label lblResult

Quick test

  • 2000 should be leap.
  • 1900 should not be leap.
  • 2024 should be leap.
FILE: Form1.cs
Form1.cs
using System;
using System.Windows.Forms;

namespace StudentForms;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        btnCheck.Click += btnCheck_Click;
    }

    private void btnCheck_Click(object? sender, EventArgs e)
    {
        if (!int.TryParse(txtYear.Text, out int year))
        {
            lblResult.Text = "Type a valid year.";
            return;
        }

        bool isLeap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);

        lblResult.Text = isLeap
            ? $"{year} is a leap year."
            : $"{year} is not a leap year.";
    }
}
WinForms

PDF form exporter with hand-drawn signature

This is the most advanced form on the page. It needs one NuGet package, but the build order is still simple if you keep it step by step.

Track: C# FormsPaste logic into the file shown below

Build steps

  1. Install the PdfSharp NuGet package first.
  2. Add the form controls. Make the signature panel fairly wide and give it a border.
  3. Paste the code into Form1.cs.
  4. Draw with the mouse inside the panel.
  5. Click Export and save the PDF.

Controls to add

  • TextBox txtStudentName
  • TextBox txtClassName
  • Panel pnlSignature
  • Button btnClearSignature
  • Button btnExportPdf

Quick test

  • Draw a short signature line and make sure it appears in the PDF.
  • Clear the signature and confirm the panel becomes empty again.
FILE: Form1.cs
Form1.cs
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using PdfSharp.Drawing;
using PdfSharp.Pdf;

namespace StudentForms;

public partial class Form1 : Form
{
    private Bitmap? signatureBitmap;
    private bool isDrawing;
    private Point lastPoint;

    public Form1()
    {
        InitializeComponent();

        Load += Form1_Load;
        pnlSignature.MouseDown += pnlSignature_MouseDown;
        pnlSignature.MouseMove += pnlSignature_MouseMove;
        pnlSignature.MouseUp += (_, _) => isDrawing = false;
        pnlSignature.Paint += pnlSignature_Paint;

        btnClearSignature.Click += btnClearSignature_Click;
        btnExportPdf.Click += btnExportPdf_Click;
    }

    private void Form1_Load(object? sender, EventArgs e)
    {
        signatureBitmap = new Bitmap(pnlSignature.Width, pnlSignature.Height);
        ClearSignature();
    }

    private void pnlSignature_MouseDown(object? sender, MouseEventArgs e)
    {
        isDrawing = true;
        lastPoint = e.Location;
    }

    private void pnlSignature_MouseMove(object? sender, MouseEventArgs e)
    {
        if (!isDrawing || signatureBitmap is null)
        {
            return;
        }

        using Graphics graphics = Graphics.FromImage(signatureBitmap);
        graphics.DrawLine(Pens.Black, lastPoint, e.Location);
        lastPoint = e.Location;
        pnlSignature.Invalidate();
    }

    private void pnlSignature_Paint(object? sender, PaintEventArgs e)
    {
        if (signatureBitmap is not null)
        {
            e.Graphics.DrawImage(signatureBitmap, 0, 0);
        }
    }

    private void btnClearSignature_Click(object? sender, EventArgs e) => ClearSignature();

    private void ClearSignature()
    {
        if (signatureBitmap is null)
        {
            return;
        }

        using Graphics graphics = Graphics.FromImage(signatureBitmap);
        graphics.Clear(Color.White);
        pnlSignature.Invalidate();
    }

    private void btnExportPdf_Click(object? sender, EventArgs e)
    {
        if (signatureBitmap is null)
        {
            return;
        }

        using SaveFileDialog dialog = new()
        {
            Filter = "PDF files|*.pdf",
            FileName = "form-output.pdf"
        };

        if (dialog.ShowDialog() != DialogResult.OK)
        {
            return;
        }

        string tempImagePath = Path.Combine(Path.GetTempPath(), "signature.png");
        signatureBitmap.Save(tempImagePath);

        PdfDocument document = new();
        PdfPage page = document.AddPage();
        XGraphics graphics = XGraphics.FromPdfPage(page);

        XFont titleFont = new("Arial", 16, XFontStyleEx.Bold);
        XFont textFont = new("Arial", 11, XFontStyleEx.Regular);

        graphics.DrawString("Student form", titleFont, XBrushes.Black, new XPoint(40, 50));
        graphics.DrawString($"Name: {txtStudentName.Text}", textFont, XBrushes.Black, new XPoint(40, 90));
        graphics.DrawString($"Class: {txtClassName.Text}", textFont, XBrushes.Black, new XPoint(40, 115));
        graphics.DrawString("Signature:", textFont, XBrushes.Black, new XPoint(40, 150));

        using XImage signatureImage = XImage.FromFile(tempImagePath);
        graphics.DrawImage(signatureImage, 40, 165, 220, 80);

        document.Save(dialog.FileName);
        File.Delete(tempImagePath);

        MessageBox.Show("PDF saved.");
    }
}
WinForms

Dice roller with image animation

This project feels fun and still teaches a normal event-plus-timer pattern.

Track: C# FormsPaste logic into the file shown below

Build steps

  1. Create a folder called Images in the project output and add dice1.png to dice6.png.
  2. Add the picture box, label, and button to the form.
  3. Paste the code into Form1.cs.
  4. Click Roll and watch the image change a few times before the final value stays on screen.

Controls to add

  • PictureBox picDice
  • Label lblResult
  • Button btnRoll

Quick test

  • The picture should change several times before stopping.
  • The label should show the same final value as the image.
FILE: Form1.cs
Form1.cs
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;

namespace StudentForms;

public partial class Form1 : Form
{
    private readonly Random random = new();
    private readonly Timer animationTimer = new() { Interval = 100 };
    private Image[] diceImages = Array.Empty<Image>();
    private int animationSteps;
    private int currentValue = 1;

    public Form1()
    {
        InitializeComponent();
        Load += Form1_Load;
        btnRoll.Click += btnRoll_Click;
        animationTimer.Tick += animationTimer_Tick;
    }

    private void Form1_Load(object? sender, EventArgs e)
    {
        diceImages = Enumerable.Range(1, 6)
            .Select(i => Image.FromFile(Path.Combine(Application.StartupPath, "Images", $"dice{i}.png")))
            .ToArray();

        picDice.Image = diceImages[0];
        lblResult.Text = "Result: 1";
    }

    private void btnRoll_Click(object? sender, EventArgs e)
    {
        animationSteps = 10;
        btnRoll.Enabled = false;
        animationTimer.Start();
    }

    private void animationTimer_Tick(object? sender, EventArgs e)
    {
        currentValue = random.Next(1, 7);
        picDice.Image = diceImages[currentValue - 1];
        lblResult.Text = $"Result: {currentValue}";
        animationSteps--;

        if (animationSteps <= 0)
        {
            animationTimer.Stop();
            btnRoll.Enabled = true;
        }
    }
}