maui
maui copied to clipboard
Shell routing and Service registration automation
Description of Change
Attribute-based shell routing and service registration in the DI container. Automated with source generators.
Below are the two attributes that are defined to generate the source that automates the tasks while defining a shell route or to add a DI service into the .NET MAUI startup pipeline.
- Route
- MauiService
Route attribute
This can be defined on any Page inherited type so that it can be added to the Routes collection. In addition, the type will also be added to the DI service in the startup.
[Route]
public class SettingsPage : ContentPage
{
}
By default, the route name generated would be the name of the type itself. But can be modified with its properties.
Defines a constructor to help in the generalized case with two parameters for route and lifetime.
public class RouteAttribute : Attribute
{
public RouteAttribute(string route, ServiceLifetime lifetime = ServiceLifetime.Singleton)
=> (Route, Lifetime) = (route, lifetime);
}
The Route attribute defines the following properties:
| Property | Type | Remarks |
|---|---|---|
| Route | string | Defines a single route definition for the page. |
| Routes | string[] | Defines multiple route definitions for the same page and takes precedence over the Route property. |
| Lifetime | ServiceLifetime | Defines the lifetime of the page in the DI container. Defaults to Singleton if not defined. |
| ImplicitViewModel | bool | Usually, the ViewModel and the View (page here) are associated by names. In such cases, the name of the ViewModel is derived from the View, when this property is set to true. |
| ViewModelType | Type | Explicit definition of the ViewModel type and takes precedence over the ImplicitViewModel property. |
[Route("settings")]
public class SettingsPage : ContentPage
{
}
[Route("search", ServiceLifetime.Transient, ImplicitViewModel = true)]
public class SearchPage : ContentPage
{
}
MauiService attribute
This can be defined on any type so that it can be added to the Services collection. of the MauiAppBuilder instance as a DI service in the .NET MAUI startup.
[MauiService]
public class DialogService
{
}
Defines a constructor to help in the generalized case that takes lifetime as a parameter.
[MauiService(ServiceLifetime.Transient)]
public class AppThemePopup : Popup
{
}
The MauiService attribute defines the following properties.
| Property | Type | Remarks |
|---|---|---|
| Lifetime | ServiceLifetime | Defines the lifetime of the page in the DI container. Defaults to Singleton if not defined. |
| UseTryAdd | bool | Makes use of the TryAdd method construct while adding the type in the Services collection when this property is set to true. |
| RegisterFor | Type | Register the type for the type defined in this property, helpful for the type to be registered against an interface. |
[MauiService(RegisterFor = typeof(INavigationService))]
public class NavigationService : INavigationService
{
}
[MauiService(UseTryAdd = true)]
public class MediaService
{
}
Output:
For each of the Shell pages defined, a partial method of the name RegisterRoutes with all those routes added as its method definition will get auto-generated. This method needs to be invoked in the constructor of the shell definition.
Assuming MauiApp1 as the project root namespace for the sample code.
User definition:
namespace MauiApp1;
public partial class AppShell : Shell
{
public AppShell()
{
// Only relevant code is shown here. If defined as XAML, InitializeComponent(); would be here.
RegisterRoutes();
}
}
Source generated:
namespace MauiApp1;
partial class AppShell : Shell
{
static partial void RegisterRoutes();
static partial void RegisterRoutes()
{
Routing.RegisterRoute(nameof(global::MauiApp1.Views.SettingsPage), typeof(global::MauiApp1.Views.SettingsPage));
Routing.RegisterRoute("search", typeof(global::MauiApp1.Views.SearchPage));
}
}
For the services, a partial method of the name ConfigureDependencies with all those types in the Services collection as its method definition will get auto-generated. This method needs to be invoked within the CreateMauiApp() method.
User definition:
namespace MauiApp1;
public static partial class MauiProgram
{
public static MauiApp CreateMauiApp()
{
// Only relevant code is shown here.
var builder = MauiApp.CreateBuilder();
// All other definitions
builder.ConfigureDependencies();
return builder;
}
}
Source generated:
namespace MauiApp1;
static partial class MauiProgram
{
static partial void ConfigureDependencies(this global::Microsoft.Maui.Hosting.MauiAppBuilder builder);
static partial void ConfigureDependencies(this global::Microsoft.Maui.Hosting.MauiAppBuilder builder)
{
builder.Services.AddSingleton<global::MauiApp1.Services.DialogService>();
builder.Services.AddSingleton<global::MauiApp1.Services.INavigationService, global::MauiApp1.Services.NavigationService>();
builder.Services.TryAddSingleton<MediaService>();
builder.Services.AddTransient<global::MauiApp1.Views.AppThemePopup>();
}
}
Issues Fixed
Fixes #5312
Hey there @egvijayanand! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.
Thank you for your pull request. We are auto-formating your source code to follow our code guidelines.
@dotnet-policy-service agree
@egvijayanand FYI, @PureWeen opened a discussion around this feature on MCT repo
- https://github.com/CommunityToolkit/Maui/discussions/1004
Not sure if this will be approved here or there, but in any case, you should use IIncrementalSourceGenerator instead, for better performance.
Not sure if this will be approved here or there, but in any case, you should use
IIncrementalSourceGeneratorinstead, for better performance.
Even I've no clarity on whether this would be part of .NET MAUI / Toolkit.
Still, reviews are being done. Will check on the Incremental part of SG.
Not my area of expertise so we should have another review (I'll ping the right person). Love the code changes, code is much nicer to read THANKS a lot for the hard work.
Definitely, I should thank you for all your patience in suggesting the changes in making the code more readable and maintainable.
And as @pictos mentioned, am looking into the possibility of making it as an incremental SG. Requires some effort, so can another review be done post that?
Not my area of expertise so we should have another review (I'll ping the right person). Love the code changes, code is much nicer to read THANKS a lot for the hard work.
Definitely, I should thank you for all your patience in suggesting the changes in making the code more readable and maintainable.
And as @pictos mentioned, am looking into the possibility of making it as an incremental SG. Requires some effort, so can another review be done post that?
I am happy to do as many reviews as needed. Is effort well spent.
Can we add more information on the PR description about how it works, and example of generated code for example?
Thanks
Sure, will include those details.
Sure, will include those details.
@egvijayanand I'm going to close this for now in favor of the discussion over on MCT. https://github.com/CommunityToolkit/Maui/discussions/1004. Once we get through the discussion process over there then let's take next steps. I'm hoping we can get this merged into MCT so users can take advantage right away.