Skip to main content

C# Blazor Basics

Introduction

Blazor is a modern web framework developed by Microsoft that allows developers to build interactive web UIs using C# instead of JavaScript. It's a significant shift in web development because it enables C# developers to leverage their existing skills to create full-stack web applications with a unified language across both server and client.

Blazor comes in two hosting models:

  • Blazor Server - Runs your C# code on the server with UI updates sent over a SignalR connection
  • Blazor WebAssembly - Runs your C# code directly in the browser using WebAssembly

In this tutorial, we'll cover the fundamental concepts of Blazor, how to create components, handle data binding, and build practical interactive web applications.

Getting Started with Blazor

Prerequisites

Before we begin, make sure you have the following installed:

  • .NET 6.0 SDK or newer
  • A code editor (Visual Studio, Visual Studio Code with C# extension, or JetBrains Rider)

Creating Your First Blazor App

Let's create a new Blazor Server application using the .NET CLI:

bash
dotnet new blazorserver -o MyFirstBlazorApp
cd MyFirstBlazorApp
dotnet run

This will create and run a Blazor Server application. Navigate to https://localhost:5001 in your browser to see your application running.

Understanding Blazor Components

Components are the building blocks of Blazor applications. A component is a self-contained piece of UI that includes HTML markup and C# code.

Basic Component Structure

Here's a simple Blazor component:

razor
@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
private int currentCount = 0;

private void IncrementCount()
{
currentCount++;
}
}

Let's break down what's happening here:

  1. @page "/counter" defines the route for this component
  2. HTML markup defines the UI structure
  3. @currentCount displays the current value using data binding
  4. @onclick="IncrementCount" binds the button's click event to a C# method
  5. The @code block contains C# logic for the component

Creating a Custom Component

Let's create a simple greeting component:

razor
@* Greeting.razor *@

<div class="alert alert-info">
<h3>Hello, @Name!</h3>
<p>Welcome to Blazor.</p>
</div>

@code {
[Parameter]
public string Name { get; set; } = "Friend";
}

To use this component in another component:

razor
@page "/welcome"

<h1>Welcome Page</h1>

<Greeting Name="John" />
<Greeting Name="Sarah" />
<Greeting /> @* Will use the default value "Friend" *@

Data Binding in Blazor

Blazor provides powerful data binding capabilities that link your UI elements with your C# code.

One-way Data Binding

One-way data binding displays data from your C# properties in the UI:

razor
<h3>Current Temperature: @temperature °C</h3>

@code {
private int temperature = 25;
}

Two-way Data Binding

Two-way data binding allows changes in the UI to update your C# model and vice versa:

razor
<input @bind="username" />
<p>Hello, @username!</p>

@code {
private string username = "";
}

For real-time updates as the user types, use @bind-value and @bind-value:event:

razor
<input @bind-value="searchText" @bind-value:event="oninput" />
<p>You are searching for: @searchText</p>

@code {
private string searchText = "";
}

Handling Events

Blazor allows you to handle DOM events using C# methods:

razor
<button @onclick="HandleClick">Click Me</button>
<p>Button clicked @clickCount times.</p>

@code {
private int clickCount = 0;

private void HandleClick()
{
clickCount++;
}
}

You can also pass parameters to event handlers:

razor
<button @onclick="() => IncrementBy(5)">Add 5</button>
<button @onclick="() => IncrementBy(10)">Add 10</button>
<p>Count: @count</p>

@code {
private int count = 0;

private void IncrementBy(int value)
{
count += value;
}
}

Component Lifecycle

Blazor components have lifecycle methods that you can override to execute code at specific points:

razor
@page "/lifecycle"
@implements IDisposable

<h1>Component Lifecycle Demo</h1>
<p>Current count: @count</p>

