- •Table of Contents
- •About the Author
- •Acknowledgments
- •Introduction
- •Version Support
- •Supported Versions
- •A Unified Platform
- •Roadmap
- •Supported Operating Systems
- •Command Line Interface
- •Desktop Development
- •Blazor
- •MAUI
- •Wrapping Up
- •.NET 6 Architecture
- •Runtimes
- •CoreCLR
- •Mono
- •WinRT
- •Managed Execution Process
- •Desktop Packs
- •Wrapping Up
- •Dotnet New
- •Dotnet Restore
- •NuGet.config
- •Dotnet Build
- •Dotnet Publish
- •Dotnet Run
- •Dotnet Test
- •Using the CLI in GitHub Actions
- •Other Commands
- •Wrapping Up
- •WinAPI
- •WinForms
- •STAThread
- •WinForms Startup
- •DPI Mode
- •Responding to Scale Events
- •Visual Styles
- •Text Rendering
- •The Message Loop
- •The Form Designer
- •WPF Startup
- •XAML Layout
- •Visual Tree
- •Data Binding
- •Windows App SDK
- •Building a Windows App SDK application
- •Using Windows APIs with Windows App SDK
- •Packaging
- •Migrating to .NET 6
- •Upgrade Assistant
- •Wrapping Up
- •Blazor WebAssembly
- •Creating a Blazor Wasm Project
- •Blazor Progressive Web Apps
- •Exploring the Blazor Client Project
- •Blazor in .NET 6
- •Blazor Component System
- •Creating Blazor Pages
- •Running a Blazor App
- •Blazor Server
- •SignalR
- •Blazor Desktop
- •Wrapping Up
- •Project Structure
- •Exploring MAUI
- •The Cross-Platform World
- •Application Lifecycle
- •MVVM
- •MVVM Toolkit
- •Wrapping Up
- •Model-View-Controller
- •Routing
- •Views
- •Controllers
- •Controller-Based APIs
- •Minimal APIs
- •Wrapping Up
- •Web Apps
- •Creating an App Service
- •Static Web Apps
- •Web App for Containers
- •Docker
- •Azure Functions
- •Deploying Azure Functions
- •Wrapping Up
- •Record Types
- •Monolith Architecture
- •Microservices
- •Container Orchestration
- •Kubernetes
- •Docker Compose
- •Dapr
- •Installing Dapr
- •Dapr State Management
- •Wrapping Up
- •Roslyn
- •Compiler API
- •Diagnostic API
- •Scripting API
- •Workspace API
- •Syntax Tree
- •Roslyn SDK
- •Source Generators
- •Writing a Source Generator
- •Debugging Source Generators
- •Wrapping Up
- •Garbage Collector
- •The Heap
- •The Stack
- •Garbage Collection
- •A Look at the Threadpool
- •Async in .NET 6
- •Await/Async
- •Cancellations
- •WaitAsync
- •Conclusion
- •Index
Chapter 5 Blazor
Figure 5-3. Installing a web app as PWA
Besides a manifest.json file, there’s also a service worker added to the project. Service workers are JavaScript code that acts like a proxy between the application and the network. Service workers enable offline access to web applications by caching data and fetching updated data once the network is available.
More information on building a Blazor-based PWA, including the manifest file and service workers, can be found at https://docs.microsoft.com/en-us/aspnet/core/ blazor/progressive-web-app?view=aspnetcore-6.0&tabs=visual-studio.
Exploring the Blazor Client Project
Figure 5-4 shows a default Blazor WASM client project loaded in Visual Studio.
129
Chapter 5 Blazor
Figure 5-4. A Blazor WASM project
Blazor makes use of the Razor framework that made its debut in ASP.NET MVC. If you have done ASP.NET MVC before, you will recognize a lot of things, but there are some Blazor-specific things in there as well.
Listing 5-2 shows the content of the counter page.
Listing 5-2. Counter page in the default Blazor template
@page "/counter"
<h1>Counter</h1>
130
Chapter 5 Blazor
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
Razor is a framework that mixes HTML and .NET code. It allows us to declare variables and bind them to HTML elements or trigger .NET methods from HTML elements.
In Razor, an @ sign signals the start of a .NET statement. An example of this is @page "/counter" at the top of the page. Page is an attribute; instead of using square brackets, we use the @ sign again to set this attribute to the counter page. In case of the page attribute, it is used for Blazor’s navigation service. This page can now be accessed through https://the-webapps-url/counter.
The @code directive specifies the code block for this specific page; those code blocks are scoped to the page they are declared in. The sample code declares a private field called currentCount in a normal C# way. That field is bound to in HTML by prefixing it with an @ sign, <p role="status">Current count: @currentCount</p>.
This page also demonstrates updating a data field by calling a method. The HTML button specifies an @onclick event. This type of event is different from the HTML/ JavaScript combination you might be used to because of the @ sign. Once again, this signals a .NET statement, in this case calling a method that is declared within this page. The IncrementCount method increases the integer field with 1, immediately updating the UI as a result.
The interesting part here is that the page is not updated through a server callback but rather through updating the DOM; this can be seen in most browser’s dev tools (F12). Looking at the visual tree in dev tools, they often mark the elements that are changing. Figure 5-5 shows this for the counter page right after clicking the button for the fifth time.
131
Chapter 5 Blazor
Figure 5-5. Updating the DOM after a button click
As you can see, only one element is marked, so only one small part of the page is changing. This results in fast web applications that feel more native than, for example, ASP.NET MVC applications that often rely on page reloads and server callbacks.
Blazor in .NET 6
To see how .NET 6 handles Blazor projects, let’s start at the beginning. As usual a Blazor app starts at Program.cs, shown in Listing 5-3.
Listing 5-3. Program class of a Blazor WASM application
using BlazorWasmDemo;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("#app"); builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
await builder.Build().RunAsync();
132
Chapter 5 Blazor
Blazor in .NET 6 makes use of top-level statements to trim down the size and complexity of the Program file. As you can see in Listing 5-3, there is no namespace, no class declaration, and no Main method declaration; the Main method is still there but it is hidden away as a syntactic trick; should you inspect the intermediate language, you will find the Main method there. More information on top-level statements can be found here https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/top- level-statements.
This sample application sets the App component as root component for this web application. The system uses selectors like selectors in CSS to select the correct element in the index.html file where to inject the components. In the default template, the #app selector is used. If you open the default index.html file in the wwwroot folder of your project, you will find a div with app as ID. This is the div where our Blazor application will be hosted.
The App component is listed in Listing 5-4.
Listing 5-4. Default app component
<Router AppAssembly="@typeof(App).Assembly"> <Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout= "@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there’s nothing at this address.</p> </LayoutView>
</NotFound> </Router>
The App component is the most top-level component in a default Blazor application. It configures the router, to enable page navigation, and considers the router to be similar to a NavigationPage if you’re more used to Xamarin Forms or MAUI development. The router specifies found and not found views; the not found view will be shown whenever navigation triggers an HTTP 404.
133