Add automatic route registration to your .NET MAUI app
The next step in simplified route management for .NET
Introduction
In December of 2023, I introduced the epj.RouteGenerator package which provides a facility to have route identifiers for .NET-based frontend applications auto-generated for you, taking away a few pain points with string-based route navigation and maintenance hassles.
This is a follow up post. You can find the original blog post here. If you're unfamiliar with Route Generator, please read that first. Gerald Versluis also made a great YouTube video showing the Route Generator in action.
Since then, one thing that I have been frequently asked was whether it would be possible to add automatic registration of routes to the route generator, as to further simplify and reduce the amount of manually written code in .NET apps.
Now, my intention is to keep the route generator independent from specific UI frameworks, so that it can be used with any kind of .NET-based application that uses routes for navigation. Therefore, in order to provide full support of automatic route registration, say in .NET MAUI, it would be required to do two things:
Gather the type information, e.g. the fully qualified type name
Add a MAUI-specific layer with a registration method
For now, I decided to focus on the first step only, because it will already make things a lot easier while remaining framework-independent. The second step is a little bit more tricky as the generator shouldn't have MAUI-specific knowledge. To overcome this limitation, creating another library that builds on top of epj.RouteGenerator, for example something like epj.RouteGenerator.Maui, would be required. Writing such library that exposes all kinds of useful utility methods specifically target at .NET MAUI will require some time and design considerations.
Here are the good news though: The first step is already done with the latest preview version of the Route Generator (1.0.1-alpha2 at the time of writing), which is already available for download on nuget.org.
Let's see what that means and how to use it ๐คฉ.
Route/Type Map
On top of the read-only AllRoutes
collection, the generated Routes
class now also exposes a read-only dictionary called RouteTypeMap
which maps the routes to their respective Type
:
// <auto-generated/>
using System.Collections.ObjectModel;
namespace RouteGeneratorSample
{
public static class Routes
{
public const string MainPage = "MainPage";
public const string VolvoPage = "VolvoPage";
public const string AudiPage = "AudiPage";
private static List<string> allRoutes = new()
{
MainPage,
VolvoPage,
AudiPage,
};
public static ReadOnlyCollection<string> AllRoutes => allRoutes.AsReadOnly();
// NEW:
private static Dictionary<string, Type> routeTypeMap = new()
{
{ MainPage, typeof(RouteGeneratorSample.MainPage) },
{ VolvoPage, typeof(RouteGeneratorSample.Cars.VolvoPage) },
{ AudiPage, typeof(RouteGeneratorSample.Cars.AudiPage) },
};
public static ReadOnlyDictionary<string, Type> RouteTypeMap => routeTypeMap.AsReadOnly();
}
}
Now, this type information can be used to register routes, for example in .NET MAUI, without the source generator requiring specific knowledge about the UI framework.
Route Registration in .NET MAUI
Usually, we would need to register each route manually by typing out each registration call for every route and type. This is typically done in AppShell.xaml.cs similar to this:
using System.Diagnostics;
namespace RouteGeneratorSample;
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(MainPage), typeof(MainPage));
Routing.RegisterRoute(nameof(VolvoPage), typeof(VolvoPage));
Routing.RegisterRoute(nameof(AudiPage), typeof(AudiPage));
}
}
With the new Routes.RouteTypeMap
dictionary, we can now register routes (semi-) automatically - all you need to do is iterate over the map like this:
foreach (var route in Routes.RouteTypeMap)
{
Routing.RegisterRoute(route.Key, route.Value);
}
๐ช That's it! No more manual typing of each route name and type for registration.
Extra Routes
What about extra routes? For example, if a route name doesn't match a type name, then what? Route Generator has got you covered, as well! The [ExtraRoute]
attribute now features a second argument which can be used to specify the type that should be used for the route using typeof(ClassName)
:
namespace RouteGeneratorSample;
// pass the type for the route to the ExtraRoute attribute,
// this will make sure to include the type in the Routes.RouteTypeMap;
// the key will be "YetAnotherRoute" and the value the type of MainPage
[AutoRoutes("Page")]
[ExtraRoute("YetAnotherRoute", typeof(MainPage))]
public static class MauiProgram
{
// ...
}
Like this, the Routes.YetAnotherRoute
identifier is used to associate a route to the MainPage
in the example above. The resulting auto-generated Routes.g.cs class will then look as follows:
// <auto-generated/>
using System.Collections.ObjectModel;
namespace RouteGeneratorSample
{
public static class Routes
{
public const string MainPage = "MainPage";
public const string VolvoPage = "VolvoPage";
public const string AudiPage = "AudiPage";
public const string YetAnotherRoute = "YetAnotherRoute";
private static List<string> allRoutes = new()
{
MainPage,
VolvoPage,
AudiPage,
YetAnotherRoute
};
public static ReadOnlyCollection<string> AllRoutes => allRoutes.AsReadOnly();
private static Dictionary<string, Type> routeTypeMap = new()
{
{ MainPage, typeof(RouteGeneratorSample.MainPage) },
{ VolvoPage, typeof(RouteGeneratorSample.Cars.VolvoPage) },
{ AudiPage, typeof(RouteGeneratorSample.Cars.AudiPage) },
{ YetAnotherRoute, typeof(RouteGeneratorSample.MainPage) },
};
public static ReadOnlyDictionary<string, Type> RouteTypeMap => routeTypeMap.AsReadOnly();
}
}
Now, the Routes.YetAnotherRoute
identifier can be used to navigate to the MainPage
using await Shell.Current.GoToAsync(Routes.YetAnotherRoute)
or similar.
Please note that if you specify an extra route that doesn't match with an existing type name without explicitly specifying the target type, that extra route will not be included in the
Routes.RouteTypeMap
, because the type would be undefined. The Route Generator will emit a warning in this case.
Conclusions and next steps
In this follow-up post I have demonstrated some of the further development possibilities for the Route Generator for .NET. Being able to register route names and types automatically can further reduce manually typed code and offering a way to facilitate this seemed like a wonderful idea, so that's what I did.
If you have feedback or suggestions for improvements or additional features, please don't hesitate to get in touch or post an issue in the GitHub repository.
If you enjoyed this blog post, then follow me on LinkedIn and subscribe to this blog so you don't miss out on any future posts. If this is useful to you, maybe also consider starring the GitHub repository of the Route Generator. Thank you very much ๐.