Shell Routing through attributes on page (using source generators) #1004
Replies: 6 comments 9 replies
-
I can see value in that... But looking the spec, our services methods do that and a little bit more... I mean, a lot of devs love to use DI can we integrate our DI code with this SG? |
Beta Was this translation helpful? Give feedback.
-
Currently designed as a partial method and its definition is source generated in the partial class. The user has to plug in the invocation call in the .NET MAUI startup pipeline within the
And for registering the Routing details of Shell enabled Apps, a similar approach, a partial method in the source generated partial class for the Shell type. The user has to plug in the invocation call in the constructor of the Shell page.
Feedback is most welcome if you people have any ideas on to improve this. |
Beta Was this translation helpful? Give feedback.
-
Have seen few projects having different Shell pages for mobile and desktop. Moreover, this approach will work for class library as well.
Regards,
Vijay Anand E G
…________________________________
From: Pedro Jesus ***@***.***>
Sent: Friday, February 24, 2023 7:52:15 AM
To: CommunityToolkit/Maui ***@***.***>
Cc: Vijay Anand E G ***@***.***>; Comment ***@***.***>
Subject: Re: [CommunityToolkit/Maui] Shell Routing through attributes on page (using source generators) (Discussion #1004)
Why not does everything in one place?
—
Reply to this email directly, view it on GitHub<#1004 (reply in thread)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/ATRGWDBGRHNEZFHZJ345NF3WZALNPANCNFSM6AAAAAAU6ROWQE>.
You are receiving this because you commented.Message ID: ***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
Description of the Proposed solutionAttribute-based shell routing and service registration in the DI container. Automated with (incremental) 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 attributeThis 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 public class RouteAttribute : Attribute
{
public RouteAttribute(string route, ServiceLifetime lifetime = ServiceLifetime.Singleton)
=> (Route, Lifetime) = (route, lifetime);
} The
[Route("settings")]
public class SettingsPage : ContentPage
{
} [Route("search", ServiceLifetime.Transient, ImplicitViewModel = true)]
public class SearchPage : ContentPage
{
} MauiService attributeThis 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 [MauiService(ServiceLifetime.Transient)]
public class AppThemePopup : Popup
{
} The
[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 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 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>();
}
} |
Beta Was this translation helpful? Give feedback.
-
Should this discussion remain open is it being progressed? |
Beta Was this translation helpful? Give feedback.
-
The spirit of the original issue is whether you can have an source generator attribute just on a page. However, the challenge is you must have lines in your MauiProgram.cs and AppShell.cs. The MauiProgram.cs is needed to register your pages/services before they're instantiated, so, adding code only in the target page is not sufficient. Also, we must have additions to AppShell to register your route. To minimize the effort, one could write a public static class RouteHelper
{
public static Dictionary<string, Type> Routes = new Dictionary<string, Type>();
public static void AddTransientRoute(this IServiceCollection services, string route, Type type) => services.AddTransient(Routes[route] = type);
public static void AddTransientRoute<T>(this IServiceCollection services, string route) => AddTransientRoute(services, route, typeof(T));
public static void AddRoutes() => Routes.ToList().ForEach(item => Routing.RegisterRoute(item.Key, item.Value));
} In your MauiProgram.cs you can register your transients and routes at the same time, e.g. builder.Services.AddTransientRoute<SecondPage>(nameof(SecondPage));
builder.Services.AddTransientRoute<ThirdPage>(nameof(ThirdPage)); But you will still call public AppShell()
{
InitializeComponent();
RouteHelper.AddRoutes();
} |
Beta Was this translation helpful? Give feedback.
-
Description
Today, we must register each page in the AppShell on startup such as:
However, it would be nicer to align with how ASP.NET Core routing works and make it an attribute on the page. Since this would require assembly scanning today, this should be implemented as source generators for optimal performance.
(Public) API Changes
This would generate code necessary to register the
DetailsPage
with the route ofDetailsPage
.Usage Scenarios
See above :)
Backward Compatibility
Would be an added option and an upgrade from the existing mechanism.
Difficulty
Medium
Beta Was this translation helpful? Give feedback.
All reactions