- •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 3 Command Line Interface
Figure 3-12. Adding a project reference through the command line
Some frameworks, extension, etc. come with their own command line-based tool; Entity Framework, for example, ships with a CLI tool, for example, to create migrations or update a database. These tools are managed with the dotnet tool command. Dotnet tool can install tools, list all the installed tools, update or restore them, run them, and uninstall them.
Wrapping Up
In this chapter, we have gone over the most common parts of the extensive CLI tooling that is included with .NET 6. We as developers use these tools more than we realize, since they are the magic behind the buttons and shortcut keys in Visual Studio. Knowing what the capabilities are, and how to find the different options, is important knowledge, not only to have an idea of what is going on under the hood of Visual Studio but also to be able to define build and release pipelines.
64
CHAPTER 4
Desktop Development
I often get the question if there is still use in learning desktop technologies like WPF or WinForms. My answer is always the same: of course there is! There has been a big movement toward the Web the past few years, which definitely makes sense in regard
to ease of deployment and installation. No need to juggle MSI files around, making sure every client computer has the correct version of .NET installed, finding out what version of the software a customer is using, and so on. With web applications, we install the software on a server, and all of our users just use their browsers to use the application. So why are native apps (both desktop and mobile apps) still a thing? The answer is simple, performance and capabilities. The Web, for now, does not have the same level of access to peripherals and operating system as a native app does. As for performance, let’s look at the way an operating system renders its applications. Rendering happens in steps, layer by layer. A WPF application, for example, will render its main window followed by the controls on that specific window; the whole rendering of an application is optimized to draw the user interface as fast as possible to not make the user feel like the application is hanging. If we apply that same logic to a web browser, you’ll understand that the browser’s main window and controls like the back and forward buttons, favorite bar, extension icons, and so on are rendered first. Once everything is on the screen, the browser will start interpreting and rendering the HTML, so the actual user interface of your web application is last to render. Another major difference is threading, JavaScript is single-threaded, so it’s not possible to schedule heavy work on a separate thread. The advice there is to have a server handle the heavy lifting and make your frontend wait for the response, which is a very valid argument, except for applications that need real-time heavy processing, like the software stock brokers use, for example. Every millisecond of delay caused by a request going over the network can cost them money. A native desktop application, running on a powerful client computer, can handle these calculations
with ease.
65
© Nico Vermeir 2022
N. Vermeir, Introducing .NET 6, https://doi.org/10.1007/978-1-4842-7319-7_4
Chapter 4 Desktop Development
.NET 6 comes with multiple choices for desktop development, WPF, WinForms, Windows App SDK, CLI, Blazor Desktop, and MAUI. In this chapter, we’ll take a look at these options, except for Blazor Desktop, and we will discuss that option in the Blazor chapter of this book.
WinAPI
Win32 and Win64, depending on the OS and CPU architecture, are the APIs in Windows that allow applications to run. The API is responsible for everything going on in Windows, from rendering applications and access to the operating system to installation/configuration and so on. It’s the engine under the hood of Windows. Without WinAPI, we simply wouldn’t be able to run applications on Windows. WinAPI is a native C/C++ API, meaning we’d need to write applications in those languages to leverage the WinAPI API set.
With WinAPI, we can build applications that have incredible performance, because we are in an unmanaged world. Languages like C# and Java, for example, are managed languages, meaning that there is a runtime taking care of recovering and managing memory. Unmanaged code, like C or C++, runs much closer to the metal, leaving
the responsibility of reclaiming memory to the developer but gaining performance in return. But this performance comes with a price. It takes longer to develop these
types of applications, and since they are unmanaged they are more prone to memoryrelated bugs. Listing 4-1 shows an example of the C++ code needed to simply draw an empty window on screen. Feel free to copy, compile, and run the code in Listing 4-1 if you have a C++ environment setup, and you will notice that it just works but shows an empty screen.
Listing 4-1. Drawing an empty window with Win32
#ifndef UNICODE #define UNICODE #endif
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
66
Chapter 4 Desktop Development
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx( |
|
||
0, |
|
|
// Optional window styles. |
CLASS_NAME, |
|
|
// Window class |
L"Learn to Program Windows", |
// Window text |
||
WS_OVERLAPPEDWINDOW, |
// Window style |
||
// Size and |
position |
|
|
CW_USEDEFAULT, |
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, |
||
NULL, |
// |
Parent window |
|
NULL, |
// |
Menu |
|
hInstance, |
// |
Instance handle |
|
NULL |
// |
Additional application data |
|
); |
|
|
|
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
67
Chapter 4 Desktop Development
// Run the message loop. MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY: PostQuitMessage(0); return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint. FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1)); EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
68