Categories
.NET MAUI Blazor Deep Dive Desktop Mobile Web Xamarin

.NET MAUI – Blazor – Interop

Logic abstracted as Razor Class Library, Shared AppState and Component Navigation

This is Part 2 in the multi-part series of .NET MAUI – Blazor articles.

In Part 1, we’ve seen an introduction on how to make use of BlazorWebView in the .NET MAUI app.

Since it is implemented as a View, it’s possible to share data between .NET MAUI and Blazor and that’s the takeaway of this article. Along with that, the core logic is now abstracted as Razor Class Library (RCL), Dependency Injection, and Component routing for Navigation.

RCL is a library project targeting the Microsoft.NET.Sdk.Razor SDK type. Allows sharing Razor components with other projects to maximize code reuse. Not only Razor components, but it’s also possible to share static assets (like CSS, JS) from these libraries.

Razor Class Library - Components Reuse
Razor Class Library – Components Reuse
Components abstracted as Razor Class Library
Components abstracted as Razor Class Library

In the sample described in the previous article, all the components were in the same project as the UI, but now apart from Gateway.razor, all the other components have been abstracted as RCL. It’s possible to have this Gateway.razor in the RCL itself, but to illustrate how to refer components from external assemblies, have left it in the MAUI project itself. And to add to this, have created a new CSS in the RCL, that would be referred to in the index.html file, the host page in BlazorWebView.

Loading Razor Components from External Assemblies
Loading Razor Components from External Assemblies

The AdditionalAssemblies attribute on the Router component holds the list of all other assemblies to scan for the components that are to be loaded during runtime. This can be multiple.

The Router component maintains a list of routes that are defined in all the components and redirects the incoming request to the right component for servicing it or if none of the routes match displays the view defined in the NotFound section.

Now the sample is updated with a different layout for Found and NotFound scenario. Check out the sample in the GitHub repo for details.

As mentioned earlier, the resources from the folder name starting with _ are from external assemblies. Now, the CSS from this RCL gets referenced in index.html as depicted in the below image. _content for user-defined resources and _framework for base libraries.

Ensure this NuGet package is referenced in the .NET MAUI project to serve the static assets from the referenced assemblies: Microsoft.Extensions.FileProviders.Embedded

The default convention is _content/<assemblyName>/<resourcePath>. The resources need to be made available in the wwwroot folder in the RCL for this to work. There is yet another approach for serving static resources with Razor components known as Scoped CSS/JS. More on that in another article.

Static Resource from Referenced Assemblies
Static Resource from Referenced Assemblies

Component routing can be handled with NavigationManager or HTML anchor element (<a href="" />). The difference is in the outcome of the requested route not matching with any of the available route addresses. While using NavigationManager, it shows the NotFound view whereas when using the anchor element, it shows the platform-specific Invalid address page (404 – Not Found). The reason is, if none of the routes match, the Router passes on the anchor’s request to the platform handler considering it as an external resource (broader scope).

NavigationManager can be injected into the component using @inject directive, capture the injected instance into a variable, and with that call the NavigateTo method with the route address to navigate to another component.

Component Routing - Dependency Injection - NavigationManager
Component Routing – Dependency Injection – NavigationManager
Component Routing - Address with No match - NavigationManager
Component Routing – Address with No match – NavigationManager
Component Routing - Address with No match - HTML Anchor element
Component Routing – Address with No match – HTML Anchor element

Note: The component directives can also be used as attributes while using code-behind. Define a partial class in the name of the component, define public properties, and decorate them with appropriate attributes. For example, @page will become [Route] (to be applied to the component class itself) and @inject will become [Inject]. @using and @implements are exceptions.

using Microsoft.AspNetCore.Components;

namespace RazorLib
{
    [Route("/")]
    public partial class Index
    {
        [Inject]
        public NavigationManager? Navigator { get; set; }
    }
}

Route addresses won’t be powerful without parameters in them. Yes, they do support passing parameters (both URL as well as Query type parameters), can be marked as optional if necessary and components can have more than one route defined on them. Query type parameters can only be used during routing and not while nesting in other components.

The routing address of the Counter component has been updated to accept a parameter named StartWith (case-insensitive) with an added constraint that the incoming value should be of type integer.

Component Routing - Route Address - Parameters
Component Routing – Route Address – Parameters

For example, a route with an address /counter/7 would match whereas /counter/foo won’t match as the parameter is not of type integer in the latter.

In the sample, the Counter component is used in two ways: Nested, in the Index itself, and Standalone with routing. From Index, on Navigate with Random value button click, a random integer is passed as a parameter to set as an initial value for the counter.

Now on to the most exciting part of this article, sharing the state between .NET MAUI and Blazor. Yes, both native MAUI controls and Blazor web components gonna work in tandem on the same counter value. Actioning on either of the controls will increment the counter and refresh the value in the UI. For this to work, we need a shared AppState to hold the counter value and we use a DI container to hold the same.

.NET MAUI - Blazor - Shared AppState
.NET MAUI – Blazor – Shared AppState

The shared AppState has a public property to read the current counter value and public methods to initialize and increment the counter. Whenever the value of the counter changes, state change notification is raised so that the interested components can subscribe to the notification and respond to that.

Shared AppState in App Startup
Shared AppState in App Startup

Now in the App Startup, this AppState is registered in the DI container as Singleton so that the same instance is returned to all the parties requesting for it. And the other registrations are for the .NET MAUI startup page (defined in XAML and C# respectively) and this AppState is injected in the constructor of those pages so that it is resolved automatically as their dependency.

public partial class App : Application
{
	public App()
	{
		InitializeComponent();

		// C# definition
		MainPage = AppService.GetService<BlazorPage>();

		// XAML definition
		//MainPage = AppService.GetService<WebPage>();
	}
}

User action on the MAUI button or Blazor button would increment the counter value and UI is updated to reflect the state change.

On navigation, a random value would be passed as input and the counter would start from that.

Finally, upon clicking the Back to Home link, the counter value would reset to zero and start again.

Shared AppState - Blazor
Shared AppState – Blazor
Shared AppState - .NET MAUI
Shared AppState – .NET MAUI
.NET MAUI - Blazor - Interop - Output
.NET MAUI – Blazor – Interop – Output
.NET MAUI - Blazor - Interop - Output - Parameter Routing
.NET MAUI – Blazor – Interop – Output – Parameter Routing

Happy coding. Stay connected as we continue to learn and share the experiences from this exciting journey of being a software developer.

4 replies on “.NET MAUI – Blazor – Interop”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s