Course 08 · Blazor · Course wrap-up

Wrap-Up — and a Colour-Match Game

The final lesson. A fast victory lap through everything you've learned this course, two quick new tricks — drag & drop and sound — and then a brand-new game that puts it all together.

Last one! We've less than 90 minutes, so this is a tour, not a marathon — we won't rebuild everything, we'll remember it and then use it. Tip: click the button to go full screen and show just the current step.
Step 1 of 13

Today's goal

Celebrate, remember, and build one more thing

Take a victory lap through the whole course, learn two new tricks — drag & drop and sound — and use them with everything you already know to build a colour-matching game.

By the end of today you'll have:

  • Recalled the core toolkit: state, binding, events, if/else and Random.
  • Seen how an element can be dragged and dropped in Blazor.
  • Made the app play a sound in response to what you do.
  • Played a game that ties it all together — and ideas to take it further.

Looking back

How far you've come

Thirteen weeks ago, a "program" was a mystery. Hands up — who remembers doing each of these?

  • Wrote your first C# program and learned what hardware actually runs.
  • Used variables, types and even your own classes.
  • Built pages with HTML & CSS, then made them dynamic with Blazor.
  • Handled events, wired up two-way binding, and made decisions with if/else.
  • Built your own <MyButton> component, then a complete Guess the Number game.
That's a real developer's toolkit. Today we prove it by reaching for it without notes.

Recap

Fast-fire: what do we remember?

Shout out the answers — quick questions spanning the whole course.

  • What's the difference between a page and a component in Blazor?
  • = vs == in C# — which one compares?
  • What does two-way binding (@bind) do that just showing @variable doesn't?
  • What whole numbers can new Random().Next(1, 11) produce?
  • What is an EventCallback, and why did <MyButton> expose one?

Fast-fire · together

What does it do?

Read the code and predict the answer before we reveal it.

What does it do?

After pressing the button, what can picked be?

ColourPicker.razor
<MyButton OnClick="Pick">Pick a colour</MyButton>
<p>@picked</p>

@code {
    string[] colours = { "Red", "Blue", "Green", "Amber" };
    string picked = "";

    void Pick()
    {
        picked = colours[new Random().Next(colours.Length)];
    }
}
Reveal the answer

One of "Red", "Blue", "Green" or "Amber", picked at random. Next(colours.Length) is Next(4) → a number 0, 1, 2 or 3 (the upper bound is excluded), which are exactly the valid positions in the array. This is the trick behind today's game: a random index into a list of colours.

Fast-fire · together

Spot the error

Read this carefully and shout out anything that's wrong. It only uses things we've already covered.

Spot the error
Check.razor
<input @bind="answer" />
<MyButton OnClick="Check">Check</MyButton>
<p>@result</p>

@code {
    string answer;
    int secret = 7;
    string result = "";

    void Check()
    {
        if (answer == secret)
        {
            result = "Correct!"
        }
    }
}
Reveal the answer
  1. string answer; is the wrong type. We compare it against secret, which is an int, so answer must be an int too — you can't compare text to a number. It should be int answer;.
  2. result = "Correct!" is missing its semicolon — every statement ends with ;.

Fast-fire · together

Spot the error, again

Here's another. This one picks a random colour from a list — shout out anything that looks wrong.

Spot the error
Picker.razor
<p>@message</p>
<MyButton OnClick="NewColour">New colour</MyButton>

@code {
    string[] colours = { "Red", "Blue", "Green", "Amber" };
    string message = "";

    void NewColour()
    {
        int i = new Random().Next(1, 4);
        message = "I picked " + colours(i);
    }
}
Reveal the answer
  1. new Random().Next(1, 4) can only give 1, 2 or 3 — never 0. So it can never pick colours[0] ("Red"), and never covers all four. To choose any of the four, use Next(0, 4) (or Next(colours.Length)) — remember the upper number is excluded.
  2. colours(i) uses round brackets, but you read an item from an array with square brackets: colours[i].

The whole course on one page

Your whole toolkit, in one place

