This is Part 1 in the multi-part series of .NET MAUI – Blazor articles.
Part 2 on abstracting core logic as Razor Class Library (RCL), Component Navigation, and Sharing AppState between .NET MAUI and Blazor is now published. Click here to read the article.
Part 3 of this series on the streamlined process of registering the dependencies for BlazorWebView from .NET MAUI RC1 release is now published. Click here to read the article.
.NET MAUI, Multi-platform App UI, a framework to develop high-performant, cross-platform, native mobile and desktop applications for Android, iOS, macOS (via Mac Catalyst), and Windows from a single codebase.
And Blazor, a framework for full-stack web development with C# and Razor.
When these two modern technologies come together, then it’s certainly a delight for the .NET developers as it paves the way to leverage the skills they’re already familiar with.
Under the hood, it’s implemented as a control, named BlazorWebView (inherits from View), which is capable of processing Razor components during runtime and generating its equivalent HTML which will get rendered using the platform’s native Web engine without the involvement of any web server. This has the capability of running truly offline. And at the meantime, it can talk to a remote API and other Web services to bring in data and content for further processing.
Since everything runs locally in the same process as the application, the real benefit would be performance and access to device capabilities like camera, onboard sensors, file system, …
Another advantage is the reuse of Razor components across frameworks, it can be implemented as a Razor Class Library (RCL) and shared with Blazor Server and WebAssembly. This allows for maximum code reuse and producing Mobile, Desktop, and Web solutions from a single codebase.
Since it’s a View, it can be added to any page and can occupy it in full or partial. To use this particular control, the app startup needs to be slightly tweaked from the usual .NET MAUI application. If you’re new to .NET MAUI, more on app startup in this article.
The first thing to do is to update the Project SDK definition (in the CSPROJ file) to that of Razor
Microsoft.NET.Sdk.Razor so that the Razor components would compile without any error.
Update: The dependency registration process has been streamlined from .NET MAUI RC1 release, this is described in detail in this article. The rest of the things still holds good from the GA release standpoint.
CreateMauiApp() startup method, invoke the
RegisterBlazorMauiWebView() extension method on the builder object, then add the BlazorWebView itself to the Services collection of DI container with the
builder.Services property. This will do the dependency injection to load the platform-specific view for rendering the output HTML as each platform has its own Web engine.
Before moving on to the usage, we’ll look at the Properties that are defined in the BlazorWebView for a better understanding.
- HostPage – Map the HTML page that gets loaded in the WebView to render the HTML output. Use URL convention (forward slash) to define the relative path of the file.
- RootComponents – Manages the Razor components added to this view, multiple such components can be added and each will have a specific area targeted within the HTML page, as defined by the Selector property.
It’s worth mentioning that BlazorWebView in WinForms and WPF has two more properties for managing the Services and underlying WebView.
Next is the RootComponent object, this is where to define the Razor component for a particular section of the page.
- ComponentType – Maps the Razor component to use
- Parameters – A dictionary of key-value pairs passed as parameters to help in initialization, optional
- Selector – Should be a valid CSS selector to locate an element within the HTML page (Could be based on Element Id (prefixed with the # symbol) or its Name)
Now it’s time for actual usage, define the view on any of the pages. This can be done with XAML or C#. Both code samples are provided below.
While using XAML, ensure to import the right XML namespace as the View is from a separate NuGet package, but the package reference is implicit. No need to add it to the project definition.
While using C#, use the below-mentioned namespace for View definition.
Now the View is added to the page, let us look at the details of the HTML page definition and Razor components.
Define a folder named
wwwroot and add all the static contents like HTML, CSS, and JS files (if any) to that folder so that they can be rendered without any sandbox concerns.
Here the output content will get rendered in the area where
div element with id
app is present (hence selector is #app).
Before closing the
body tag, it is necessary to include the blazor framework JS (blazor.webview.js) to initialize the whole process.
Any reference to the artifacts from the folder name starting with
_ (like _framework or _content) is from external assemblies.
Now we’ll look into the Razor components that are defined for generating the output content.
Some basics, Razor components inherit from
ComponentBase and there is a special type of Razor component called Layout component which inherits from
LayoutComponentBase. This layout component defines a property named
Body where the child content would get rendered in the place where it is defined. Useful for defining a common layout across the entire app or to implement branding. Quite like the MasterPage definition in ASP.NET, but here it can be used only once.
There are 4 different Razor components used in this sample.
- Gateway.razor – The Router component
- MainLayout.razor – The Layout component
- Index.razor – Landing component
- Counter.razor – Sample code
The Router component, quite like the name suggests, acts as a router that parses the URL of the incoming request and redirects it to the appropriate component which can serve the request. If none is available, then display the view defined in the
NotFound section. The layouts can be different for
Here the Layout component is plain and simple with just an HTML
div around the rendered output.
The routing address would be defined using the component’s
@page attribute. By convention, the initial request served would be the component with the route address
/ , and here Index is the landing component.
And components can be nested onto another, Counter itself is a component and it is defined in the Index so that it gets rendered as part of the Index component. Navigation from one component to another is also possible. Check the samples that are in the Blazor docs for further info.
Here, the Counter component just keeps incrementing the variable value by one for each button click and displays the same in another element.
Project output in Android and Windows.
Source code for this sample is now made available in this GitHub repository. Find it in the MauiBlazorApp folder under the src directory.
If you would like to see a more advanced sample with core logic abstracted as Razor Class Library, Java Script, and Essentials integration, then have a look at this sample in this GitHub repository. This solution has BlazorWebView integration with WinForms and WPF too. Then the RCL is shared with Blazor Server and WebAssembly.
Happy coding. Stay connected as we continue to learn and share the experiences from this exciting journey of being a software developer.