
- •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 6 MAUI
Figure 6-4 shows the newly created app running on an Android and Windows device.
Figure 6-4. MAUI App running on Android
Exploring MAUI
The startup of a MAUI project looks quite similar to how ASP.net has been operating ever since .NET Core. It uses a Microsoft.Extensions.HostBuilder to bootstrap and launch an app. However, this is where the multiplatform part starts; just like with Xamarin before, we first need to launch the actual application on the operating system that we’re targeting; that’s what the platform-specific folders are for. Let’s take the iOS folder as an example.
156

Chapter 6 MAUI
Figure 6-5. The iOS platform folder
The contents of this folder should look familiar if you have done Xamarin Forms work before. It looks similar to the contents of the iOS project in a Xamarin.Forms project; it also serves the exact same purpose. Program.cs is the launch class of iOS in this case. It creates a UIApplication instance passing AppDelegate as startup class. These classes are .NET wrappers around the native iOS APIs.
As I’ve mentioned before, the single project system is mostly compiler tricks, which isn’t a bad thing; not having a project for every supported platform greatly simplifies things and makes it easier to maintain an overview once a project grows to a certain size.
A Maui.iOS application starts with Program.cs
Listing 6-1. The starting point of a MAUI iOS application
public class Program
{
// This is the main entry point of the application. static void Main(string[] args)
{
157
Chapter 6 MAUI
UIApplication.Main(args, null, typeof(AppDelegate));
}
}
Nothing exciting going on here, just a basic .NET application starting point. It calls into UIApplication.Main to start the application loop. The parameters passed into this method determine the actual starting point of the iOS application, AppDelegate in this case. Listing 6-2 shows the content of AppDelegate.cs.
Listing 6-2. iOS AppDelegate.cs
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram. CreateMauiApp();
}
This is where things are starting to really get different from the classic Xamarin way of working. Instead of overriding the FinishedLaunching method and calling into Xamarin.Forms, from there we make sure that AppDelegate inherits from
MauiUIApplicationDelegate and calls CreateMauiApp on the MauiProgram class included in the project. The other platforms work in a very similar way. Listing 6-3 shows the corresponding Android file.
Listing 6-3. Android MainApplication.cs
[Application]
public class MainApplication : MauiApplication
{
public MainApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership)
{
}
protected override MauiApp CreateMauiApp() => MauiProgram. CreateMauiApp();
}
158
Chapter 6 MAUI
The base class is a different, Android-specific, one; and there is a constructor. That constructor is needed for Android to successfully launch the application. But we do see the same call to MauiProgram.CreateMauiApp.
All our target platforms call MauiProgram.CreateMauiApp() in their startup. This method initializes the cross-platform part of MAUI, while the platform-specific class initializes and bootstraps the application according to the platform. These startup classes can be found in their respective folder in the Platforms folder of your project. For Android, it’s called MainApplication, while for iOS and MacOS it’s AppDelegate, and for Windows it’s App.xaml.cs.
All of these platform-specific classes inherit from a platform-specific Maui base class. This class is where FinishedLaunching moved to. Once the OS has bootstrapped and launched the app, this method fires and initializes the MAUI context. Besides FinishedLaunching, this class also handles all lifecycle events, for example, when the app is activated, moved to the background, terminated, and so on. We’ll discuss app lifecycle more a bit further in this chapter.
The Cross-Platform World
Once the operating system has bootstrapped our app, we enter MAUI cross-platform space. MauiProgram.cs creates the MAUI context in a way which should look familiar; it’s very similar to the Program.cs we have seen in other frameworks within .NET 6.
Listing 6-4. MauiProgram Startup class
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder(); builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
159
Chapter 6 MAUI
return builder.Build();
}
}
The CreateMauiApp method creates a builder. With that builder, we can set our startup object to the App class; keep in mind that at the point we reach this code, our application is already launched. We are now setting up the cross-platform world. We can use the builder for several other things like configuring external fonts that are packaged with the project, for example. Finally we call the Build method on the builder. From that moment on, control will be passed to the class defined as startup. Listing 6-5 shows the App class.
Listing 6-5. The Application class
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MainPage();
}
}
This is the part where we finally get into our own code. Everything is set up and bootstrapped, ready to go. InitializeComponent starts building the visual tree; next we instantiate a new instance of MainPage and set that as the current page. The Application base class contains a MainPage property; whatever page is set to that property is the one shown on screen. Things might get a bit confusing here since we are setting a property called MainPage with an instance of a class called MainPage.
160