Almost everything you built this term has the same shape: some state in @code, the user does something (an event), you decide with if/else, and the screen updates automatically.

The pattern behind every app you built
<input @bind="answer" />            <!-- binding: the box ↔ a variable -->
<MyButton OnClick="Check">Go</MyButton>  <!-- your component + an event -->
<p>@message</p>                     <!-- show state on screen -->

@code {
    int secret = new Random().Next(1, 11);  // random
    int answer;
    string message = "";

    void Check()                       // event handler
    {
        if (answer == secret)          // decide
        {
            message = "Correct!";
        }
        else
        {
            message = "Try again!";
        }
    }
}
Recognise it? That's the Guess the Number game — and, with colours instead of numbers, it's today's game too.

New trick · 1 of 2 · Build

Drag & drop — build the page

Two ingredients. You make something draggable, and you make a drop zone that accepts it. Each zone must do two things: allow the drop with @ondragover:preventDefault="true", and react to it with @ondrop. What happens on a drop is just an if/else — exactly what you already know. The four zones are the same recipe repeated once per colour.

Do this: right-click the Pages folder → Add → Razor Component, name it ColourMatch.razor, and replace its contents with the code below.
ColourMatch.razor — create this file
@page "/colour-match"

<h1>Colour Match</h1>
<p>Drag the card onto the zone with the same name.</p>

<div class="zones">
    <div class="zone" style="background:#E5484D"
         @ondragover:preventDefault="true" @ondrop='() => Drop("Red")'>Red</div>
    <div class="zone" style="background:#2563EB"
         @ondragover:preventDefault="true" @ondrop='() => Drop("Blue")'>Blue</div>
    <div class="zone" style="background:#22A06B"
         @ondragover:preventDefault="true" @ondrop='() => Drop("Green")'>Green</div>
    <div class="zone" style="background:#F0A202; color:#18122B"
         @ondragover:preventDefault="true" @ondrop='() => Drop("Amber")'>Amber</div>
</div>

<div class="card" draggable="true">@cardColour</div>

<p>@message</p>

@code {
    string[] colours = { "Red", "Blue", "Green", "Amber" };
    string cardColour = "Red";
    string message = "";

    void Drop(string zoneColour)
    {
        if (zoneColour == cardColour)
        {
            message = "🎉 Correct!";
            cardColour = colours[new Random().Next(colours.Length)]; // deal a new card
        }
        else
        {
            message = "Try again!";
        }
    }
}

The @page "/colour-match" line gives the page its own address — we'll link to it in a moment. Notice the handler quotes: the attribute uses single quotes (@ondrop='…') so the "Red" inside can use double quotes.

The easy-to-forget bit: without @ondragover:preventDefault="true" the drop simply never happens — the page just bounces the card back. (Drag & drop is a mouse gesture, so this game is best on a laptop, not a touch screen.)

New trick · 1 of 2 · Style

Make the zones and card visible

Right now those <div>s are just text — you can't see a zone to aim at, or a card to grab. Like MyButton back in lesson 11, a component carries its own scoped CSS in a matching .razor.css file, so these styles only affect this page.

Do this: create a new file called ColourMatch.razor.css in the same folder as ColourMatch.razor (right-click the Pages folder → Add → New Item → Style Sheet). Blazor links it to the page automatically by matching the name.
ColourMatch.razor.css — create this file
.zones {
    display: flex;
    gap: 10px;
    margin: 16px 0;
}

.zone {
    flex: 1;
    height: 64px;
    border-radius: 12px;
    border: 3px dashed rgba(255, 255, 255, .6);
    color: #fff;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
}

.card {
    width: 120px;
    height: 64px;
    border-radius: 12px;
    background: #fff;
    border: 2px solid #ECE7F2;
    box-shadow: 0 6px 16px rgba(24, 18, 43, .2);
    font-weight: 700;
    cursor: grab;          /* the "you can drag me" hand */
    user-select: none;
    display: flex;
    align-items: center;
    justify-content: center;
}

.card:active { cursor: grabbing; }

