Course 08 · Blazor · Your first app
In one sitting you'll build a complete little app — a number-guessing game — that picks a secret number, takes your guess, and tells you if you're too high, too low or spot on. Everything you've learned so far, working together.
Today's goal
Build a working game: the app secretly picks a number from 1 to 10, you type a guess and press a button, and it tells you too high, too low, or correct — counting how many guesses it took.
By the end you'll be able to:
Random.@bind).if / else if / else and the ==, < and > operators.<MyButton> from last lesson into one small, finished app.Recap
Shout out the answers — quick questions on things we've already covered.
@bind) do?= and == in C#?if / else if / else chain let our code do?EventCallback, and where did we use one?count++ do?Fast-fire · together
Read the code and predict the answer before we reveal it.
What numbers can secret end up being?
@code {
int secret = new Random().Next(1, 11);
}
A whole number from 1 to 10 — not 1 to 11. Next(min, max) includes the lower bound but excludes the upper one, so Next(1, 11) means “1 up to but not including 11”. A classic off-by-one trap.
Fast-fire · together
This snippet is from the game we're about to build. Shout out everything that's wrong.
<input @bind="guess" />
<MyButton OnClick="CheckGuess">Guess</MyButton>
<p>@message</p>
@code {
int secret = 7;
int guess;
string message = "";
void Check()
{
if (guess = secret)
message = "Correct!";
else if (guess < secret)
message = "Too low"
else
message = "Too high";
}
}
OnClick="CheckGuess" calls a method that doesn't exist — the method below is named Check. The names must match exactly.if (guess = secret) uses a single = (assignment) instead of == (comparison). A condition must be true/false, and guess = secret isn't — it won't compile.message = "Too low" is missing its semicolon — every statement ends with ;.Fast-fire · together
A different snippet — shout out anything that looks wrong.
<input @bind="guess" />
<MyButton OnClick="NewGame">New game</MyButton>
<p>@message</p>
@code {
int secret;
string guess;
string message = "";
void NewGame()
{
secret = new Random().Next(1, 10);
message = "I'm thinking of a number from 1 to 10...";
}
}
string guess; is the wrong type — to compare a guess against secret (an int), guess must be an int too. A string would never compare numerically.Next(1, 10) is an off-by-one logic bug: it only ever produces 1–9, so the secret can never be 10 even though the message promises “1 to 10”. It should be Next(1, 11).Fast-fire · together
One more prediction before we build.
After pressing the button three times, what does the paragraph show?
<p>@message</p>
@code {
int tries = 0;
string message = "";
void Check()
{
tries++;
message = "Guess number " + tries;
}
}
“Guess number 3”. tries++ adds one each press (0 → 1 → 2 → 3), and "Guess number " + tries glues the text to the number, so the value climbs with every click.
Build · The secret number
A game is just a page that holds some state in @code and reacts to the user. Let's start with the one thing every guessing game needs: a secret number the computer chooses.
GuessTheNumber.razor, and replace its contents with the code below.@page "/guess"
<h1>Guess the Number</h1>
<p>I'm thinking of a number from 1 to 10.</p>
@code {
int secret = new Random().Next(1, 11);
}
The @page "/guess" line gives the page its own address. new Random().Next(1, 11) picks the secret — remember, that's 1 to 10, not 11.
Build · Make it reachable
Your page now lives at the address /guess — but nothing points to it, so the only way to reach it is to type the address into the browser by hand. Real apps give every page a link in the menu. In a Blazor project that menu lives in one shared file, NavMenu.razor, and each page you want in the sidebar gets its own <NavLink>.
Shared/NavMenu.razor and add the block below next to the menu items already there (Home, Counter, Weather).<div class="nav-item px-3">
<NavLink class="nav-link" href="guess">
Guess the Number
</NavLink>
</div>
The href="guess" here matches the @page "/guess" on your page — that's exactly how Blazor connects a menu link to a page: by its route. (The menu drops the leading slash; same address.)
Build · Take a guess
We need somewhere to type a guess, and a button to submit it. The input uses two-way binding (@bind) so whatever the user types lands in a C# variable called guess. The button is your <MyButton> from last lesson — it calls a method when activated by mouse or keyboard.
GuessTheNumber.razor so it matches the code below. (If your MyButton lives in a Components folder, Blazor finds it automatically — no extra @using needed.)@page "/guess"
<h1>Guess the Number</h1>
<p>I'm thinking of a number from 1 to 10.</p>
<input type="number" @bind="guess" />
<MyButton OnClick="Check">Guess</MyButton>
<p>@message</p>
@code {
int secret = new Random().Next(1, 11);
int guess;
string message = "";
void Check()
{
// We'll fill this in next.
}
}
Check(), but it does nothing yet, so message stays empty. Time to make it decide.Build · Make it decide
This is the heart of the game: compare the guess with the secret and set the message. An if / else if / else chain checks the three possibilities in order — equal first, then lower, otherwise higher.
void Check()
{
if (guess == secret)
message = "Correct! 🎉";
else if (guess < secret)
message = "Too low — try higher.";
else
message = "Too high — try lower.";
}
guess == secret with two equals signs — comparison, not assignment. One = would try to change secret and won't even compile.That's a fully playable game already. Run it (F5), type a number and press Guess. Now let's make it count your tries.
Build · Count the guesses
Every guess should bump a counter — exactly the tries++ idea from the fast-fire round. Add a tries field, increase it at the start of Check(), and show it on the page.
<input type="number" @bind="guess" />
<MyButton OnClick="Check">Guess</MyButton>
<p>@message</p>
<p>Guesses so far: @tries</p>
@code {
int secret = new Random().Next(1, 11);
int guess;
int tries = 0;
string message = "";
void Check()
{
tries++;
if (guess == secret)
message = "Correct! 🎉";
else if (guess < secret)
message = "Too low — try higher.";
else
message = "Too high — try lower.";
}
}
Here's the finished game running live — type a guess, or Tab to the button and press Enter:
Your turn
Build on your GuessTheNumber.razor. Start where you're comfortable and climb as far as you can.
Make the game pick from 1 to 20 instead of 1 to 10 (remember the off-by-one rule), and update the on-screen text to match.
Reword the too-high / too-low / correct messages in your own style, and show the number of guesses inside the “Correct!” message.
Add a second button that starts a new game — pick a fresh secret, reset tries to 0, and clear the message.
Show “Correct!” in green, “Too high” in orange and “Too low” in blue, using a CSS class you switch in Check().
If the guess is below 1 or above 10, show “Stick to 1–10!” and don't count it as a guess.
Build a separate Quiz.razor page that asks a single multiple-choice question (e.g. “What does == do in C#?”). The student types or picks an answer, presses your <MyButton>, and an if/else shows “Correct! ✅” or “Not quite — try again.” Same ingredients as the game, but comparing text instead of numbers. Stretch within the stretch: add a second question or keep a score.
Homework
Finish these at home — we'll review them at the start of the next lesson.
Add the “new game” button so the game can be replayed without restarting the app.
Make the message change colour depending on the result.
Build the advanced quiz page above. Bring it next time to show the group.