When building a workflow for a mobile application, it’s not uncommon to need to be able to go through a multi-step process. In .NET MAUI, I often see people use something like the CarouselView control to switch between these components. While that works, it becomes cumbersome because the carousel control is intended for use where you have an indefinite amount of similar items. If you need to manage multiple controls where each control has a unique view model, that is where the Componentizer
shines. You can think of it as an in-page way to navigate between subcomponents or workflows of your page with familiar APIs and MVVM-focused features.
This is a simple component that offers up the following features:
In-Page Navigation Easily navigate within a single page, updating content dynamically.
Multiple Navigation Methods Navigate between different components from your views or use view models with simple registration.
Easy Testing Everything is backed by interfaces, so you can unit test your little heart out.
Simplified API A clean and intuitive API for handling navigation within your app.
The Componentizer
uses a view model-first configuration that needs a view model to be registered to a view. This is done in the MauiProgram
using the RegisterComponents
extension method.
⚠️ NOTE
Componentizer
uses dependency injection to create your view models and views, so make sure that you register them using the builder’sServices
collection.
Below is an example of registering your views and view models.
public static class MauiProgram{public static MauiApp CreateMauiApp(){MauiAppBuilder builder = MauiApp.CreateBuilder();builder.UseMauiApp<App>().ConfigureFonts(fonts =>{fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");}).RegisterViews().RegisterViewModels().RegisterComponents(componentNavigation =>{componentNavigation.RegisterView<SampleAViewModel, ComponentA>();componentNavigation.RegisterView<SampleBViewModel, ComponentB>();}).Build();}private static MauiAppBuilder RegisterViews(this MauiAppBuilder builder){builder.Services.AddTransient<MainPage>();builder.Services.AddTransient<ComponentA>();builder.Services.AddTransient<ComponentB>();return builder;}private static MauiAppBuilder RegisterViewModels(this MauiAppBuilder builder){builder.Services.AddTransient<MainViewModel>();builder.Services.AddTransient<SampleAViewModel>();builder.Services.AddTransient<SampleBViewModel>();return builder;}}
The first thing to do is import the namespace for the control using the http://componentizer.4k/schemas/controls
namespace.
<ContentPagex:Class="SampleApp.MainPage"xmlns="http://schemas.microsoft.com/dotnet/2021/maui"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"xmlns:ctz="http://componentizer.4k/schemas/controls">...</ContentPage>
Once configured, you can add a MauiComponentNavigator
control to your user interface. The MauiComponentNavigator
is a container control that will host your views. The only other required property is ComponentName
, a unique identifier for this specific component. The ComponentName
property will need to be referenced from your view model layer, so it is recommended to host this with a shared reference or to keep track of the name as you will need it later on. One of the neat parts of component navigation is that you can host multiple MauiComponentNavigator
components on a single page.
<ctz:MauiComponentNavigatorx:Name="ComponentNav"ComponentName="{x:Static local:ComponentNames.MainComponent}" />
To perform the actual navigation, you will need to get an instance of the IComponentNavigation
component. This is registered with the dependency injection container as a singleton, so it can either be resolved using the service provider or via dependency injection. IComponentNavigation
has a very similar API to MAUI’s stack-based INavigation manager where you can choose to PushAsync
and ‘PopAsync’, but it requires the view model and component that you will be navigating to.
In this example, the _componentNavigation
instance will push a new instance of the SampleAViewModel
associated with the ComponentA
in the RegisterComponents
method. The view will be pushed to the container named MainComponent
. As stated earlier, it is recommended to place the names of your components in shared code so that the views and the view models can reference them without a direct dependency between them.
await _componentNavigation.PushAsync<SampleAViewModel>(ComponentNames.MainComponent);
The IComponentNavigation
allows you to pass an optional Dictionary<string, object>
query parameter. If your View
or view model
implements the IQueryAttributable
interface, the query dictionary will be passed
await this._componentNavigation.PushAsync<SampleBViewModel>(ComponentNames.MainComponent,new Dictionary<string, object>{{ "Value", DateTimeOffset.Now.ToUnixTimeMilliseconds() },});