@code {
private int count = 0;
private System.Threading.Timer? timer;

// Called when the component is initialized
protected override void OnInitialized()
{
Console.WriteLine("OnInitialized called");
}

// Called after the component has been rendered to the DOM
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
Console.WriteLine("First render complete");
timer = new System.Threading.Timer(_ =>
{
count++;
InvokeAsync(StateHasChanged); // Update UI
}, null, 1000, 1000);
}
}

// Called when the component is disposed
public void Dispose()
{
Console.WriteLine("Component being disposed");
timer?.Dispose();
}
}

Practical Example: Todo List Application

Let's build a simple todo list application to demonstrate these concepts:

razor
@page "/todo"

<h3>Todo List</h3>

<div class="form-group">
<input placeholder="What needs to be done?" @bind="newTodo" @bind:event="oninput" @onkeyup="AddTodoOnEnter" class="form-control" />
<button class="btn btn-primary mt-2" @onclick="AddTodo" disabled="@string.IsNullOrWhiteSpace(newTodo)">Add Todo</button>
</div>

@if (todos.Count == 0)
{
<p class="mt-3">No todos yet! Add some.</p>
}
else
{
<ul class="mt-3 list-group">
@foreach (var todo in todos)
{
<li class="list-group-item">
<input type="checkbox" @bind="todo.IsDone" />
<span style="@(todo.IsDone ? "text-decoration: line-through" : "")">@todo.Title</span>
<button class="btn btn-sm btn-danger float-end" @onclick="() => RemoveTodo(todo)">Delete</button>
</li>
}
</ul>

<p class="mt-3">@todos.Count(t => !t.IsDone) out of @todos.Count items remaining</p>
@if (todos.Any(t => t.IsDone))
{
<button class="btn btn-sm btn-warning" @onclick="RemoveCompletedTodos">Clear Completed</button>
}
}

@code {
private List<TodoItem> todos = new List<TodoItem>();
private string newTodo = "";

private void AddTodo()
{
if (!string.IsNullOrWhiteSpace(newTodo))
{
todos.Add(new TodoItem { Title = newTodo });
newTodo = "";
}
}

private void AddTodoOnEnter(KeyboardEventArgs e)
{
if (e.Key == "Enter")
{
AddTodo();
}
}

private void RemoveTodo(TodoItem todo)
{
todos.Remove(todo);
}

private void RemoveCompletedTodos()
{
todos.RemoveAll(t => t.IsDone);
}

private class TodoItem
{
public string Title { get; set; } = "";
public bool IsDone { get; set; }
}
}

This example showcases:

  1. Two-way data binding with @bind
  2. Event handling with @onclick and @onkeyup
  3. Conditional rendering with @if statements
  4. List rendering with @foreach
  5. CSS styling based on component state
  6. State management with C# properties and methods

Dependency Injection

Blazor fully supports dependency injection. Here's how to use services:

razor
@page "/weather"
@inject WeatherService WeatherService

<h1>Weather Forecast</h1>

@if (forecasts == null)
{
<p>Loading...</p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}

@code {
private WeatherForecast[]? forecasts;

protected override async Task OnInitializedAsync()
{
forecasts = await WeatherService.GetForecastAsync();
}
}

To register this service, add it to the Program.cs file:

csharp
builder.Services.AddSingleton<WeatherService>();

Summary

In this tutorial, you've learned the basics of Blazor, including:

  • Setting up a Blazor application
  • Creating and using components
  • Implementing data binding
  • Handling events
  • Working with component lifecycle
  • Building a practical todo application
  • Using dependency injection

Blazor brings the power of C# to web development, allowing you to build rich, interactive web applications without having to write JavaScript. It's particularly appealing for C# developers who want to leverage their existing skills for full-stack development.

Additional Resources

Exercises

  1. Enhance the todo application to persist todos in browser localStorage
  2. Create a weather dashboard that fetches data from a weather API
  3. Build a simple calculator application with Blazor
  4. Create a form with validation using EditForm and DataAnnotations
  5. Implement a component that uses JavaScript interop to access browser APIs

Happy coding with Blazor!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)