The zones get their colour from the style="background:…" you wrote in the markup; this CSS gives them shape and the dashed "drop here" look. cursor: grab is the small touch that tells the player the card can be picked up.

Build · Make it reachable

Add a link to your new page

Your page lives at the address /colour-match — but nothing points to it yet, so the only way to reach it is to type the address 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>.

Do this: open Shared/NavMenu.razor and add the block below next to the menu items already there (Home, Counter, Weather — and your Guess the Number link from last time).
Shared/NavMenu.razor — add a menu item
<div class="nav-item px-3">
    <NavLink class="nav-link" href="colour-match">
        Colour Match
    </NavLink>
</div>

The href="colour-match" here matches the @page "/colour-match" on your page — that's how Blazor connects a menu link to a page: by its route. (The menu drops the leading slash; same address.)

Notice: run the app (F5) — your new Colour Match link appears in the sidebar, and clicking it opens the game. Try dragging a card onto a zone: a match cheers and deals a new card, a miss says "Try again". Next we add sound.

New trick · 2 of 2

Play a sound

To play a sound, show an <audio> element with autoplay. The trick to making it play again on the next try is @key: change the key and Blazor rebuilds the element from scratch, so it plays once more.

Grab the sounds — no hunting online. Download these two ready-made clips, then we'll add them to your project:
⬇ correct.wav  ·  ⬇ wrong.wav

Add them to your project — step by step:

  1. In Visual Studio, find the wwwroot folder in Solution Explorer (the file tree, usually on the right). wwwroot is where a Blazor app keeps files the browser can load — images, sounds, and so on.
  2. Right-click wwwrootAddNew Folder, and name the new folder sounds.
  3. Drag the two files you just downloaded into that sounds folder. (Or right-click the sounds folder → AddExisting Item… and pick them.)

Now your app can reach them at sounds/correct.wav and sounds/wrong.wav. Wire them up like this:

ColourMatch.razor — add sound
@if (sound is not null)
{
    <audio autoplay src="@sound" @key="soundKey"></audio>
}

@code {
    string? sound;
    int soundKey;

    void Play(string file)   // e.g. Play("sounds/correct.wav")
    {
        sound = file;
        soundKey++;          // re-key so the same sound replays
    }
}

Call Play("sounds/correct.wav") when the colours match, and Play("sounds/wrong.wav") when they don't.

Curiosity — no need to copy: the live game below uses no sound files at all. It builds its two little beeps as numbers in C# and plays them straight from memory — the same <audio> + @key idea underneath.

Your turn

The Colour-Match game — play it, then make it yours

Here's the finished game. Drag the card onto the matching colour: a correct drop cheers and deals a new card; a wrong one buzzes and lets you retry. Build your streak!

✎ Live — drag the card to the matching colour (sound on!)

Now make it your own — pick your level:

  • Easy

    New colours

    Change the four colours and their names (the Colours list). Try pastels, or your favourite team's colours.

  • Easy

    Your words

    Reword the congratulations and try-again messages, and pick your own emojis.

  • Medium

    Real sound files

    Add your own correct.mp3 / wrong.mp3 in wwwroot/sounds and play them with the <audio> + @key trick above.

  • Medium

    Best streak

    Remember the highest streak reached and show “Best: N” next to the current streak.

  • Hard

    Five colours

    Add a fifth colour and a fifth zone. Notice how little code changes when the colours live in a list.

  • Hard · Advanced

    Beat the clock

    Add a 30-second countdown: how many can you match before time runs out? (You'll need a timer — ask, and we'll point you at System.Timers.)

Where to go next

You're a developer now

No homework this time — the course is complete. 🎉 But the fun part is that you can keep going. A few good next adventures:

  • Next

    Loops & lists

    Show a whole list of things on screen with @foreach — high scores, a to-do list, a gallery.

  • Next

    Talk to the internet

    Fetch real data from a free web API (a joke, the weather, a fact) and show it in your app.

  • Next

    Publish it

    Put one of your apps online so friends and family can use it from a link.

Thank you. You started not knowing what a program was, and you're finishing by building real, interactive apps. Keep that curiosity — and keep building.