The key feature of modules is the ability to be disabled entirely or partially. Currently, the module cannot do this, so we need to extend its capabilities.
Adding AppHost extension
Create an AppHost folder in the root directory of Asv.Avalonia.Module. Inside this folder, add three files: ModuleModuleMixin, ModuleModuleOptionsBuilder, and ModuleModuleOptions.
ModuleModuleOptions
In this file, we define the configuration options for the module. These options will later be used by the builder.
public class ModuleModuleOptions
{
public const string Section = "GeoMap"; // Section for the IServiceCollection
public required bool IsEnabled { get; set; }
}
ModuleModuleOptionsBuilder
Add one base method for the builder. We will extend this class later, but for now this method is enough.
This file extends the builder in Program.cs. We use this method to set up our module.
public static class ModuleModuleMixin
{
public static IHostApplicationBuilder UseModuleModule(this IHostApplicationBuilder builder)
{
var options = builder
.Services.AddOptions<ModuleModuleOptions>()
.Bind(builder.Configuration.GetSection(ModuleModuleOptions.Section));
var subBuilder = new ModuleModuleBuilder();
subBuilder.Build(options);
return builder;
}
}
Adding useful extension
We want to provide users with the simplest and best possible experience. Therefore, we will create a useful, but completely optional, extension method. This method will have preconfigured exports for our Module.
Go to the ModuleModule.cs and the following code:
public static class ContainerConfigurationMixin
{
public static ContainerConfiguration WithDependenciesFromModuleModule(
this ContainerConfiguration containerConfiguration
)
{
if (Design.IsDesignMode)
{
return containerConfiguration.WithAssemblies([typeof(ModuleModule).Assembly]);
}
var exceptionTypes = new List<Type>();
var options = AppHost.Instance.GetService<IOptions<ModuleModuleOptions>>().Value;
if (!options.IsEnabled)
{
// if the module is disabled, we should remove all the dependencies from the export
exceptionTypes.AddRange(typeof(ModuleModule).Assembly.GetTypes());
}
var typesToExport = typeof(ModuleModule).Assembly
.GetTypes()
.Except(exceptionTypes);
return containerConfiguration.WithParts(typesToExport);
}
}
Adding modularity to the app
Now our module is fully modular, and we need to enable it in the application.
Go to the Program.cs and add the following line to the builder:
.UseModuleModule()
Then go to the App.axaml.cs and change
.WithAssemblies([typeof(ModuleModule).Assembly])
to
.WithDependenciesFromModuleModule()
Run the application. You should see the Cats page.
Now remove UseModuleModule from the builder, .WithDependenciesFromModuleModule() from App.axaml.cs, or both. Run the application again. The page should no longer be visible.
If you did everything right, now your module meets our standards for modules. Next we will show you how to extend your module and add more settings to it so that you will be able to split your module into smaller parts.
Extending the module
We have already implemented the basic module structure with some simple features. In this section we are going to extend our module with new features. We will also add new settings to the builder. This section is useful if you want to add to your module optional features.
Adding a new page
We want to provide additional customization options for the user. Let's add the ability to open a page with dogs.
Download a picture with dogs from the internet.
Add a new picture to the assets folder.
Create a new View and ViewModel for the dogs page. We will skip the details of this step as it is similar to the Cats page implementation.
We need to change the builder extension to use our new options. Here we will show you the advanced version of the builder extension that can take default options from the app.settings.json.
public static IHostApplicationBuilder UseModuleModule(
this IHostApplicationBuilder builder,
Action<ModuleModuleOptionsBuilder>? configure = null
)
{
var options = builder
.Services.AddOptions<ModuleModuleOptions>()
.Bind(builder.Configuration.GetSection(ModuleModuleOptions.Section));
var defaultOptions = builder
.Configuration.GetSection(ModuleModuleOptions.Section)
.Get<ModuleModuleOptions>();
var optionsBuilder = defaultOptions is null
? new ModuleModuleOptionsBuilder()
: new ModuleModuleOptionsBuilder(defaultOptions);
if (configure is null)
{
return builder;
}
configure.Invoke(optionsBuilder);
optionsBuilder.Build(options);
return builder;
}
Extending useful extension
Go to the ModuleModule.cs and add the following code:
...
// New code
else if (!options.IsDogsPageEnabled)
{
// if we disable the dogs page, we should remove all dependencies for this page
exceptionTypes.AddRange([
typeof(DogsPageView),
typeof(DogsPageViewModel),
typeof(HomePageDogsPageExtension),
typeof(OpenDogsPageCommand)
]);
}
else if (!options.IsCatsPageEnabled)
{
// if we disable the cats page, we should remove all dependencies for this page
exceptionTypes.AddRange([
typeof(CatsPageView),
typeof(CatsPageViewModel),
typeof(HomePageCatsPageExtension),
typeof(OpenCatsPageCommand)
]);
}
//
var typesToExport = typeof(ModuleModule).Assembly
.GetTypes()
.Except(exceptionTypes);
...
Now you can run the application and see that all pages are disabled. To enable them, modify the UseModuleModule call in Program.cs as follows: